On real hardware, panic and machine reboot may not flush hardware cache to memory. This means the persistent ring buffer, which relies on a coherent state of memory, may not have its events written to the buffer and they may be lost. Moreover, there may be inconsistency with the counters which are used for validation of the integrity of the persistent ring buffer which may cause all data to be discarded. To avoid this issue, stop recording of the ring buffer on panic and flush the cache of the ring buffer's memory.
Showing the changes to kernel (1 of 23 files). Show all
Unified · Split
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <linux/ring_buffer_types.h> |
| 8 | 8 | #include <linux/sched/isolation.h> |
| 9 | 9 | #include <linux/trace_recursion.h> |
| 10 | +#include <linux/panic_notifier.h> | |
| 10 | 11 | #include <linux/trace_events.h> |
| 11 | 12 | #include <linux/ring_buffer.h> |
| 12 | 13 | #include <linux/trace_clock.h> |
| @@ -31,6 +32,7 @@ · 18 unmodified lines | ||
| 31 | 32 | #include <linux/oom.h> |
| 32 | 33 | #include <linux/mm.h> |
| 33 | 34 | |
| 35 | +#include <asm/ring_buffer.h> | |
| 34 | 36 | #include <asm/local64.h> |
| 35 | 37 | #include <asm/local.h> |
| 36 | 38 | #include <asm/setup.h> |
| @@ -559,6 +561,7 @@ struct trace_buffer { · 522 unmodified lines | ||
| 559 | 561 | |
| 560 | 562 | unsigned long range_addr_start; |
| 561 | 563 | unsigned long range_addr_end; |
| 564 | + struct notifier_block flush_nb; | |
| 562 | 565 | |
| 563 | 566 | struct ring_buffer_meta *meta; |
| 564 | 567 | |
| @@ -2521,6 +2524,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) · 1956 unmodified lines | ||
| 2521 | 2524 | kfree(cpu_buffer); |
| 2522 | 2525 | } |
| 2523 | 2526 | |
| 2527 | +/* Stop recording on a persistent buffer and flush cache if needed. */ | |
| 2528 | +static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data) | |
| 2529 | +{ | |
| 2530 | + struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb); | |
| 2531 | + | |
| 2532 | + ring_buffer_record_off(buffer); | |
| 2533 | + arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end); | |
| 2534 | + return NOTIFY_DONE; | |
| 2535 | +} | |
| 2536 | + | |
| 2524 | 2537 | static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, |
| 2525 | 2538 | int order, unsigned long start, |
| 2526 | 2539 | unsigned long end, |
| @@ -2651,6 +2664,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, · 124 unmodified lines | ||
| 2651 | 2664 | |
| 2652 | 2665 | mutex_init(&buffer->mutex); |
| 2653 | 2666 | |
| 2667 | + /* Persistent ring buffer needs to flush cache before reboot. */ | |
| 2668 | + if (start && end) { | |
| 2669 | + buffer->flush_nb.notifier_call = rb_flush_buffer_cb; | |
| 2670 | + atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb); | |
| 2671 | + } | |
| 2672 | + | |
| 2654 | 2673 | return_ptr(buffer); |
| 2655 | 2674 | |
| 2656 | 2675 | fail_free_buffers: |
| @@ -2749,6 +2768,9 @@ ring_buffer_free(struct trace_buffer *buffer) · 92 unmodified lines | ||
| 2749 | 2768 | { |
| 2750 | 2769 | int cpu; |
| 2751 | 2770 | |
| 2771 | + if (buffer->range_addr_start && buffer->range_addr_end) | |
| 2772 | + atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb); | |
| 2773 | + | |
| 2752 | 2774 | cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node); |
| 2753 | 2775 | |
| 2754 | 2776 | irq_work_sync(&buffer->irq_work.work); |