From: Eduard Zingerman <eddyz87@gmail.com>
To: Tamir Duberstein <tamird@kernel.org>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Martin KaFai Lau <martin.lau@linux.dev>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>, Shuah Khan <shuah@kernel.org>,
Andrea Righi <arighi@nvidia.com>,
Xu Kuohai <xukuohai@huawei.com>,
Andrea Righi <andrea.righi@canonical.com>,
Bing-Jhong Billy Jheng <billy@starlabs.sg>,
David Vernet <void@manifault.com>
Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org,
Andrew Werner <awerner32@gmail.com>,
Zvi Effron <zeffron@riotgames.com>,
Andrii Nakryiko <andriin@fb.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
Sashiko <sashiko-bot@kernel.org>
Subject: Re: [PATCH bpf v2 8/8] libbpf: ringbuf: Prevent missed wakeups
Date: Fri, 19 Jun 2026 18:53:54 -0700 [thread overview]
Message-ID: <be221942819c7654f054b53907fe9ff2e9526f79.camel@gmail.com> (raw)
In-Reply-To: <20260618-bpf-ringbuf-fixes-v2-8-33fde039ddf3@kernel.org>
On Thu, 2026-06-18 at 20:26 -0400, Tamir Duberstein wrote:
> After consuming the last visible record, ringbuf_process_ring()
> publishes the consumer position and checks the producer position. These
> operations lack a full StoreLoad barrier. A producer can therefore
> commit a new record but read the old consumer position while the
> consumer reads the old producer position. The producer sends no
> notification and the consumer waits despite a queued record.
>
> Insert a full barrier between publishing a consumer position and the
> next producer position load. When a record bound or callback ends the
> current invocation first, execute the barrier before returning so the
> load in a later invocation completes the same handshake.
>
> Add an edge-triggered epoll test that drains one record per call while a
> concurrent producer fills the ring. Without the barrier, a missed
> notification leaves the producer dropping records from a full ring while
> the consumer times out. Document that bounded consumers and callbacks
> that terminate consumption must drain before waiting again.
>
> Fixes: bf99c936f947 ("libbpf: Add BPF ring buffer support")
> Reported-by: Andrew Werner <awerner32@gmail.com>
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://lore.kernel.org/bpf/20260614015716.945AF1F000E9@smtp.kernel.org/
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Tamir Duberstein <tamird@kernel.org>
> ---
Took me a while, but I agree there is a race here.
FWIW, here is the description of the race as far as I understand it.
Simplified pseudo-code for the consumer (ringbuf_process_ring)
cons_pos = load_acquire(consumer_pos); // op#1 [orders before 2,3,4,5]
do {
got_new_data = false;
prod_pos = load_acquire(producer_pos); // op#2 [orders before 3,4,5]
while (cons_pos < prod_pos) {
len_ptr = ... cons_pos ...;
len = load_acquire(len_ptr); // op#3 [orders before 4,5]
got_new_data = true;
callback(... cons_pos ...); // op#4
cons_pos += len;
store_release(consumer_pos, cons_pos); // op#5 [orders after 2,3,4; ordering relative to 2' not defined]
}
} while (got_new_data);
The patch fixes the issue with op#5, store_release operation is
ordered after operations 2,3,4, but it's ordering relative to op#2
from the next outer loop iteration (denoted as 2') is not defined.
Simplified pseudo-code for the producer (bpf_ringbuf_{reserve,commit})
// reserve
store_release(producer_pos, ...);
// commit
xchg(... clear BUSY_BIT ...);
cons_pos = load_acquire(consumer_pos);
if (cons_pos == rec_pos)
schedule_wakeup();
A possible sequence of events
Producer:
// submits a record such that:
consumer_pos = 0 (initial state)
producer_pos = 10
Consumer:
cons_pos = load_acquire(consumer_pos) -> cons_pos = 0
prod_pos = load_acquire(producer_pos) -> prod_pos = 10
len = load_acquire(len_ptr) -> len = 10
callback(...) -> (record at pos 0 consumed)
cons_pos += len -> cons_pos = 10
store_release(consumer_pos, cons_pos) -> consumer_pos = 10 (issued; global visibility deferred)
(cons_pos < prod_pos) 10 < 10 -> false, exit inner loop
while (got_new_data) -> true, re-enter outer loop
prod_pos = load_acquire(producer_pos) -> prod_pos = 10 (reads current value; record at pos 10 not published yet)
(cons_pos < prod_pos) 10 < 10 -> false, skip inner loop
while (got_new_data) -> false; consumer returns 0 and enters an epoll wait
Producer (reserves and submits a new record):
store_release(producer_pos, 20) -> producer_pos = 20 (record at pos 10 reserved)
xchg(... clear BUSY_BIT ...) -> (record at pos 10 committed)
cons_pos = load_acquire(consumer_pos) -> cons_pos = 0 (consumer's store of 10 not visible yet)
rec_pos = 10; cons_pos == rec_pos -> 0 == 10 false -> NO WAKEUP
Consumer:
(deferred store becomes visible) -> consumer_pos = 10 (too late)
---
Consumer exits while a new record is available in the buffer,
and the producer fails to notify the consumer via epoll.
The added barrier prevents such sequence of events.
For the fix:
Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Please move the test to a separate commit, I'll review it a bit later.
[...]
next prev parent reply other threads:[~2026-06-20 1:53 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-19 0:26 [PATCH bpf v2 0/8] bpf: Fix ring buffer handling Tamir Duberstein
2026-06-19 0:26 ` [PATCH bpf v2 1/8] libbpf: ringbuf: Honor zero consume bounds Tamir Duberstein
2026-06-19 21:13 ` Eduard Zingerman
2026-06-19 0:26 ` [PATCH bpf v2 2/8] libbpf: ringbuf: Prevent NULL callback crash Tamir Duberstein
2026-06-19 13:54 ` Jiri Olsa
2026-06-19 21:11 ` Eduard Zingerman
2026-06-20 2:48 ` Alexei Starovoitov
2026-06-19 0:26 ` [PATCH bpf v2 3/8] libbpf: ringbuf: Reject overwrite callback use Tamir Duberstein
2026-06-19 0:26 ` [PATCH bpf v2 4/8] libbpf: ringbuf: Handle position counter wrap Tamir Duberstein
2026-06-19 0:26 ` [PATCH bpf v2 5/8] bpf: ringbuf: Handle pending position wrap Tamir Duberstein
2026-06-19 0:26 ` [PATCH bpf v2 6/8] bpf: user_ringbuf: Handle " Tamir Duberstein
2026-06-23 20:15 ` Andrii Nakryiko
2026-06-19 0:26 ` [PATCH bpf v2 7/8] libbpf: ringbuf: Use compiler atomics Tamir Duberstein
2026-06-23 20:16 ` Andrii Nakryiko
2026-06-19 0:26 ` [PATCH bpf v2 8/8] libbpf: ringbuf: Prevent missed wakeups Tamir Duberstein
2026-06-20 1:53 ` Eduard Zingerman [this message]
2026-06-23 20:27 ` Andrii Nakryiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=be221942819c7654f054b53907fe9ff2e9526f79.camel@gmail.com \
--to=eddyz87@gmail.com \
--cc=andrea.righi@canonical.com \
--cc=andrii@kernel.org \
--cc=andriin@fb.com \
--cc=arighi@nvidia.com \
--cc=ast@kernel.org \
--cc=awerner32@gmail.com \
--cc=billy@starlabs.sg \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=emil@etsalapatis.com \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=sashiko-bot@kernel.org \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=tamird@kernel.org \
--cc=void@manifault.com \
--cc=xukuohai@huawei.com \
--cc=yonghong.song@linux.dev \
--cc=zeffron@riotgames.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome