extra io_uring BPF examples
Subsystems (4)
Pavel Begunkov
maintainercover extra io_uring BPF examples
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the io_uring BPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:
1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight io_uring request. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the main io_uring path.
3. A toy example of how BPF can interact with registered buffers.
Pavel Begunkov (3):
io_uring/selftests: add BPF'ed cp example io_uring/selftests: add rate limiter BPF program io_uring/selftests: add regbuf xor example
io_uring/bpf-ops.c | 29 +++ tools/testing/selftests/io_uring/Makefile | 2 +- .../testing/selftests/io_uring/common-defs.h | 27 +++ tools/testing/selftests/io_uring/cp.bpf.c | 134 ++++++++++++++ tools/testing/selftests/io_uring/cp.c | 130 +++++++++++++ tools/testing/selftests/io_uring/helpers.h | 15 +- .../selftests/io_uring/rate_limiter.bpf.c | 84 +++++++++ .../testing/selftests/io_uring/rate_limiter.c | 172 ++++++++++++++++++ tools/testing/selftests/io_uring/xor.bpf.c | 31 ++++ tools/testing/selftests/io_uring/xor.c | 83 +++++++++ 10 files changed, 702 insertions(+), 5 deletions(-) create mode 100644 tools/testing/selftests/io_uring/cp.bpf.c create mode 100644 tools/testing/selftests/io_uring/cp.c create mode 100644 tools/testing/selftests/io_uring/rate_limiter.bpf.c create mode 100644 tools/testing/selftests/io_uring/rate_limiter.c create mode 100644 tools/testing/selftests/io_uring/xor.bpf.c create mode 100644 tools/testing/selftests/io_uring/xor.c
Signature · 1 line
2.52.0
Pavel Begunkov
maintainerPATCH 1/3 io_uring/selftests: add BPF'ed cp example
Implement a simple QD=1 cp using BPF io_uring as an example of a state machine handling.
--- tools/testing/selftests/io_uring/Makefile | 2 +- .../testing/selftests/io_uring/common-defs.h | 10 ++ tools/testing/selftests/io_uring/cp.bpf.c | 134 ++++++++++++++++++ tools/testing/selftests/io_uring/cp.c | 130 +++++++++++++++++ 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/io_uring/cp.bpf.c create mode 100644 tools/testing/selftests/io_uring/cp.c
tools/testing/selftests/io_uring/Makefile +1 -1
@@ -3,7 +3,7 @@ include ../../../build/Build.include include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include -TEST_GEN_PROGS := nops_loop overflow unreg +TEST_GEN_PROGS := nops_loop overflow unreg cp # override lib.mk's default rules OVERRIDE_TARGETS := 1
tools/testing/selftests/io_uring/common-defs.h +10 -0
@@ -28,4 +28,14 @@ struct unreg_state { unsigned times_invoked; }; +struct cp_state { +int input_fd; +int output_fd; +void *buffer; +unsigned nr_infligt; +unsigned cur_offset; +size_t buffer_size; +int res; +}; +#endif /* IOU_TOOLS_COMMON_DEFS_H */
tools/testing/selftests/io_uring/cp.bpf.c new file +134 -0
134 lines not on this page · raw message
tools/testing/selftests/io_uring/cp.c new file +130 -0
130 lines not on this page · raw message
Signature · 1 line
2.52.0
Pavel Begunkov
maintainerPATCH 2/3 io_uring/selftests: add rate limiter BPF program
In reply to Pavel BegunkovShow how io_uring can be extended with features using BPF, which would've otherwise required extra complexity and overhead in the common io_uring for somewhat niche users. The rate limiter program caps how many requests can be in flight at the same time. If the user queues up more SQEs than the cap, their execution will be delayed until previous requests has completed. It'll automatically handle submitting deferred SQEs, as well will correct the waiting parameters based. E.g. if the user submits 8 SQEs and wants to wait for CQEs but limits it at 2 requests at a time, the program will wait for 2 CQEs multiple times until there are all 8 CQEs.
The test executes timeout requests and measures the total time to make sure they're not executed in parallel.
--- tools/testing/selftests/io_uring/Makefile | 2 +- .../testing/selftests/io_uring/common-defs.h | 11 ++ tools/testing/selftests/io_uring/helpers.h | 15 +- .../selftests/io_uring/rate_limiter.bpf.c | 84 +++++++++ .../testing/selftests/io_uring/rate_limiter.c | 172 ++++++++++++++++++ 5 files changed, 279 insertions(+), 5 deletions(-) create mode 100644 tools/testing/selftests/io_uring/rate_limiter.bpf.c create mode 100644 tools/testing/selftests/io_uring/rate_limiter.c
tools/testing/selftests/io_uring/Makefile +1 -1
@@ -3,7 +3,7 @@ include ../../../build/Build.include include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include -TEST_GEN_PROGS := nops_loop overflow unreg cp +TEST_GEN_PROGS := nops_loop overflow unreg cp rate_limiter # override lib.mk's default rules OVERRIDE_TARGETS := 1
tools/testing/selftests/io_uring/common-defs.h +11 -0
@@ -38,4 +38,15 @@ struct cp_state { int res; }; +struct rate_limiter_state { +unsigned inflight; +unsigned max_inflight; +unsigned cached_cq_tail; +unsigned target_cq_tail; +bool waitiing; + +unsigned to_wait; +int res; +}; +#endif /* IOU_TOOLS_COMMON_DEFS_H */
tools/testing/selftests/io_uring/helpers.h +11 -4
@@ -31,7 +31,9 @@ static inline void ring_ctx_destroy(struct ring_ctx *ctx)free (ctx->region); } -static inline void ring_ctx_create (struct ring_ctx *ctx,size_t region_size) +static inline void ring_ctx_create_flags (struct ring_ctx *ctx, +size_t region_size, +unsigned ring_flags) {struct io_uring_mem_region_reg mr;struct io_uring_region_desc rd;
@@ -47,11 +49,11 @@ static inline void ring_ctx_create(struct ring_ctx *ctx, size_t region_size)memset (¶ms,0 ,sizeof (params)); params.cq_entries = cq_entries; - params.flags =IORING_SETUP_SINGLE_ISSUER | + params.flags = ring_flags | +IORING_SETUP_SINGLE_ISSUER |IORING_SETUP_DEFER_TASKRUN |IORING_SETUP_NO_SQARRAY | -IORING_SETUP_CQSIZE | -IORING_SETUP_SQ_REWIND ; +IORING_SETUP_CQSIZE ; ret =io_uring_queue_init_params (sq_entries, &ctx->ring, ¶ms);if (ret) {
@@ -92,4 +94,9 @@ static inline void ring_ctx_create(struct ring_ctx *ctx, size_t region_size) ri->cq_entries = cq_entries; } +static inline void ring_ctx_create (struct ring_ctx *ctx,size_t region_size) +{ +ring_ctx_create_flags (ctx, region_size,IORING_SETUP_SQ_REWIND ); +} +#endif /* IOU_TOOLS_HELPERS_H */
tools/testing/selftests/io_uring/rate_limiter.bpf.c new file +84 -0
84 lines not on this page · raw message
tools/testing/selftests/io_uring/rate_limiter.c new file +172 -0
172 lines not on this page · raw message
Signature · 1 line
2.52.0
Pavel Begunkov
maintainerPATCH 3/3 io_uring/selftests: add regbuf xor example
In reply to Pavel BegunkovAdd a kfunc and an example of calculating xor for a registered buffer. That's useful for cases where the registered buffer is not visible by user space and exists in-kernel only. The rest depends on the BPF writer, and one way to use it could be to execute all jobs like xor'ing at the beginning and the do normal request processing (the example program just stops). Another approach would be to implement it as a part of BPF CQE processing.
We'll need to think through the registered buffer kfunc API before merging anything similar (not to mention implementing it properly).
--- io_uring/bpf-ops.c | 29 +++++++ tools/testing/selftests/io_uring/Makefile | 2 +- .../testing/selftests/io_uring/common-defs.h | 6 ++ tools/testing/selftests/io_uring/xor.bpf.c | 31 +++++++ tools/testing/selftests/io_uring/xor.c | 83 +++++++++++++++++++ 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/io_uring/xor.bpf.c create mode 100644 tools/testing/selftests/io_uring/xor.c
io_uring/bpf-ops.c +29 -0
@@ -9,6 +9,7 @@#include "memmap.h" #include "bpf-ops.h" #include "loop.h" +#include "rsrc.h" static DEFINE_MUTEX (io_bpf_ctrl_mutex);static const struct btf_type *loop_params_type;
@@ -47,11 +48,39 @@ __u8 *bpf_io_uring_get_region(struct io_ring_ctx *ctx, __u32 region_id,return io_region_get_ptr (r); } +__bpf_kfunc +__u8 bpf_io_uring_xor_regbuf(struct io_ring_ctx *ctx, unsigned idx, +size_t size) +{ +const struct io_mapped_ubuf *imu; +struct io_rsrc_node *node; +struct iov_iter iter; +__u8 xor_res =0 ; +__u8 buffer[512 ]; +int i, ret; + + node =io_rsrc_node_lookup (&ctx->buf_table, idx); +if (!node) +return 0 ; + imu = node->buf; +if (size > imu->len || size >sizeof (buffer)) +return 0 ; + +iov_iter_bvec (&iter,ITER_SOURCE , imu->bvec, imu->nr_bvecs, size); + ret =copy_from_iter (buffer, size, &iter); +if (ret != size) +return 0 ; +for (i =0 ; i < size; i++) + xor_res ^= buffer[i]; +return xor_res; +} +__bpf_kfunc_end_defs ();BTF_KFUNCS_START (io_uring_kfunc_set)BTF_ID_FLAGS (func, bpf_io_uring_submit_sqes, KF_SLEEPABLE);BTF_ID_FLAGS (func, bpf_io_uring_get_region,KF_RET_NULL ); +BTF_ID_FLAGS (func, bpf_io_uring_xor_regbuf,KF_SLEEPABLE );BTF_KFUNCS_END (io_uring_kfunc_set) static const struct btf_kfunc_id_set bpf_io_uring_kfunc_set = {
tools/testing/selftests/io_uring/Makefile +1 -1
@@ -3,7 +3,7 @@ include ../../../build/Build.include include ../../../scripts/Makefile.arch include ../../../scripts/Makefile.include -TEST_GEN_PROGS := nops_loop overflow unreg cp rate_limiter +TEST_GEN_PROGS := nops_loop overflow unreg cp rate_limiter xor # override lib.mk's default rules OVERRIDE_TARGETS := 1
tools/testing/selftests/io_uring/common-defs.h +6 -0
@@ -49,4 +49,10 @@ struct rate_limiter_state { int res; }; +struct xor_state { +unsigned regbuf_idx; +unsigned xor_size; +__u8 xor_res; +}; +#endif /* IOU_TOOLS_COMMON_DEFS_H */
tools/testing/selftests/io_uring/xor.bpf.c new file +31 -0
@@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/types.h> +#include <linux/errno.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include "vmlinux.h" +#include "common-defs.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const volatile struct ring_info ri; + +SEC("struct_ops.s/xor_step") +int BPF_PROG (xor_step ,struct io_ring_ctx *ring,struct iou_loop_params *ls) +{ +struct xor_state *xs; + + xs = (void *)bpf_io_uring_get_region (ring,IOU_REGION_MEM , +sizeof (*xs)); +if (!xs) +return IOU_LOOP_STOP ; + + xs->xor_res =bpf_io_uring_xor_regbuf (ring, xs->regbuf_idx, + xs->xor_size); +return IOU_LOOP_STOP ; +} + +SEC(".struct_ops.link") +struct io_uring_bpf_ops xor_ops = { + .loop_step = (void *)xor_step, +};
tools/testing/selftests/io_uring/xor.c new file +83 -0
83 lines not on this page · raw message
Signature · 1 line
2.52.0
Jens Axboe
maintainerQuoted 7 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:
Quoted 7 lines
1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
Signature · 1 line
Jens Axboe
Pavel Begunkov
maintainerQuoted 10 lines · Jens Axboe
Quoted 9 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:1. A QD=1 file copy program that show cases state machine handling.
Quoted 8 lines
Quoted 5 lines
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
I got you a liburing example
https://github.com/isilence/liburing/tree/bpf-ops-example
but need to sync with the selftest. I think I'll rather keep the rest into a separate repository instead of adding all helpers to liburing, which will inevitably do similar things but deviating in API and internal details. And it's simpler on dependency management instead of requiring libbpf / etc. for liburing. I wanted a space for misc io_uring bits that doesn't make sense to keep as core liburing anyway.
Signature · 1 line
Pavel Begunkov
Jens Axboe
maintainerQuoted 15 lines · Pavel Begunkov
Quoted 14 lines · Jens Axboe
Quoted 13 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
I got you a liburing example
Right, but that's just the nop thing, which isn't really interesting outside of doing basic verification of yes indeed the kernel side works.
Quoted 7 lines
but need to sync with the selftest. I think I'll rather keep the rest into a separate repository instead of adding all helpers to liburing, which will inevitably do similar things but deviating in API and internal details. And it's simpler on dependency management instead of requiring libbpf / etc. for liburing. I wanted a space for misc
io_uringbits that doesn't make sense to keep as core liburing anyway.
liburing should have support and documentation. It's not that hard to check for libbpf in configure and either build the support or not. Once various feature support ends up being fragmented in the ecosystem THAT is a mess for users, I'd much rather have it consolidated and deal with it on the liburing side.
Signature · 1 line
Jens Axboe
Pavel Begunkov
maintainerQuoted 36 lines · Jens Axboe
Quoted 15 lines · Pavel Begunkov
Quoted 14 lines · Jens Axboe
Quoted 13 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.Quoted 8 lines
Quoted 4 lines
Quoted 1 line
3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
I got you a liburing example
Right, but that's just the nop thing, which isn't really interesting outside of doing basic verification of yes indeed the kernel side works.
Quoted 7 lines
but need to sync with the selftest. I think I'll rather keep the rest into a separate repository instead of adding all helpers to liburing, which will inevitably do similar things but deviating in API and internal details. And it's simpler on dependency management instead of requiring libbpf / etc. for liburing. I wanted a space for misc
io_uringbits that doesn't make sense to keep as core liburing anyway.
Quoted 5 lines
liburing should have support and documentation. It's not that hard to check for libbpf in configure and either build the support or not. Once various feature support ends up being fragmented in the ecosystem THAT is a mess for users, I'd much rather have it consolidated and deal with it on the liburing side.
What kind of support do you mean? Installation, removal, other BPF management is all on the libbpf side, e.g. skeleton open/load/etc., and with it generating new structures for each of them, I'm not sure how to make it into some generic API whether it's liburing or not instead of making users to fill in the gaps, nor I think we should. As for figuring out right helpers and abstractions for BPF program writing, it'll be a gradual process, and I'd rather have it separately, it's not like I can reuse liburing for that.
Signature · 1 line
Pavel Begunkov
Quoted 44 lines · Pavel Begunkov
Quoted 36 lines · Jens Axboe
Quoted 15 lines · Pavel Begunkov
Quoted 14 lines · Jens Axboe
Quoted 13 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.Quoted 8 lines
Quoted 4 lines
Quoted 1 line
3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
I got you a liburing example
Right, but that's just the nop thing, which isn't really interesting outside of doing basic verification of yes indeed the kernel side works.
Quoted 7 lines
but need to sync with the selftest. I think I'll rather keep the rest into a separate repository instead of adding all helpers to liburing, which will inevitably do similar things but deviating in API and internal details. And it's simpler on dependency management instead of requiring libbpf / etc. for liburing. I wanted a space for misc
io_uringbits that doesn't make sense to keep as core liburing anyway.Quoted 5 lines
liburing should have support and documentation. It's not that hard to check for libbpf in configure and either build the support or not. Once various feature support ends up being fragmented in the ecosystem THAT is a mess for users, I'd much rather have it consolidated and deal with it on the liburing side.
Quoted 8 lines
What kind of support do you mean? Installation, removal, other BPF management is all on the libbpf side, e.g. skeleton open/load/etc., and with it generating new structures for each of them, I'm not sure how to make it into some generic API whether it's liburing or not instead of making users to fill in the gaps, nor I think we should. As for figuring out right helpers and abstractions for BPF program writing, it'll be a gradual process, and I'd rather have it separately, it's not like I can reuse liburing for that.
The cp and ratelimiter examples make sense to me. The 3rd one with xor kfunc is a conversation starter.
Since liburing already has "cp" in examples/
I have to agree with Jens that "cp" as bpf prog should be there as well. Spreading the examples across github and kernel tree won't work well. We learned this lesson with samples/bpf/ long ago.
Pavel Begunkov
maintainerQuoted 54 lines · Alexei Starovoitov
Quoted 44 lines · Pavel Begunkov
Quoted 36 lines · Jens Axboe
Quoted 15 lines · Pavel Begunkov
Quoted 14 lines · Jens Axboe
Quoted 13 lines · Pavel Begunkov
NOT FOR INCLUSION
Alexei asked for extra more realistic examples. This this series is based on top of v9 of the
io_uringBPF series and implemented as selftests. There could be more, but I stopped with 3 that should give an idea how it'll be used:1. A QD=1 file copy program that show cases state machine handling.
2. A BPF program rate-limiting the number of inflight
io_uringrequest. That's a good example of what users asked for before but seemed to be too niche to be plumbed into the mainio_uringpath.Quoted 8 lines
Quoted 4 lines
Quoted 1 line
3. A toy example of how BPF can interact with registered buffers.
Let's please keep examples in the liburing side, where they can be with the documentation too.
I got you a liburing example
Right, but that's just the nop thing, which isn't really interesting outside of doing basic verification of yes indeed the kernel side works.
Quoted 7 lines
but need to sync with the selftest. I think I'll rather keep the rest into a separate repository instead of adding all helpers to liburing, which will inevitably do similar things but deviating in API and internal details. And it's simpler on dependency management instead of requiring libbpf / etc. for liburing. I wanted a space for misc
io_uringbits that doesn't make sense to keep as core liburing anyway.Quoted 5 lines
liburing should have support and documentation. It's not that hard to check for libbpf in configure and either build the support or not. Once various feature support ends up being fragmented in the ecosystem THAT is a mess for users, I'd much rather have it consolidated and deal with it on the liburing side.
Quoted 8 lines
What kind of support do you mean? Installation, removal, other BPF management is all on the libbpf side, e.g. skeleton open/load/etc., and with it generating new structures for each of them, I'm not sure how to make it into some generic API whether it's liburing or not instead of making users to fill in the gaps, nor I think we should. As for figuring out right helpers and abstractions for BPF program writing, it'll be a gradual process, and I'd rather have it separately, it's not like I can reuse liburing for that.
Quoted 7 lines
The cp and ratelimiter examples make sense to me. The 3rd one with xor kfunc is a conversation starter.
Since liburing already has "cp" in examples/
I have to agree with Jens that "cp" as bpf prog should be there as well. Spreading the examples across github and kernel tree won't work well. We learned this lesson withsamples/bpf/long ago.
Urgh, that's the reason why I don't want these 3 to be merged. Neither I was excited about wiring any examples as selftests, but you asked for something that can be posted together a while back, and I obliged. Maybe there is some misunderstanding? and if nobody needs these selftests, I'd rather drop them entirely.
Ok, I'll copy paste the cp example to liburing. And there is no BPF support I can add to liburing as it's all mandated by libbpf. But I hope nobody says that I can't use io_uring outside of liburing and anything and everything should be shackled to liburing, there are enough examples showing the opposite.
Signature · 1 line
Pavel Begunkov