Skip to contentSkip to conversation

extra io_uring BPF examples

PATCH In review Pavel Begunkov 3 participants 10 messages Feb 23 – Feb 25

Subsystems (4)
lore
Share & tools
lore

Pavel Begunkov

author maintainer
#0 lore raw

cover 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
author maintainer
#1 lore raw

PATCH 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
index 37f50acdba37..26e4cf721b86 100644
@@ -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
index 9a44e0687436..20b59adbe703 100644
@@ -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
author maintainer
#2 lore raw

PATCH 2/3 io_uring/selftests: add rate limiter BPF program

In reply to Pavel Begunkov

Show 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
index 26e4cf721b86..e0581f96d98a 100644
@@ -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
index 20b59adbe703..dae3b0fe8588 100644
@@ -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
index b6d1b8ca64b8..6894e081e3c0 100644
@@ -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(&params, 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, &params);
 	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
author maintainer
#3 lore raw

PATCH 3/3 io_uring/selftests: add regbuf xor example

In reply to Pavel Begunkov

Add 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
index 1ffe7ba73b89..51c239c7cb34 100644
@@ -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
index e0581f96d98a..3b139964f537 100644
@@ -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
index dae3b0fe8588..ee6c36eae426 100644
@@ -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
new file mode 100644
index 000000000000..c7c3b46ca1bf
@@ -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
maintainer
#4 lore raw
In reply to Pavel Begunkov
Quoted 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_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:

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_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.

Let's please keep examples in the liburing side, where they can be with the documentation too.

Signature · 1 line
Jens Axboe
author maintainer
#5 lore raw
Quoted 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_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.

Quoted 8 lines
Quoted 5 lines

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.

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
maintainer
#6 lore raw
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_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.

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

https://github.com/isilence/liburing/tree/bpf-ops-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_uring bits 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
author maintainer
#7 lore raw
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_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.

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

https://github.com/isilence/liburing/tree/bpf-ops-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_uring bits 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
#8 lore raw
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_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.

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

https://github.com/isilence/liburing/tree/bpf-ops-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_uring bits 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.

author maintainer
#9 lore raw
Quoted 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_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.

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

https://github.com/isilence/liburing/tree/bpf-ops-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_uring bits 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 with samples/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