diff mbox series

libbpf: ringbuf: allow to partially consume items

Message ID 20240310154726.734289-1-andrea.righi@canonical.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series libbpf: ringbuf: allow to partially consume items | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization

Commit Message

Andrea Righi March 10, 2024, 3:47 p.m. UTC
Instead of always consuming all items from a ring buffer in a greedy
way, allow to stop when the callback returns a value > 0.

This allows to distinguish between an error condition and an intentional
stop condition by returning a non-negative non-zero value from the ring
buffer callback.

This can be useful, for example, to consume just a single item from the
ring buffer.

Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
---
 tools/lib/bpf/ringbuf.c | 8 ++++++++
 1 file changed, 8 insertions(+)

Comments

Andrii Nakryiko March 11, 2024, 5:55 p.m. UTC | #1
On Sun, Mar 10, 2024 at 8:47 AM Andrea Righi <andrea.righi@canonical.com> wrote:
>
> Instead of always consuming all items from a ring buffer in a greedy
> way, allow to stop when the callback returns a value > 0.
>
> This allows to distinguish between an error condition and an intentional
> stop condition by returning a non-negative non-zero value from the ring
> buffer callback.
>
> This can be useful, for example, to consume just a single item from the
> ring buffer.
>
> Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
> ---
>  tools/lib/bpf/ringbuf.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/tools/lib/bpf/ringbuf.c b/tools/lib/bpf/ringbuf.c
> index aacb64278a01..dd8908eb3204 100644
> --- a/tools/lib/bpf/ringbuf.c
> +++ b/tools/lib/bpf/ringbuf.c
> @@ -265,6 +265,14 @@ static int64_t ringbuf_process_ring(struct ring *r)
>                                         return err;
>                                 }
>                                 cnt++;
> +                               if (err > 0) {

So libbpf already stops at any err < 0 (and sets correct consumer
pos). So you could already get desired behavior by just returning your
own error code. If you need count, you'd have to count it yourself
through custom context, that's a bit of inconvenience.

But on the other hand, currently if user callback returns anything > 0
they keep going and that return value is ignored. Your change will
break any such user pretty badly. So I'm a bit hesitant to do this.

Is there any reason you can't just return error code (libbpf doesn't
do anything with it, just passes it back, so it might as well be
`-cnt`, if you need that).

pw-bot: cr

> +                                       /* update consumer pos and return the
> +                                        * total amount of items consumed.
> +                                        */
> +                                       smp_store_release(r->consumer_pos,
> +                                                         cons_pos);
> +                                       goto done;
> +                               }
>                         }
>
>                         smp_store_release(r->consumer_pos, cons_pos);
> --
> 2.43.0
>
>
Andrea Righi March 11, 2024, 8:25 p.m. UTC | #2
On Mon, Mar 11, 2024 at 10:55:57AM -0700, Andrii Nakryiko wrote:
> On Sun, Mar 10, 2024 at 8:47 AM Andrea Righi <andrea.righi@canonical.com> wrote:
> >
> > Instead of always consuming all items from a ring buffer in a greedy
> > way, allow to stop when the callback returns a value > 0.
> >
> > This allows to distinguish between an error condition and an intentional
> > stop condition by returning a non-negative non-zero value from the ring
> > buffer callback.
> >
> > This can be useful, for example, to consume just a single item from the
> > ring buffer.
> >
> > Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
> > ---
> >  tools/lib/bpf/ringbuf.c | 8 ++++++++
> >  1 file changed, 8 insertions(+)
> >
> > diff --git a/tools/lib/bpf/ringbuf.c b/tools/lib/bpf/ringbuf.c
> > index aacb64278a01..dd8908eb3204 100644
> > --- a/tools/lib/bpf/ringbuf.c
> > +++ b/tools/lib/bpf/ringbuf.c
> > @@ -265,6 +265,14 @@ static int64_t ringbuf_process_ring(struct ring *r)
> >                                         return err;
> >                                 }
> >                                 cnt++;
> > +                               if (err > 0) {
> 
> So libbpf already stops at any err < 0 (and sets correct consumer
> pos). So you could already get desired behavior by just returning your
> own error code. If you need count, you'd have to count it yourself
> through custom context, that's a bit of inconvenience.

Yep, that's exactly what I'm doing right now.

To give more context, here's the code:
https://github.com/sched-ext/scx/blob/b7c06b9ed9f72cad83c31e39e9c4e2cfd8683a55/rust/scx_rustland_core/src/bpf.rs#L217

> 
> But on the other hand, currently if user callback returns anything > 0
> they keep going and that return value is ignored. Your change will
> break any such user pretty badly. So I'm a bit hesitant to do this.

So, returning a value > 0 should have the same behavior as returning 0?
Why any user callback would return > 0 then?

> 
> Is there any reason you can't just return error code (libbpf doesn't
> do anything with it, just passes it back, so it might as well be
> `-cnt`, if you need that).

Sure, I can keep using my special error code to stop. It won't be a
problem for my particular use case.

Actually, one thing that it would be nice to have is a way to consume up
to a certain amount of items, let's say I need to copy multiple items
from the ring buffer to a limited user buffer. But that would require a
new API I guess, in order to pass the max counter... right?

Thanks,
-Andrea

> 
> pw-bot: cr
> 
> > +                                       /* update consumer pos and return the
> > +                                        * total amount of items consumed.
> > +                                        */
> > +                                       smp_store_release(r->consumer_pos,
> > +                                                         cons_pos);
> > +                                       goto done;
> > +                               }
> >                         }
> >
> >                         smp_store_release(r->consumer_pos, cons_pos);
> > --
> > 2.43.0
> >
> >
Andrii Nakryiko March 12, 2024, 10:42 p.m. UTC | #3
On Mon, Mar 11, 2024 at 1:25 PM Andrea Righi <andrea.righi@canonical.com> wrote:
>
> On Mon, Mar 11, 2024 at 10:55:57AM -0700, Andrii Nakryiko wrote:
> > On Sun, Mar 10, 2024 at 8:47 AM Andrea Righi <andrea.righi@canonical.com> wrote:
> > >
> > > Instead of always consuming all items from a ring buffer in a greedy
> > > way, allow to stop when the callback returns a value > 0.
> > >
> > > This allows to distinguish between an error condition and an intentional
> > > stop condition by returning a non-negative non-zero value from the ring
> > > buffer callback.
> > >
> > > This can be useful, for example, to consume just a single item from the
> > > ring buffer.
> > >
> > > Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
> > > ---
> > >  tools/lib/bpf/ringbuf.c | 8 ++++++++
> > >  1 file changed, 8 insertions(+)
> > >
> > > diff --git a/tools/lib/bpf/ringbuf.c b/tools/lib/bpf/ringbuf.c
> > > index aacb64278a01..dd8908eb3204 100644
> > > --- a/tools/lib/bpf/ringbuf.c
> > > +++ b/tools/lib/bpf/ringbuf.c
> > > @@ -265,6 +265,14 @@ static int64_t ringbuf_process_ring(struct ring *r)
> > >                                         return err;
> > >                                 }
> > >                                 cnt++;
> > > +                               if (err > 0) {
> >
> > So libbpf already stops at any err < 0 (and sets correct consumer
> > pos). So you could already get desired behavior by just returning your
> > own error code. If you need count, you'd have to count it yourself
> > through custom context, that's a bit of inconvenience.
>
> Yep, that's exactly what I'm doing right now.
>
> To give more context, here's the code:
> https://github.com/sched-ext/scx/blob/b7c06b9ed9f72cad83c31e39e9c4e2cfd8683a55/rust/scx_rustland_core/src/bpf.rs#L217
>

cool, great that you at least have a work-around


> >
> > But on the other hand, currently if user callback returns anything > 0
> > they keep going and that return value is ignored. Your change will
> > break any such user pretty badly. So I'm a bit hesitant to do this.
>
> So, returning a value > 0 should have the same behavior as returning 0?

yes, that's what the contract is right now

> Why any user callback would return > 0 then?

this is not the code I can control and ringbuf API was like that for a
long time, which is why I'm saying I'm hesitant to make these changes

>
> >
> > Is there any reason you can't just return error code (libbpf doesn't
> > do anything with it, just passes it back, so it might as well be
> > `-cnt`, if you need that).
>
> Sure, I can keep using my special error code to stop. It won't be a
> problem for my particular use case.
>
> Actually, one thing that it would be nice to have is a way to consume up
> to a certain amount of items, let's say I need to copy multiple items
> from the ring buffer to a limited user buffer. But that would require a
> new API I guess, in order to pass the max counter... right?

Yes, definitely a new API, but that's not a big problem. Though I'm
wondering if ring_buffer__consume_one() would be a more flexible API,
where user would have more flexible control. Either way libbpf is
doing smp_store_release() after each consumed element, so I don't
think it will have any downsides in terms of performance.

So please consider contributing a patch for this new API.

>
> Thanks,
> -Andrea
>
> >
> > pw-bot: cr
> >
> > > +                                       /* update consumer pos and return the
> > > +                                        * total amount of items consumed.
> > > +                                        */
> > > +                                       smp_store_release(r->consumer_pos,
> > > +                                                         cons_pos);
> > > +                                       goto done;
> > > +                               }
> > >                         }
> > >
> > >                         smp_store_release(r->consumer_pos, cons_pos);
> > > --
> > > 2.43.0
> > >
> > >
diff mbox series

Patch

diff --git a/tools/lib/bpf/ringbuf.c b/tools/lib/bpf/ringbuf.c
index aacb64278a01..dd8908eb3204 100644
--- a/tools/lib/bpf/ringbuf.c
+++ b/tools/lib/bpf/ringbuf.c
@@ -265,6 +265,14 @@  static int64_t ringbuf_process_ring(struct ring *r)
 					return err;
 				}
 				cnt++;
+				if (err > 0) {
+					/* update consumer pos and return the
+					 * total amount of items consumed.
+					 */
+					smp_store_release(r->consumer_pos,
+							  cons_pos);
+					goto done;
+				}
 			}
 
 			smp_store_release(r->consumer_pos, cons_pos);