* [PATCH] ring-buffer: Handle a reader taking the new head in rb_handle_head_page()
@ 2026-11-02 14:30 Breno Leitao
0 siblings, 0 replies; only message in thread
From: Breno Leitao @ 2026-11-02 14:30 UTC (permalink / raw)
To: Steven Rostedt, Vincent Donnefort, Masami Hiramatsu, Mathieu Desnoyers
Cc: linux-kernel, linux-trace-kernel, Steven Rostedt, kernel-team,
stable, Breno Leitao
I am occasionally seeing the following KASAN warning when running
some debug kernel at Meta's fleet:
WARNING: kernel/trace/ring_buffer.c:3793 at rb_move_tail+0xf78/0x1050
Call trace:
rb_move_tail+0xf78/0x1050 (P)
__rb_reserve_next+0x648/0xac0
ring_buffer_lock_reserve+0xd00/0xf00
trace_buffer_lock_reserve+0x28/0x188
trace_graph_return+0x220/0x3e8
ftrace_return_to_handler+0x2bc/0x5a8
return_to_handler+0x20/0x48
__arm64_sys_sched_yield+0x18/0x30 (F)
I've understood that rb_handle_head_page() moves the head page forward
in two steps: it marks the pointer to the old head page UPDATE, then
marks the pointer to the page after it (new_head) HEAD. The second
cmpxchg is assumed to return only HEAD or NORMAL, but it can also return
RB_PAGE_MOVED:
CPU 0 (writer) CPU 1 (reader)
------------------------------------ --------------------------------
rb_handle_head_page()
old head ptr: HEAD -> UPDATE
new_head = page after old head
<interrupt: nested write>
sees UPDATE, marks new_head
HEAD and moves the tail page
</interrupt>
rb_get_reader_page()
swaps new_head out for the
reader page
cmpxchg(new_head|NORMAL ->
new_head|HEAD) -> MOVED
RB_WARN_ON()
RB_WARN_ON() disables the whole ring buffer, and the function returns
without clearing its UPDATE flag, so tracing stays off until the buffer
is reset.
Do not treat RB_PAGE_MOVED as fatal, retry after clearing UPDATE. The
head already advanced past both pages, so the caller can simply try
again without losing tracing.
Fixes: 77ae365eca89 ("ring-buffer: make lockless")
Cc: stable@vger.kernel.org
Signed-off-by: Breno Leitao <leitao@debian.org>
---
A nested writer and a consuming reader can race so that the second
cmpxchg of a head page move in rb_handle_head_page() returns
RB_PAGE_MOVED. That trips RB_WARN_ON(), which disables the ring buffer
and leaves the old head pointer flagged UPDATE. The patch handles the
return like the RB_PAGE_MOVED case of the first cmpxchg: clear the
UPDATE flag if this writer set it, and retry.
Testing, on arm64 KVM guests under virtme-ng (32 vCPUs, KASAN config,
next-20261030; kernel/trace/ring_buffer.c is identical in v7.4-rc1):
- Setup: function_graph tracer filtered to "*sched*", buffer_size_kb=7
(two pages per CPU), overwrite on, a trace_pipe_raw reader on every
CPU plus a trace_pipe reader, and stress-ng --yield/--timer/--futex.
- Without the patch the warning fired after about 12 minutes. With a
debug-only 50us udelay() between computing new_head and the cmpxchg
it fired within a second, and a debug print showed ret ==
RB_PAGE_MOVED, type == RB_PAGE_HEAD and new_head being the reader's
page.
- With the patch: no warning in a 5 minute run with the udelay() (the
RB_PAGE_MOVED case ran about 1000 times) or in a 25 minute run
without it (it ran once), and the buffer kept recording in both.
- checkpatch --strict is clean, and kernel/trace/ring_buffer.o builds
without warnings at W=1 on v7.4-rc1.
Not tested: the fix has not run on the arm64 Grace host where the
original WARN was seen.
The original WARN came from a next-20261030 KASAN kernel on an NVIDIA
Grace host under stress-ng, after an interrupted ftracetest run had left
that tracing setup in place.
The analysis, reproducer and patch were developed with an AI coding
assistant (Claude Code). It triaged the WARN from the netconsole logs,
wrote the virtme-ng reproducer and the debug instrumentation, and
drafted the fix and changelog.
---
kernel/trace/ring_buffer.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 1fd28a72e6ee13..7a60fa175e2cc6 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -3789,6 +3789,15 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer,
case RB_PAGE_NORMAL:
/* OK */
break;
+ case RB_PAGE_MOVED:
+ if (type == RB_PAGE_HEAD) {
+ ret = rb_head_page_set_normal(cpu_buffer, next_page,
+ tail_page,
+ RB_PAGE_UPDATE);
+ if (RB_WARN_ON(cpu_buffer, ret != RB_PAGE_UPDATE))
+ return -1;
+ }
+ return 1;
default:
RB_WARN_ON(cpu_buffer, 1);
return -1;
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261102-ring-buffer-head-moved-f9caf5da9444
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-02 14:30 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 14:30 [PATCH] ring-buffer: Handle a reader taking the new head in rb_handle_head_page() Breno Leitao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®