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). Signed-off-by: Pavel Begunkov --- 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 diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c index 1ffe7ba73b89..51c239c7cb34 100644 --- a/io_uring/bpf-ops.c +++ b/io_uring/bpf-ops.c @@ -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 = { diff --git a/tools/testing/selftests/io_uring/Makefile b/tools/testing/selftests/io_uring/Makefile index e0581f96d98a..3b139964f537 100644 --- a/tools/testing/selftests/io_uring/Makefile +++ b/tools/testing/selftests/io_uring/Makefile @@ -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 diff --git a/tools/testing/selftests/io_uring/common-defs.h b/tools/testing/selftests/io_uring/common-defs.h index dae3b0fe8588..ee6c36eae426 100644 --- a/tools/testing/selftests/io_uring/common-defs.h +++ b/tools/testing/selftests/io_uring/common-defs.h @@ -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 */ diff --git a/tools/testing/selftests/io_uring/xor.bpf.c b/tools/testing/selftests/io_uring/xor.bpf.c new file mode 100644 index 000000000000..c7c3b46ca1bf --- /dev/null +++ b/tools/testing/selftests/io_uring/xor.bpf.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#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, +}; diff --git a/tools/testing/selftests/io_uring/xor.c b/tools/testing/selftests/io_uring/xor.c new file mode 100644 index 000000000000..c2e7d6af2c92 --- /dev/null +++ b/tools/testing/selftests/io_uring/xor.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "helpers.h" +#include "xor.bpf.skel.h" + +int main() +{ + struct xor_state *xs; + struct bpf_link *link; + struct xor *skel; + struct ring_ctx ctx; + __u8 *buffer; + size_t buf_size = 512; + struct iovec iov; + __u8 xorv = 0; + int ret, i; + + ring_ctx_create(&ctx, sizeof(*xs)); + + skel = xor__open(); + if (!skel) { + fprintf(stderr, "can't generate skeleton\n"); + exit(1); + } + skel->struct_ops.xor_ops->ring_fd = ctx.ring.ring_fd; + skel->rodata->ri = ctx.ri; + + ret = xor__load(skel); + if (ret) { + fprintf(stderr, "failed to load skeleton\n"); + exit(1); + } + link = bpf_map__attach_struct_ops(skel->maps.xor_ops); + if (!link) { + fprintf(stderr, "failed to attach ops\n"); + return 1; + } + + + buffer = aligned_alloc(4096, buf_size); + if (!buffer) { + fprintf(stderr, "allocation failed\n"); + return 1; + } + + srand((unsigned)time(NULL)); + for (i = 0; i < buf_size; i++) { + buffer[i] = rand() % 256; + xorv ^= buffer[i]; + } + + iov.iov_len = buf_size; + iov.iov_base = buffer; + ret = io_uring_register_buffers(&ctx.ring, &iov, 1); + if (ret < 0) { + fprintf(stderr, "can't register buffer\n"); + return 1; + } + + xs = ctx.region; + xs->regbuf_idx = 0; + xs->xor_size = buf_size; + ring_ctx_run(&ctx); + + if (xs->xor_res != xorv) { + fprintf(stderr, "invalid result, %i %i\n", xorv, xs->xor_res); + return 1; + } + + bpf_link__destroy(link); + xor__destroy(skel); + ring_ctx_destroy(&ctx); + return 0; +} -- 2.52.0