* [PATCH v9 1/4] ring-buffer: Fix to update per-subbuf entries of persistent ring buffer
2026-03-11 1:32 [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu (Google)
@ 2026-03-11 1:32 ` Masami Hiramatsu (Google)
2026-03-11 1:32 ` [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic Masami Hiramatsu (Google)
` (3 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Masami Hiramatsu (Google) @ 2026-03-11 1:32 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Since the validation loop in rb_meta_validate_events() updates
the same cpu_buffer->head_page->entries, the other subbuf entries
are not updated.
Fix to use head_page to update the entries field, since it is the
cursor in this loop.
Fixes: 5f3b6e839f3c ("ring-buffer: Validate boot range memory events")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
kernel/trace/ring_buffer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index f16f053ef77d..353a5aa1b612 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2053,7 +2053,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
entries += ret;
entry_bytes += local_read(&head_page->page->commit);
- local_set(&cpu_buffer->head_page->entries, ret);
+ local_set(&head_page->entries, ret);
if (head_page == cpu_buffer->commit_page)
break;
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-11 1:32 [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu (Google)
2026-03-11 1:32 ` [PATCH v9 1/4] ring-buffer: Fix to update per-subbuf entries of persistent ring buffer Masami Hiramatsu (Google)
@ 2026-03-11 1:32 ` Masami Hiramatsu (Google)
2026-03-18 14:19 ` Masami Hiramatsu
2026-03-11 1:32 ` [PATCH v9 3/4] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer Masami Hiramatsu (Google)
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Masami Hiramatsu (Google) @ 2026-03-11 1:32 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
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.
Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v9:
- Fix typo of & to &&.
- Fix typo of "Generic"
Changes in v6:
- Introduce asm/ring_buffer.h for arch_ring_buffer_flush_range().
- Use flush_cache_vmap() instead of flush_cache_all().
Changes in v5:
- Use ring_buffer_record_off() instead of ring_buffer_record_disable().
- Use flush_cache_all() to ensure flush all cache.
Changes in v3:
- update patch description.
---
arch/alpha/include/asm/Kbuild | 1 +
arch/arc/include/asm/Kbuild | 1 +
arch/arm/include/asm/Kbuild | 1 +
arch/arm64/include/asm/ring_buffer.h | 10 ++++++++++
arch/csky/include/asm/Kbuild | 1 +
arch/hexagon/include/asm/Kbuild | 1 +
arch/loongarch/include/asm/Kbuild | 1 +
arch/m68k/include/asm/Kbuild | 1 +
arch/microblaze/include/asm/Kbuild | 1 +
arch/mips/include/asm/Kbuild | 1 +
arch/nios2/include/asm/Kbuild | 1 +
arch/openrisc/include/asm/Kbuild | 1 +
arch/parisc/include/asm/Kbuild | 1 +
arch/powerpc/include/asm/Kbuild | 1 +
arch/riscv/include/asm/Kbuild | 1 +
arch/s390/include/asm/Kbuild | 1 +
arch/sh/include/asm/Kbuild | 1 +
arch/sparc/include/asm/Kbuild | 1 +
arch/um/include/asm/Kbuild | 1 +
arch/x86/include/asm/Kbuild | 1 +
arch/xtensa/include/asm/Kbuild | 1 +
include/asm-generic/ring_buffer.h | 13 +++++++++++++
kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++
23 files changed, 65 insertions(+)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
index 483965c5a4de..b154b4e3dfa8 100644
--- a/arch/alpha/include/asm/Kbuild
+++ b/arch/alpha/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += agp.h
generic-y += asm-offsets.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 4c69522e0328..483caacc6988 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -5,5 +5,6 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 03657ff8fbe3..decad5f2c826 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -3,6 +3,7 @@ generic-y += early_ioremap.h
generic-y += extable.h
generic-y += flat.h
generic-y += parport.h
+generic-y += ring_buffer.h
generated-y += mach-types.h
generated-y += unistd-nr.h
diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h
new file mode 100644
index 000000000000..62316c406888
--- /dev/null
+++ b/arch/arm64/include/asm/ring_buffer.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_ARM64_RING_BUFFER_H
+#define _ASM_ARM64_RING_BUFFER_H
+
+#include <asm/cacheflush.h>
+
+/* Flush D-cache on persistent ring buffer */
+#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end)
+
+#endif /* _ASM_ARM64_RING_BUFFER_H */
diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild
index 3a5c7f6e5aac..7dca0c6cdc84 100644
--- a/arch/csky/include/asm/Kbuild
+++ b/arch/csky/include/asm/Kbuild
@@ -9,6 +9,7 @@ generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
generic-y += text-patching.h
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index 1efa1e993d4b..0f887d4238ed 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += extable.h
generic-y += iomap.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
index 9034b583a88a..7e92957baf6a 100644
--- a/arch/loongarch/include/asm/Kbuild
+++ b/arch/loongarch/include/asm/Kbuild
@@ -10,5 +10,6 @@ generic-y += qrwlock.h
generic-y += user.h
generic-y += ioctl.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
generic-y += statfs.h
generic-y += text-patching.h
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index b282e0dd8dc1..62543bf305ff 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -3,5 +3,6 @@ generated-y += syscall_table.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += text-patching.h
diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
index 7178f990e8b3..0030309b47ad 100644
--- a/arch/microblaze/include/asm/Kbuild
+++ b/arch/microblaze/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += syscalls.h
generic-y += tlb.h
generic-y += user.h
diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
index 684569b2ecd6..9771c3d85074 100644
--- a/arch/mips/include/asm/Kbuild
+++ b/arch/mips/include/asm/Kbuild
@@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
index 28004301c236..0a2530964413 100644
--- a/arch/nios2/include/asm/Kbuild
+++ b/arch/nios2/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += cmpxchg.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
index cef49d60d74c..8aa34621702d 100644
--- a/arch/openrisc/include/asm/Kbuild
+++ b/arch/openrisc/include/asm/Kbuild
@@ -8,4 +8,5 @@ generic-y += spinlock_types.h
generic-y += spinlock.h
generic-y += qrwlock_types.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
index 4fb596d94c89..d48d158f7241 100644
--- a/arch/parisc/include/asm/Kbuild
+++ b/arch/parisc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild
index 2e23533b67e3..805b5aeebb6f 100644
--- a/arch/powerpc/include/asm/Kbuild
+++ b/arch/powerpc/include/asm/Kbuild
@@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h
generic-y += agp.h
generic-y += mcs_spinlock.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += early_ioremap.h
diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
index bd5fc9403295..7721b63642f4 100644
--- a/arch/riscv/include/asm/Kbuild
+++ b/arch/riscv/include/asm/Kbuild
@@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h
generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild
index 80bad7de7a04..0c1fc47c3ba0 100644
--- a/arch/s390/include/asm/Kbuild
+++ b/arch/s390/include/asm/Kbuild
@@ -7,3 +7,4 @@ generated-y += unistd_nr.h
generic-y += asm-offsets.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
index 4d3f10ed8275..f0403d3ee8ab 100644
--- a/arch/sh/include/asm/Kbuild
+++ b/arch/sh/include/asm/Kbuild
@@ -3,4 +3,5 @@ generated-y += syscall_table.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 17ee8a273aa6..49c6bb326b75 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 1b9b82bbe322..2a1629ba8140 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -17,6 +17,7 @@ generic-y += module.lds.h
generic-y += parport.h
generic-y += percpu.h
generic-y += preempt.h
+generic-y += ring_buffer.h
generic-y += runtime-const.h
generic-y += softirq_stack.h
generic-y += switch_to.h
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 4566000e15c4..078fd2c0d69d 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -14,3 +14,4 @@ generic-y += early_ioremap.h
generic-y += fprobe.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
index 13fe45dea296..e57af619263a 100644
--- a/arch/xtensa/include/asm/Kbuild
+++ b/arch/xtensa/include/asm/Kbuild
@@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h
new file mode 100644
index 000000000000..930d96571f23
--- /dev/null
+++ b/include/asm-generic/ring_buffer.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Generic arch dependent ring_buffer macros.
+ */
+#ifndef __ASM_GENERIC_RING_BUFFER_H__
+#define __ASM_GENERIC_RING_BUFFER_H__
+
+#include <linux/cacheflush.h>
+
+/* Flush cache on ring buffer range if needed */
+#define arch_ring_buffer_flush_range(start, end) flush_cache_vmap(start, end)
+
+#endif /* __ASM_GENERIC_RING_BUFFER_H__ */
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 353a5aa1b612..9f4ee9e3803d 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -6,6 +6,7 @@
*/
#include <linux/sched/isolation.h>
#include <linux/trace_recursion.h>
+#include <linux/panic_notifier.h>
#include <linux/trace_events.h>
#include <linux/ring_buffer.h>
#include <linux/trace_clock.h>
@@ -30,6 +31,7 @@
#include <linux/oom.h>
#include <linux/mm.h>
+#include <asm/ring_buffer.h>
#include <asm/local64.h>
#include <asm/local.h>
#include <asm/setup.h>
@@ -589,6 +591,7 @@ struct trace_buffer {
unsigned long range_addr_start;
unsigned long range_addr_end;
+ struct notifier_block flush_nb;
struct ring_buffer_meta *meta;
@@ -2471,6 +2474,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+/* Stop recording on a persistent buffer and flush cache if needed. */
+static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
+
+ ring_buffer_record_off(buffer);
+ arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
+ return NOTIFY_DONE;
+}
+
static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
int order, unsigned long start,
unsigned long end,
@@ -2590,6 +2603,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
mutex_init(&buffer->mutex);
+ /* Persistent ring buffer needs to flush cache before reboot. */
+ if (start && end) {
+ buffer->flush_nb.notifier_call = rb_flush_buffer_cb;
+ atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb);
+ }
+
return_ptr(buffer);
fail_free_buffers:
@@ -2677,6 +2696,9 @@ ring_buffer_free(struct trace_buffer *buffer)
{
int cpu;
+ if (buffer->range_addr_start && buffer->range_addr_end)
+ atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb);
+
cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node);
irq_work_sync(&buffer->irq_work.work);
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-11 1:32 ` [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic Masami Hiramatsu (Google)
@ 2026-03-18 14:19 ` Masami Hiramatsu
2026-03-18 14:53 ` Mathieu Desnoyers
0 siblings, 1 reply; 11+ messages in thread
From: Masami Hiramatsu @ 2026-03-18 14:19 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Steven Rostedt, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
On Wed, 11 Mar 2026 10:32:29 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
> 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.
Hmm, on some architectures, flush_cache_vmap() is implemented using
on_each_cpu() which waits IPI. But that does not safe in panic notifier
because it is called after smp_send_stop().
Since this cache flush issue is currently only confirmed on arm64,
I would like to make it doing nothing (do { } while (0)) by default.
Thanks,
>
> Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance")
> Cc: stable@vger.kernel.org
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> ---
> Changes in v9:
> - Fix typo of & to &&.
> - Fix typo of "Generic"
> Changes in v6:
> - Introduce asm/ring_buffer.h for arch_ring_buffer_flush_range().
> - Use flush_cache_vmap() instead of flush_cache_all().
> Changes in v5:
> - Use ring_buffer_record_off() instead of ring_buffer_record_disable().
> - Use flush_cache_all() to ensure flush all cache.
> Changes in v3:
> - update patch description.
> ---
> arch/alpha/include/asm/Kbuild | 1 +
> arch/arc/include/asm/Kbuild | 1 +
> arch/arm/include/asm/Kbuild | 1 +
> arch/arm64/include/asm/ring_buffer.h | 10 ++++++++++
> arch/csky/include/asm/Kbuild | 1 +
> arch/hexagon/include/asm/Kbuild | 1 +
> arch/loongarch/include/asm/Kbuild | 1 +
> arch/m68k/include/asm/Kbuild | 1 +
> arch/microblaze/include/asm/Kbuild | 1 +
> arch/mips/include/asm/Kbuild | 1 +
> arch/nios2/include/asm/Kbuild | 1 +
> arch/openrisc/include/asm/Kbuild | 1 +
> arch/parisc/include/asm/Kbuild | 1 +
> arch/powerpc/include/asm/Kbuild | 1 +
> arch/riscv/include/asm/Kbuild | 1 +
> arch/s390/include/asm/Kbuild | 1 +
> arch/sh/include/asm/Kbuild | 1 +
> arch/sparc/include/asm/Kbuild | 1 +
> arch/um/include/asm/Kbuild | 1 +
> arch/x86/include/asm/Kbuild | 1 +
> arch/xtensa/include/asm/Kbuild | 1 +
> include/asm-generic/ring_buffer.h | 13 +++++++++++++
> kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++
> 23 files changed, 65 insertions(+)
> create mode 100644 arch/arm64/include/asm/ring_buffer.h
> create mode 100644 include/asm-generic/ring_buffer.h
>
> diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
> index 483965c5a4de..b154b4e3dfa8 100644
> --- a/arch/alpha/include/asm/Kbuild
> +++ b/arch/alpha/include/asm/Kbuild
> @@ -5,4 +5,5 @@ generic-y += agp.h
> generic-y += asm-offsets.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += text-patching.h
> diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
> index 4c69522e0328..483caacc6988 100644
> --- a/arch/arc/include/asm/Kbuild
> +++ b/arch/arc/include/asm/Kbuild
> @@ -5,5 +5,6 @@ generic-y += extable.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> generic-y += parport.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> generic-y += text-patching.h
> diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
> index 03657ff8fbe3..decad5f2c826 100644
> --- a/arch/arm/include/asm/Kbuild
> +++ b/arch/arm/include/asm/Kbuild
> @@ -3,6 +3,7 @@ generic-y += early_ioremap.h
> generic-y += extable.h
> generic-y += flat.h
> generic-y += parport.h
> +generic-y += ring_buffer.h
>
> generated-y += mach-types.h
> generated-y += unistd-nr.h
> diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h
> new file mode 100644
> index 000000000000..62316c406888
> --- /dev/null
> +++ b/arch/arm64/include/asm/ring_buffer.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _ASM_ARM64_RING_BUFFER_H
> +#define _ASM_ARM64_RING_BUFFER_H
> +
> +#include <asm/cacheflush.h>
> +
> +/* Flush D-cache on persistent ring buffer */
> +#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end)
> +
> +#endif /* _ASM_ARM64_RING_BUFFER_H */
> diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild
> index 3a5c7f6e5aac..7dca0c6cdc84 100644
> --- a/arch/csky/include/asm/Kbuild
> +++ b/arch/csky/include/asm/Kbuild
> @@ -9,6 +9,7 @@ generic-y += qrwlock.h
> generic-y += qrwlock_types.h
> generic-y += qspinlock.h
> generic-y += parport.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> generic-y += vmlinux.lds.h
> generic-y += text-patching.h
> diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
> index 1efa1e993d4b..0f887d4238ed 100644
> --- a/arch/hexagon/include/asm/Kbuild
> +++ b/arch/hexagon/include/asm/Kbuild
> @@ -5,4 +5,5 @@ generic-y += extable.h
> generic-y += iomap.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += text-patching.h
> diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
> index 9034b583a88a..7e92957baf6a 100644
> --- a/arch/loongarch/include/asm/Kbuild
> +++ b/arch/loongarch/include/asm/Kbuild
> @@ -10,5 +10,6 @@ generic-y += qrwlock.h
> generic-y += user.h
> generic-y += ioctl.h
> generic-y += mmzone.h
> +generic-y += ring_buffer.h
> generic-y += statfs.h
> generic-y += text-patching.h
> diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
> index b282e0dd8dc1..62543bf305ff 100644
> --- a/arch/m68k/include/asm/Kbuild
> +++ b/arch/m68k/include/asm/Kbuild
> @@ -3,5 +3,6 @@ generated-y += syscall_table.h
> generic-y += extable.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += spinlock.h
> generic-y += text-patching.h
> diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
> index 7178f990e8b3..0030309b47ad 100644
> --- a/arch/microblaze/include/asm/Kbuild
> +++ b/arch/microblaze/include/asm/Kbuild
> @@ -5,6 +5,7 @@ generic-y += extable.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> generic-y += parport.h
> +generic-y += ring_buffer.h
> generic-y += syscalls.h
> generic-y += tlb.h
> generic-y += user.h
> diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
> index 684569b2ecd6..9771c3d85074 100644
> --- a/arch/mips/include/asm/Kbuild
> +++ b/arch/mips/include/asm/Kbuild
> @@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h
> generic-y += parport.h
> generic-y += qrwlock.h
> generic-y += qspinlock.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> generic-y += text-patching.h
> diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
> index 28004301c236..0a2530964413 100644
> --- a/arch/nios2/include/asm/Kbuild
> +++ b/arch/nios2/include/asm/Kbuild
> @@ -5,6 +5,7 @@ generic-y += cmpxchg.h
> generic-y += extable.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += spinlock.h
> generic-y += user.h
> generic-y += text-patching.h
> diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
> index cef49d60d74c..8aa34621702d 100644
> --- a/arch/openrisc/include/asm/Kbuild
> +++ b/arch/openrisc/include/asm/Kbuild
> @@ -8,4 +8,5 @@ generic-y += spinlock_types.h
> generic-y += spinlock.h
> generic-y += qrwlock_types.h
> generic-y += qrwlock.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
> index 4fb596d94c89..d48d158f7241 100644
> --- a/arch/parisc/include/asm/Kbuild
> +++ b/arch/parisc/include/asm/Kbuild
> @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
> generic-y += agp.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild
> index 2e23533b67e3..805b5aeebb6f 100644
> --- a/arch/powerpc/include/asm/Kbuild
> +++ b/arch/powerpc/include/asm/Kbuild
> @@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h
> generic-y += agp.h
> generic-y += mcs_spinlock.h
> generic-y += qrwlock.h
> +generic-y += ring_buffer.h
> generic-y += early_ioremap.h
> diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
> index bd5fc9403295..7721b63642f4 100644
> --- a/arch/riscv/include/asm/Kbuild
> +++ b/arch/riscv/include/asm/Kbuild
> @@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h
> generic-y += qrwlock.h
> generic-y += qrwlock_types.h
> generic-y += qspinlock.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> generic-y += vmlinux.lds.h
> diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild
> index 80bad7de7a04..0c1fc47c3ba0 100644
> --- a/arch/s390/include/asm/Kbuild
> +++ b/arch/s390/include/asm/Kbuild
> @@ -7,3 +7,4 @@ generated-y += unistd_nr.h
> generic-y += asm-offsets.h
> generic-y += mcs_spinlock.h
> generic-y += mmzone.h
> +generic-y += ring_buffer.h
> diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
> index 4d3f10ed8275..f0403d3ee8ab 100644
> --- a/arch/sh/include/asm/Kbuild
> +++ b/arch/sh/include/asm/Kbuild
> @@ -3,4 +3,5 @@ generated-y += syscall_table.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> generic-y += parport.h
> +generic-y += ring_buffer.h
> generic-y += text-patching.h
> diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
> index 17ee8a273aa6..49c6bb326b75 100644
> --- a/arch/sparc/include/asm/Kbuild
> +++ b/arch/sparc/include/asm/Kbuild
> @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
> generic-y += agp.h
> generic-y += kvm_para.h
> generic-y += mcs_spinlock.h
> +generic-y += ring_buffer.h
> generic-y += text-patching.h
> diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
> index 1b9b82bbe322..2a1629ba8140 100644
> --- a/arch/um/include/asm/Kbuild
> +++ b/arch/um/include/asm/Kbuild
> @@ -17,6 +17,7 @@ generic-y += module.lds.h
> generic-y += parport.h
> generic-y += percpu.h
> generic-y += preempt.h
> +generic-y += ring_buffer.h
> generic-y += runtime-const.h
> generic-y += softirq_stack.h
> generic-y += switch_to.h
> diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
> index 4566000e15c4..078fd2c0d69d 100644
> --- a/arch/x86/include/asm/Kbuild
> +++ b/arch/x86/include/asm/Kbuild
> @@ -14,3 +14,4 @@ generic-y += early_ioremap.h
> generic-y += fprobe.h
> generic-y += mcs_spinlock.h
> generic-y += mmzone.h
> +generic-y += ring_buffer.h
> diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
> index 13fe45dea296..e57af619263a 100644
> --- a/arch/xtensa/include/asm/Kbuild
> +++ b/arch/xtensa/include/asm/Kbuild
> @@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h
> generic-y += parport.h
> generic-y += qrwlock.h
> generic-y += qspinlock.h
> +generic-y += ring_buffer.h
> generic-y += user.h
> generic-y += text-patching.h
> diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h
> new file mode 100644
> index 000000000000..930d96571f23
> --- /dev/null
> +++ b/include/asm-generic/ring_buffer.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Generic arch dependent ring_buffer macros.
> + */
> +#ifndef __ASM_GENERIC_RING_BUFFER_H__
> +#define __ASM_GENERIC_RING_BUFFER_H__
> +
> +#include <linux/cacheflush.h>
> +
> +/* Flush cache on ring buffer range if needed */
> +#define arch_ring_buffer_flush_range(start, end) flush_cache_vmap(start, end)
> +
> +#endif /* __ASM_GENERIC_RING_BUFFER_H__ */
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 353a5aa1b612..9f4ee9e3803d 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -6,6 +6,7 @@
> */
> #include <linux/sched/isolation.h>
> #include <linux/trace_recursion.h>
> +#include <linux/panic_notifier.h>
> #include <linux/trace_events.h>
> #include <linux/ring_buffer.h>
> #include <linux/trace_clock.h>
> @@ -30,6 +31,7 @@
> #include <linux/oom.h>
> #include <linux/mm.h>
>
> +#include <asm/ring_buffer.h>
> #include <asm/local64.h>
> #include <asm/local.h>
> #include <asm/setup.h>
> @@ -589,6 +591,7 @@ struct trace_buffer {
>
> unsigned long range_addr_start;
> unsigned long range_addr_end;
> + struct notifier_block flush_nb;
>
> struct ring_buffer_meta *meta;
>
> @@ -2471,6 +2474,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
> kfree(cpu_buffer);
> }
>
> +/* Stop recording on a persistent buffer and flush cache if needed. */
> +static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
> +{
> + struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
> +
> + ring_buffer_record_off(buffer);
> + arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
> + return NOTIFY_DONE;
> +}
> +
> static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
> int order, unsigned long start,
> unsigned long end,
> @@ -2590,6 +2603,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
>
> mutex_init(&buffer->mutex);
>
> + /* Persistent ring buffer needs to flush cache before reboot. */
> + if (start && end) {
> + buffer->flush_nb.notifier_call = rb_flush_buffer_cb;
> + atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb);
> + }
> +
> return_ptr(buffer);
>
> fail_free_buffers:
> @@ -2677,6 +2696,9 @@ ring_buffer_free(struct trace_buffer *buffer)
> {
> int cpu;
>
> + if (buffer->range_addr_start && buffer->range_addr_end)
> + atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb);
> +
> cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node);
>
> irq_work_sync(&buffer->irq_work.work);
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-18 14:19 ` Masami Hiramatsu
@ 2026-03-18 14:53 ` Mathieu Desnoyers
2026-03-18 15:29 ` Masami Hiramatsu
0 siblings, 1 reply; 11+ messages in thread
From: Mathieu Desnoyers @ 2026-03-18 14:53 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Steven Rostedt, linux-kernel, linux-trace-kernel, Ian Rogers
On 2026-03-18 10:19, Masami Hiramatsu (Google) wrote:
> On Wed, 11 Mar 2026 10:32:29 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
>
>> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>
>> 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.
>
> Hmm, on some architectures, flush_cache_vmap() is implemented using
> on_each_cpu() which waits IPI. But that does not safe in panic notifier
> because it is called after smp_send_stop().
>
> Since this cache flush issue is currently only confirmed on arm64,
> I would like to make it doing nothing (do { } while (0)) by default.
FWIW, I've sent a related series a while ago about flushing pmem
areas to memory on panic:
https://lore.kernel.org/lkml/20240618154157.334602-3-mathieu.desnoyers@efficios.com/
When reading your patch, I feel like I'm missing something, so please bear with
me for a few questions:
- What exactly are you trying to flush ? By "flush" do you mean
evince cache lines or write back cache lines ? (I expect you aim
at the second option)
- AFAIU, you are not trying to evince cache lines after creation
of a new virtual mapping (which is the documented intent of
flush_cache_vmap).
- AFAIU flush_cache_vmap maps to no-code on arm64 (asm-generic), what am
I missing ? It makes sense to be a no-op because AFAIR arm64 does not
have to deal with virtually aliasing caches.
see commit 8690bbcf3b7 ("Introduce cpu_dcache_is_aliasing() across all architectures")
The arch_wb_cache_pmem is specific to pmem, which is not exactly what you want
to use, but on arm64 it's implemented as:
/* Ensure order against any prior non-cacheable writes */
dmb(osh);
dcache_clean_pop((unsigned long)addr, (unsigned long)addr + size);
Which I think has the writeback semantic you are looking for, and AFAIU should no
require IPIs (at least on arm64) to flush cache lines across the entire system.
Cheers,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-18 14:53 ` Mathieu Desnoyers
@ 2026-03-18 15:29 ` Masami Hiramatsu
2026-03-18 15:51 ` Mathieu Desnoyers
0 siblings, 1 reply; 11+ messages in thread
From: Masami Hiramatsu @ 2026-03-18 15:29 UTC (permalink / raw)
To: Mathieu Desnoyers
Cc: Steven Rostedt, linux-kernel, linux-trace-kernel, Ian Rogers
On Wed, 18 Mar 2026 10:53:26 -0400
Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote:
> On 2026-03-18 10:19, Masami Hiramatsu (Google) wrote:
> > On Wed, 11 Mar 2026 10:32:29 +0900
> > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> >
> >> From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> >>
> >> 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.
> >
> > Hmm, on some architectures, flush_cache_vmap() is implemented using
> > on_each_cpu() which waits IPI. But that does not safe in panic notifier
> > because it is called after smp_send_stop().
> >
> > Since this cache flush issue is currently only confirmed on arm64,
> > I would like to make it doing nothing (do { } while (0)) by default.
>
> FWIW, I've sent a related series a while ago about flushing pmem
> areas to memory on panic:
>
> https://lore.kernel.org/lkml/20240618154157.334602-3-mathieu.desnoyers@efficios.com/
>
Ah, nice!
> When reading your patch, I feel like I'm missing something, so please bear with
> me for a few questions:
>
> - What exactly are you trying to flush ? By "flush" do you mean
> evince cache lines or write back cache lines ? (I expect you aim
> at the second option)
Yes, I need to write back cache lines, at least it can be
read after hot reboot. (not evict cache)
>
> - AFAIU, you are not trying to evince cache lines after creation
> of a new virtual mapping (which is the documented intent of
> flush_cache_vmap).
Ah, OK. That's a good point!
(anyway I will replace it with do { } while (0) in the next version.)
>
> - AFAIU flush_cache_vmap maps to no-code on arm64 (asm-generic), what am
> I missing ? It makes sense to be a no-op because AFAIR arm64 does not
> have to deal with virtually aliasing caches.
Yeah, so my patch also introduces arm64 specific implementation.
>
> see commit 8690bbcf3b7 ("Introduce cpu_dcache_is_aliasing() across all architectures")
OK, let me check.
>
> The arch_wb_cache_pmem is specific to pmem, which is not exactly what you want
> to use, but on arm64 it's implemented as:
>
> /* Ensure order against any prior non-cacheable writes */
> dmb(osh);
> dcache_clean_pop((unsigned long)addr, (unsigned long)addr + size);
>
> Which I think has the writeback semantic you are looking for, and AFAIU should no
> require IPIs (at least on arm64) to flush cache lines across the entire system.
Yes, that's what I need.
Thank you!
>
> Cheers,
>
> Mathieu
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-18 15:29 ` Masami Hiramatsu
@ 2026-03-18 15:51 ` Mathieu Desnoyers
2026-03-19 3:36 ` Masami Hiramatsu
0 siblings, 1 reply; 11+ messages in thread
From: Mathieu Desnoyers @ 2026-03-18 15:51 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Steven Rostedt, linux-kernel, linux-trace-kernel, Ian Rogers
On 2026-03-18 11:29, Masami Hiramatsu (Google) wrote:
>>
>> - AFAIU, you are not trying to evince cache lines after creation
>> of a new virtual mapping (which is the documented intent of
>> flush_cache_vmap).
>
> Ah, OK. That's a good point!
> (anyway I will replace it with do { } while (0) in the next version.)
>
>>
>> - AFAIU flush_cache_vmap maps to no-code on arm64 (asm-generic), what am
>> I missing ? It makes sense to be a no-op because AFAIR arm64 does not
>> have to deal with virtually aliasing caches.
>
> Yeah, so my patch also introduces arm64 specific implementation.
Just make sure to call this something else than "flush_cache_vmap",
because you don't want to slow down vmap on arm64 which does not
require to evince and certainly not write back cache lines after
creation of a new virtual mapping.
Thanks,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic
2026-03-18 15:51 ` Mathieu Desnoyers
@ 2026-03-19 3:36 ` Masami Hiramatsu
0 siblings, 0 replies; 11+ messages in thread
From: Masami Hiramatsu @ 2026-03-19 3:36 UTC (permalink / raw)
To: Mathieu Desnoyers
Cc: Steven Rostedt, linux-kernel, linux-trace-kernel, Ian Rogers
On Wed, 18 Mar 2026 11:51:28 -0400
Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote:
> On 2026-03-18 11:29, Masami Hiramatsu (Google) wrote:
> >>
> >> - AFAIU, you are not trying to evince cache lines after creation
> >> of a new virtual mapping (which is the documented intent of
> >> flush_cache_vmap).
> >
> > Ah, OK. That's a good point!
> > (anyway I will replace it with do { } while (0) in the next version.)
> >
> >>
> >> - AFAIU flush_cache_vmap maps to no-code on arm64 (asm-generic), what am
> >> I missing ? It makes sense to be a no-op because AFAIR arm64 does not
> >> have to deal with virtually aliasing caches.
> >
> > Yeah, so my patch also introduces arm64 specific implementation.
>
> Just make sure to call this something else than "flush_cache_vmap",
> because you don't want to slow down vmap on arm64 which does not
> require to evince and certainly not write back cache lines after
> creation of a new virtual mapping.
OK, I will just leave it an empty do-while in asm-generic instead of
flush_cache_vmap(). If any architecture finds persistent ring buffer
needs to write back caches, it can add its own flush implementation.
BTW, do we need dmb(osh)? This runs dcache_clean_pop() after atomic
operation in ring_buffer_record_off().
ring_buffer_record_off(buffer);
arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
Thank you,
>
> Thanks,
>
> Mathieu
>
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://www.efficios.com
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v9 3/4] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
2026-03-11 1:32 [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu (Google)
2026-03-11 1:32 ` [PATCH v9 1/4] ring-buffer: Fix to update per-subbuf entries of persistent ring buffer Masami Hiramatsu (Google)
2026-03-11 1:32 ` [PATCH v9 2/4] ring-buffer: Flush and stop persistent ring buffer on panic Masami Hiramatsu (Google)
@ 2026-03-11 1:32 ` Masami Hiramatsu (Google)
2026-03-11 1:32 ` [PATCH v9 4/4] ring-buffer: Add persistent ring buffer selftest Masami Hiramatsu (Google)
2026-03-16 23:21 ` [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu
4 siblings, 0 replies; 11+ messages in thread
From: Masami Hiramatsu (Google) @ 2026-03-11 1:32 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Skip invalid sub-buffers when validating the persistent ring buffer
instead of discarding the entire ring buffer. Only skipped buffers
are invalidated (cleared).
If the cache data in memory fails to be synchronized during a reboot,
the persistent ring buffer may become partially corrupted, but other
sub-buffers may still contain readable event data. Only discard the
subbuffersa that ar found to be corrupted.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v9:
- Add meta->subbuf_size check.
- Fix a typo.
- Handle invalid reader_page case.
Changes in v8:
- Add comment in rb_valudate_buffer()
- Clear the RB_MISSED_* flags in rb_valudate_buffer() instead of
skipping subbuf.
- Remove unused subbuf local variable from rb_cpu_meta_valid().
Changes in v7:
- Combined with Handling RB_MISSED_* flags patch, focus on validation at boot.
- Remove checking subbuffer data when validating metadata, because it should be done
later.
- Do not mark the discarded sub buffer page but just reset it.
Changes in v6:
- Show invalid page detection message once per CPU.
Changes in v5:
- Instead of showing errors for each page, just show the number
of discarded pages at last.
Changes in v3:
- Record missed data event on commit.
---
kernel/trace/ring_buffer.c | 98 ++++++++++++++++++++++++++------------------
1 file changed, 58 insertions(+), 40 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 9f4ee9e3803d..91b3f18d707b 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -396,6 +396,12 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
return local_read(&bpage->page->commit);
}
+/* Size is determined by what has been committed */
+static __always_inline unsigned int rb_page_size(struct buffer_page *bpage)
+{
+ return rb_page_commit(bpage) & ~RB_MISSED_MASK;
+}
+
static void free_buffer_page(struct buffer_page *bpage)
{
/* Range pages are not to be freed */
@@ -1791,7 +1797,6 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
unsigned long *subbuf_mask)
{
int subbuf_size = PAGE_SIZE;
- struct buffer_data_page *subbuf;
unsigned long buffers_start;
unsigned long buffers_end;
int i;
@@ -1799,6 +1804,11 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
if (!subbuf_mask)
return false;
+ if (meta->subbuf_size != PAGE_SIZE) {
+ pr_info("Ring buffer boot meta [%d] invalid subbuf_size\n", cpu);
+ return false;
+ }
+
buffers_start = meta->first_buffer;
buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs);
@@ -1815,11 +1825,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- subbuf = rb_subbufs_from_meta(meta);
-
bitmap_clear(subbuf_mask, 0, meta->nr_subbufs);
- /* Is the meta buffers and the subbufs themselves have correct data? */
+ /*
+ * Ensure the meta::buffers array has correct data. The data in each subbufs
+ * are checked later in rb_meta_validate_events().
+ */
for (i = 0; i < meta->nr_subbufs; i++) {
if (meta->buffers[i] < 0 ||
meta->buffers[i] >= meta->nr_subbufs) {
@@ -1827,18 +1838,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- if ((unsigned)local_read(&subbuf->commit) > subbuf_size) {
- pr_info("Ring buffer boot meta [%d] buffer invalid commit\n", cpu);
- return false;
- }
-
if (test_bit(meta->buffers[i], subbuf_mask)) {
pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu);
return false;
}
set_bit(meta->buffers[i], subbuf_mask);
- subbuf = (void *)subbuf + subbuf_size;
}
return true;
@@ -1902,13 +1907,22 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu)
+static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
+ struct ring_buffer_cpu_meta *meta)
{
unsigned long long ts;
+ unsigned long tail;
u64 delta;
- int tail;
- tail = local_read(&dpage->commit);
+ /*
+ * When a sub-buffer is recovered from a read, the commit value may
+ * have RB_MISSED_* bits set, as these bits are reset on reuse.
+ * Even after clearing these bits, a commit value greater than the
+ * subbuf_size is considered invalid.
+ */
+ tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ if (tail > meta->subbuf_size)
+ return -1;
return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
}
@@ -1919,6 +1933,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
struct buffer_page *head_page, *orig_head;
unsigned long entry_bytes = 0;
unsigned long entries = 0;
+ int discarded = 0;
int ret;
u64 ts;
int i;
@@ -1929,14 +1944,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_head = head_page = cpu_buffer->head_page;
/* Do the reader page first */
- ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer reader page is invalid\n");
- goto invalid;
+ pr_info("Ring buffer meta [%d] invalid reader page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&cpu_buffer->reader_page->entries, 0);
+ local_set(&cpu_buffer->reader_page->page->commit, 0);
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(cpu_buffer->reader_page);
+ local_set(&cpu_buffer->reader_page->entries, ret);
}
- entries += ret;
- entry_bytes += local_read(&cpu_buffer->reader_page->page->commit);
- local_set(&cpu_buffer->reader_page->entries, ret);
ts = head_page->page->time_stamp;
@@ -1964,7 +1984,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
break;
/* Stop rewind if the page is invalid. */
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0)
break;
@@ -2043,21 +2063,24 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == cpu_buffer->reader_page)
continue;
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer meta [%d] invalid buffer page\n",
- cpu_buffer->cpu);
- goto invalid;
- }
-
- /* If the buffer has content, update pages_touched */
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
-
- entries += ret;
- entry_bytes += local_read(&head_page->page->commit);
- local_set(&head_page->entries, ret);
+ if (!discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&head_page->entries, 0);
+ local_set(&head_page->page->commit, 0);
+ } else {
+ /* If the buffer has content, update pages_touched */
+ if (ret)
+ local_inc(&cpu_buffer->pages_touched);
+ entries += ret;
+ entry_bytes += rb_page_size(head_page);
+ local_set(&head_page->entries, ret);
+ }
if (head_page == cpu_buffer->commit_page)
break;
}
@@ -2071,7 +2094,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
local_set(&cpu_buffer->entries, entries);
local_set(&cpu_buffer->entries_bytes, entry_bytes);
- pr_info("Ring buffer meta [%d] is from previous boot!\n", cpu_buffer->cpu);
+ pr_info("Ring buffer meta [%d] is from previous boot! (%d pages discarded)\n",
+ cpu_buffer->cpu, discarded);
return;
invalid:
@@ -3258,12 +3282,6 @@ rb_iter_head_event(struct ring_buffer_iter *iter)
return NULL;
}
-/* Size is determined by what has been committed */
-static __always_inline unsigned rb_page_size(struct buffer_page *bpage)
-{
- return rb_page_commit(bpage) & ~RB_MISSED_MASK;
-}
-
static __always_inline unsigned
rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer)
{
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v9 4/4] ring-buffer: Add persistent ring buffer selftest
2026-03-11 1:32 [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu (Google)
` (2 preceding siblings ...)
2026-03-11 1:32 ` [PATCH v9 3/4] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer Masami Hiramatsu (Google)
@ 2026-03-11 1:32 ` Masami Hiramatsu (Google)
2026-03-16 23:21 ` [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu
4 siblings, 0 replies; 11+ messages in thread
From: Masami Hiramatsu (Google) @ 2026-03-11 1:32 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a self-destractive test for the persistent ring buffer. This
will invalidate some sub-buffer pages in the persistent ring buffer
when kernel gets panic, and check whether the number of detected
invalid pages is the same as record after reboot.
This can ensure the kernel correctly recover partially corrupted
persistent ring buffer when boot.
The test only runs on the persistent ring buffer whose name is
"ptracingtest". And user has to fill it up with events before
kernel panics.
To run the test, enable CONFIG_RING_BUFFER_PERSISTENT_SELFTEST
and you have to setup the kernel cmdline;
reserve_mem=20M:2M:trace trace_instance=ptracingtest^traceoff@trace
panic=1
And run following commands after the 1st boot;
cd /sys/kernel/tracing/instances/ptracingtest
echo 1 > tracing_on
echo 1 > events/enable
sleep 3
echo c > /proc/sysrq-trigger
After panic message, the kernel will reboot and run the verification
on the persistent ring buffer, e.g.
Ring buffer meta [1] invalid buffer page detected
Ring buffer meta [1] is from previous boot! (318 pages discarded)
Ring buffer testing [1]: PASSED (318/318)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v9:
- Test also reader pages.
---
include/linux/ring_buffer.h | 1 +
kernel/trace/Kconfig | 15 +++++++++++++
kernel/trace/ring_buffer.c | 49 +++++++++++++++++++++++++++++++++++++++++++
kernel/trace/trace.c | 4 ++++
4 files changed, 69 insertions(+)
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index 876358cfe1b1..927b6e8587cb 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -238,6 +238,7 @@ int ring_buffer_subbuf_size_get(struct trace_buffer *buffer);
enum ring_buffer_flags {
RB_FL_OVERWRITE = 1 << 0,
+ RB_FL_TESTING = 1 << 1,
};
#ifdef CONFIG_RING_BUFFER
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 49de13cae428..2e6f3b7c6a31 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -1202,6 +1202,21 @@ config RING_BUFFER_VALIDATE_TIME_DELTAS
Only say Y if you understand what this does, and you
still want it enabled. Otherwise say N
+config RING_BUFFER_PERSISTENT_SELFTEST
+ bool "Enable persistent ring buffer selftest"
+ depends on RING_BUFFER
+ help
+ Run a selftest on the persistent ring buffer which names
+ "ptracingtest" (and its backup) when panic_on_reboot by
+ invalidating ring buffer pages.
+ Note that user has to enable events on the persistent ring
+ buffer manually to fill up ring buffers before rebooting.
+ Since this invalidates the data on test target ring buffer,
+ "ptracingtest" persistent ring buffer must not be used for
+ actual tracing, but only for testing.
+
+ If unsure, say N
+
config MMIOTRACE_TEST
tristate "Test module for mmiotrace"
depends on MMIOTRACE && m
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 91b3f18d707b..bfe213c89b43 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -63,6 +63,7 @@ struct ring_buffer_cpu_meta {
unsigned long commit_buffer;
__u32 subbuf_size;
__u32 nr_subbufs;
+ __u32 nr_invalid;
int buffers[];
};
@@ -2096,6 +2097,11 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
pr_info("Ring buffer meta [%d] is from previous boot! (%d pages discarded)\n",
cpu_buffer->cpu, discarded);
+ if (meta->nr_invalid)
+ pr_info("Ring buffer testing [%d]: %s (%d/%d)\n",
+ cpu_buffer->cpu,
+ (discarded == meta->nr_invalid) ? "PASSED" : "FAILED",
+ discarded, meta->nr_invalid);
return;
invalid:
@@ -2498,12 +2504,55 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_SELFTEST
+static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
+{
+ struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_cpu_meta *meta;
+ struct buffer_data_page *dpage;
+ unsigned long ptr;
+ int subbuf_size;
+ int invalid = 0;
+ int cpu;
+ int i;
+
+ if (!(buffer->flags & RB_FL_TESTING))
+ return;
+
+ guard(preempt)();
+ cpu = smp_processor_id();
+
+ cpu_buffer = buffer->buffers[cpu];
+ meta = cpu_buffer->ring_meta;
+ ptr = (unsigned long)rb_subbufs_from_meta(meta);
+ subbuf_size = meta->subbuf_size;
+
+ /* Invalidate even pages. */
+ for (i = 0; i < meta->nr_subbufs; i += 2) {
+ int idx = meta->buffers[i];
+
+ dpage = (void *)(ptr + idx * subbuf_size);
+ /* Skip unused pages */
+ if (!local_read(&dpage->commit))
+ continue;
+ local_add(subbuf_size + 1, &dpage->commit);
+ invalid++;
+ }
+
+ pr_info("Inject invalidated %d pages on CPU%d\n", invalid, cpu);
+ meta->nr_invalid = invalid;
+}
+#else /* !CONFIG_RING_BUFFER_PERSISTENT_SELFTEST */
+#define rb_test_inject_invalid_pages(buffer) do { } while (0)
+#endif
+
/* Stop recording on a persistent buffer and flush cache if needed. */
static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
{
struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
ring_buffer_record_off(buffer);
+ rb_test_inject_invalid_pages(buffer);
arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
return NOTIFY_DONE;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 23de3719f495..eccc1ff22f71 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9336,6 +9336,8 @@ static void setup_trace_scratch(struct trace_array *tr,
memset(tscratch, 0, size);
}
+#define TRACE_TEST_PTRACING_NAME "ptracingtest"
+
static int
allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size)
{
@@ -9348,6 +9350,8 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size
buf->tr = tr;
if (tr->range_addr_start && tr->range_addr_size) {
+ if (!strcmp(tr->name, TRACE_TEST_PTRACING_NAME))
+ rb_flags |= RB_FL_TESTING;
/* Add scratch buffer to handle 128 modules */
buf->buffer = ring_buffer_alloc_range(size, rb_flags, 0,
tr->range_addr_start,
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust
2026-03-11 1:32 [PATCH v9 0/4] ring-buffer: Making persistent ring buffers robust Masami Hiramatsu (Google)
` (3 preceding siblings ...)
2026-03-11 1:32 ` [PATCH v9 4/4] ring-buffer: Add persistent ring buffer selftest Masami Hiramatsu (Google)
@ 2026-03-16 23:21 ` Masami Hiramatsu
4 siblings, 0 replies; 11+ messages in thread
From: Masami Hiramatsu @ 2026-03-16 23:21 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Steven Rostedt, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers
On Wed, 11 Mar 2026 10:32:11 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> Hi,
>
> Here is the 9th version of improvement patches for making persistent
> ring buffers robust to failures.
> The previous version is here:
>
> https://lore.kernel.org/all/177303264034.767813.5345788067082238396.stgit@mhiramat.tok.corp.google.com/
>
> In this version, I fixed bugs/typos in [2/4][3/4] and add a bugfix patch
> [1/4] and a test[4/4]. Also, add a meta->subbuf_size validation[3/4].
Hmm, the test case fails if rewinding happens, because the
data_page validation failed in rewinding and stop rewinding.
The test may need to be designed more carefully.
Others looks good to me.
Thanks,
>
> Thank you,
>
> ---
>
> Masami Hiramatsu (Google) (4):
> ring-buffer: Fix to update per-subbuf entries of persistent ring buffer
> ring-buffer: Flush and stop persistent ring buffer on panic
> ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
> ring-buffer: Add persistent ring buffer selftest
>
>
> arch/alpha/include/asm/Kbuild | 1
> arch/arc/include/asm/Kbuild | 1
> arch/arm/include/asm/Kbuild | 1
> arch/arm64/include/asm/ring_buffer.h | 10 ++
> arch/csky/include/asm/Kbuild | 1
> arch/hexagon/include/asm/Kbuild | 1
> arch/loongarch/include/asm/Kbuild | 1
> arch/m68k/include/asm/Kbuild | 1
> arch/microblaze/include/asm/Kbuild | 1
> arch/mips/include/asm/Kbuild | 1
> arch/nios2/include/asm/Kbuild | 1
> arch/openrisc/include/asm/Kbuild | 1
> arch/parisc/include/asm/Kbuild | 1
> arch/powerpc/include/asm/Kbuild | 1
> arch/riscv/include/asm/Kbuild | 1
> arch/s390/include/asm/Kbuild | 1
> arch/sh/include/asm/Kbuild | 1
> arch/sparc/include/asm/Kbuild | 1
> arch/um/include/asm/Kbuild | 1
> arch/x86/include/asm/Kbuild | 1
> arch/xtensa/include/asm/Kbuild | 1
> include/asm-generic/ring_buffer.h | 13 +++
> include/linux/ring_buffer.h | 1
> kernel/trace/Kconfig | 15 +++
> kernel/trace/ring_buffer.c | 169 ++++++++++++++++++++++++++--------
> kernel/trace/trace.c | 4 +
> 26 files changed, 192 insertions(+), 40 deletions(-)
> create mode 100644 arch/arm64/include/asm/ring_buffer.h
> create mode 100644 include/asm-generic/ring_buffer.h
>
> --
> Masami Hiramatsu (Google) <mhiramat@kernel.org>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread