From: Ming Lei <ming.lei@redhat.com>
To: Pavel Begunkov <asml.silence@gmail.com>
Cc: io-uring@vger.kernel.org, axboe@kernel.dk,
Martin KaFai Lau <martin.lau@linux.dev>,
bpf@vger.kernel.org,
Alexei Starovoitov <alexei.starovoitov@gmail.com>,
Andrii Nakryiko <andrii@kernel.org>
Subject: Re: [PATCH v3 10/10] selftests/io_uring: add bpf io_uring selftests
Date: Fri, 14 Nov 2025 21:08:52 +0800 [thread overview]
Message-ID: <aRcp5Gi41i-g64ov@fedora> (raw)
In-Reply-To: <6143e4393c645c539fc34dc37eeb6d682ad073b9.1763031077.git.asml.silence@gmail.com>
On Thu, Nov 13, 2025 at 11:59:47AM +0000, Pavel Begunkov wrote:
> Add a io_uring bpf selftest/example. runner.c sets up a ring and BPF and
> calls io_uring_enter syscall to run the BPF program. All the execution
> logic is in basic.bpf.c, which creates a request, waits for its
> completion and repeats it N=10 times, after which it terminates. The
> makefile is borrowed from sched_ext.
>
> Note, it doesn't need to be all in BPF and can be intermingled with
> userspace code. This needs a separate example.
>
> Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
> ---
> tools/testing/selftests/Makefile | 3 +-
> tools/testing/selftests/io_uring/Makefile | 164 +++++++++++++++++++
> tools/testing/selftests/io_uring/basic.bpf.c | 81 +++++++++
> tools/testing/selftests/io_uring/common.h | 2 +
> tools/testing/selftests/io_uring/runner.c | 80 +++++++++
> tools/testing/selftests/io_uring/types.bpf.h | 136 +++++++++++++++
> 6 files changed, 465 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/io_uring/Makefile
> create mode 100644 tools/testing/selftests/io_uring/basic.bpf.c
> create mode 100644 tools/testing/selftests/io_uring/common.h
> create mode 100644 tools/testing/selftests/io_uring/runner.c
> create mode 100644 tools/testing/selftests/io_uring/types.bpf.h
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index c46ebdb9b8ef..31dd369a7154 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -129,6 +129,7 @@ TARGETS += vfio
> TARGETS += x86
> TARGETS += x86/bugs
> TARGETS += zram
> +TARGETS += io_uring
> #Please keep the TARGETS list alphabetically sorted
> # Run "make quicktest=1 run_tests" or
> # "make quicktest=1 kselftest" from top level Makefile
> @@ -146,7 +147,7 @@ endif
> # User can optionally provide a TARGETS skiplist. By default we skip
> # targets using BPF since it has cutting edge build time dependencies
> # which require more effort to install.
> -SKIP_TARGETS ?= bpf sched_ext
> +SKIP_TARGETS ?= bpf sched_ext io_uring
> ifneq ($(SKIP_TARGETS),)
> TMP := $(filter-out $(SKIP_TARGETS), $(TARGETS))
> override TARGETS := $(TMP)
> diff --git a/tools/testing/selftests/io_uring/Makefile b/tools/testing/selftests/io_uring/Makefile
> new file mode 100644
> index 000000000000..7dfba422e5a6
> --- /dev/null
> +++ b/tools/testing/selftests/io_uring/Makefile
> @@ -0,0 +1,164 @@
> +# SPDX-License-Identifier: GPL-2.0
> +include ../../../build/Build.include
> +include ../../../scripts/Makefile.arch
> +include ../../../scripts/Makefile.include
> +
> +TEST_GEN_PROGS := runner
> +
> +# override lib.mk's default rules
> +OVERRIDE_TARGETS := 1
> +include ../lib.mk
> +
> +CURDIR := $(abspath .)
> +REPOROOT := $(abspath ../../../..)
> +TOOLSDIR := $(REPOROOT)/tools
> +LIBDIR := $(TOOLSDIR)/lib
> +BPFDIR := $(LIBDIR)/bpf
> +TOOLSINCDIR := $(TOOLSDIR)/include
> +BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
> +APIDIR := $(TOOLSINCDIR)/uapi
> +GENDIR := $(REPOROOT)/include/generated
> +GENHDR := $(GENDIR)/autoconf.h
> +
> +OUTPUT_DIR := $(OUTPUT)/build
> +OBJ_DIR := $(OUTPUT_DIR)/obj
> +INCLUDE_DIR := $(OUTPUT_DIR)/include
> +BPFOBJ_DIR := $(OBJ_DIR)/libbpf
> +IOUOBJ_DIR := $(OBJ_DIR)/io_uring
> +LIBBPF_OUTPUT := $(OBJ_DIR)/libbpf/libbpf.a
> +BPFOBJ := $(BPFOBJ_DIR)/libbpf.a
> +
> +DEFAULT_BPFTOOL := $(OUTPUT_DIR)/host/sbin/bpftool
> +HOST_OBJ_DIR := $(OBJ_DIR)/host/bpftool
> +HOST_LIBBPF_OUTPUT := $(OBJ_DIR)/host/libbpf/
> +HOST_LIBBPF_DESTDIR := $(OUTPUT_DIR)/host/
> +HOST_DESTDIR := $(OUTPUT_DIR)/host/
> +
> +VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
> + $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
> + ../../../../vmlinux \
> + /sys/kernel/btf/vmlinux \
> + /boot/vmlinux-$(shell uname -r)
> +VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
> +ifeq ($(VMLINUX_BTF),)
> +$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
> +endif
> +
> +BPFTOOL ?= $(DEFAULT_BPFTOOL)
> +
> +ifneq ($(wildcard $(GENHDR)),)
> + GENFLAGS := -DHAVE_GENHDR
> +endif
> +
> +CFLAGS += -g -O2 -rdynamic -pthread -Wall -Werror $(GENFLAGS) \
> + -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) \
> + -I$(TOOLSINCDIR) -I$(APIDIR) -I$(CURDIR)/include
> +
> +# Silence some warnings when compiled with clang
> +ifneq ($(LLVM),)
> +CFLAGS += -Wno-unused-command-line-argument
> +endif
> +
> +LDFLAGS = -lelf -lz -lpthread -lzstd
> +
> +IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
> + grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
> +
> +# Get Clang's default includes on this system, as opposed to those seen by
> +# '-target bpf'. This fixes "missing" files on some architectures/distros,
> +# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
> +#
> +# Use '-idirafter': Don't interfere with include mechanics except where the
> +# build would have failed anyways.
> +define get_sys_includes
> +$(shell $(1) $(2) -v -E - </dev/null 2>&1 \
> + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
> +$(shell $(1) $(2) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}')
> +endef
> +
> +ifneq ($(CROSS_COMPILE),)
> +CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
> +endif
> +
> +CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))
> +
> +BPF_CFLAGS = -g -D__TARGET_ARCH_$(SRCARCH) \
> + $(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian) \
> + -I$(CURDIR)/include -I$(CURDIR)/include/bpf-compat \
> + -I$(INCLUDE_DIR) -I$(APIDIR) \
> + -I$(REPOROOT)/include \
> + $(CLANG_SYS_INCLUDES) \
> + -Wall -Wno-compare-distinct-pointer-types \
> + -Wno-incompatible-function-pointer-types \
> + -O2 -mcpu=v3
> +
> +# sort removes libbpf duplicates when not cross-building
> +MAKE_DIRS := $(sort $(OBJ_DIR)/libbpf $(OBJ_DIR)/libbpf \
> + $(OBJ_DIR)/bpftool $(OBJ_DIR)/resolve_btfids \
> + $(HOST_OBJ_DIR) $(INCLUDE_DIR) $(IOUOBJ_DIR))
> +
> +$(MAKE_DIRS):
> + $(call msg,MKDIR,,$@)
> + $(Q)mkdir -p $@
> +
> +$(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \
> + $(APIDIR)/linux/bpf.h \
> + | $(OBJ_DIR)/libbpf
> + $(Q)$(MAKE) $(submake_extras) -C $(BPFDIR) OUTPUT=$(OBJ_DIR)/libbpf/ \
> + ARCH=$(ARCH) CC="$(CC)" CROSS_COMPILE=$(CROSS_COMPILE) \
> + EXTRA_CFLAGS='-g -O0 -fPIC' \
> + DESTDIR=$(OUTPUT_DIR) prefix= all install_headers
> +
> +$(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \
> + $(LIBBPF_OUTPUT) | $(HOST_OBJ_DIR)
> + $(Q)$(MAKE) $(submake_extras) -C $(BPFTOOLDIR) \
> + ARCH= CROSS_COMPILE= CC=$(HOSTCC) LD=$(HOSTLD) \
> + EXTRA_CFLAGS='-g -O0' \
> + OUTPUT=$(HOST_OBJ_DIR)/ \
> + LIBBPF_OUTPUT=$(HOST_LIBBPF_OUTPUT) \
> + LIBBPF_DESTDIR=$(HOST_LIBBPF_DESTDIR) \
> + prefix= DESTDIR=$(HOST_DESTDIR) install-bin
> +
> +$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
> +ifeq ($(VMLINUX_H),)
> + $(call msg,GEN,,$@)
> + $(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
> +else
> + $(call msg,CP,,$@)
> + $(Q)cp "$(VMLINUX_H)" $@
> +endif
> +
> +$(IOUOBJ_DIR)/%.bpf.o: %.bpf.c $(INCLUDE_DIR)/vmlinux.h | $(BPFOBJ) $(IOUOBJ_DIR)
> + $(call msg,CLNG-BPF,,$(notdir $@))
> + $(Q)$(CLANG) $(BPF_CFLAGS) -target bpf -c $< -o $@
> +
> +$(INCLUDE_DIR)/%.bpf.skel.h: $(IOUOBJ_DIR)/%.bpf.o $(INCLUDE_DIR)/vmlinux.h $(BPFTOOL) | $(INCLUDE_DIR)
> + $(eval sched=$(notdir $@))
> + $(call msg,GEN-SKEL,,$(sched))
> + $(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $<
> + $(Q)$(BPFTOOL) gen object $(<:.o=.linked2.o) $(<:.o=.linked1.o)
> + $(Q)$(BPFTOOL) gen object $(<:.o=.linked3.o) $(<:.o=.linked2.o)
> + $(Q)diff $(<:.o=.linked2.o) $(<:.o=.linked3.o)
> + $(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked3.o) name $(subst .bpf.skel.h,,$(sched)) > $@
> + $(Q)$(BPFTOOL) gen subskeleton $(<:.o=.linked3.o) name $(subst .bpf.skel.h,,$(sched)) > $(@:.skel.h=.subskel.h)
> +
> +override define CLEAN
> + rm -rf $(OUTPUT_DIR)
> + rm -f $(TEST_GEN_PROGS)
> +endef
> +
> +all_test_bpfprogs := $(foreach prog,$(wildcard *.bpf.c),$(INCLUDE_DIR)/$(patsubst %.c,%.skel.h,$(prog)))
> +
> +$(IOUOBJ_DIR)/runner.o: runner.c $(all_test_bpfprogs) | $(IOUOBJ_DIR) $(BPFOBJ)
> + $(CC) $(CFLAGS) -c $< -o $@
> +
> +$(OUTPUT)/runner: $(IOUOBJ_DIR)/runner.o $(BPFOBJ)
> + @echo "$(testcase-targets)"
> + echo 111
> + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
> +
> +.DEFAULT_GOAL := all
> +
> +.DELETE_ON_ERROR:
> +
> +.SECONDARY:
> diff --git a/tools/testing/selftests/io_uring/basic.bpf.c b/tools/testing/selftests/io_uring/basic.bpf.c
> new file mode 100644
> index 000000000000..c7954146ae4d
> --- /dev/null
> +++ b/tools/testing/selftests/io_uring/basic.bpf.c
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/types.h>
> +#include <linux/stddef.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include "types.bpf.h"
> +#include "common.h"
> +
> +extern int bpf_io_uring_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr) __ksym;
> +extern __u8 *bpf_io_uring_get_region(struct io_ring_ctx *ctx, __u32 region_id,
> + const __u64 rdwr_buf_size) __ksym;
> +
> +static inline void io_bpf_wait_nr(struct io_ring_ctx *ring,
> + struct iou_loop_state *ls, int nr)
> +{
> + ls->cq_tail = ring->rings->cq.head + nr;
> +}
> +
> +enum {
> + RINGS_REGION_ID = 0,
> + SQ_REGION_ID = 1,
> +};
> +
> +char LICENSE[] SEC("license") = "Dual BSD/GPL";
> +int reqs_to_run;
> +
> +SEC("struct_ops.s/link_loop")
> +int BPF_PROG(link_loop, struct io_ring_ctx *ring, struct iou_loop_state *ls)
> +{
> + struct ring_hdr *sq_hdr, *cq_hdr;
> + struct io_uring_cqe *cqe, *cqes;
> + struct io_uring_sqe *sqes, *sqe;
> + void *rings;
> + int ret;
> +
> + sqes = (void *)bpf_io_uring_get_region(ring, SQ_REGION_ID,
> + SQ_ENTRIES * sizeof(struct io_uring_sqe));
> + rings = (void *)bpf_io_uring_get_region(ring, RINGS_REGION_ID,
> + 64 + CQ_ENTRIES * sizeof(struct io_uring_cqe));
> + if (!rings || !sqes) {
> + bpf_printk("error: can't get regions");
> + return IOU_LOOP_STOP;
> + }
> +
> + sq_hdr = rings;
> + cq_hdr = sq_hdr + 1;
> + cqes = rings + 64;
> +
> + if (cq_hdr->tail != cq_hdr->head) {
> + unsigned cq_mask = CQ_ENTRIES - 1;
> +
> + cqe = &cqes[cq_hdr->head++ & cq_mask];
> + bpf_printk("found cqe: data %lu res %i",
> + (unsigned long)cqe->user_data, (int)cqe->res);
> +
> + int left = --reqs_to_run;
> + if (left <= 0) {
> + bpf_printk("finished");
> + return IOU_LOOP_STOP;
> + }
> + }
> +
> + bpf_printk("queue nop request, data %lu\n", (unsigned long)reqs_to_run);
> + sqe = &sqes[sq_hdr->tail & (SQ_ENTRIES - 1)];
> + sqe->user_data = reqs_to_run;
> + sq_hdr->tail++;
Looks this way turns io_uring_enter() into pthread-unsafe, does it need to
be documented?
Thanks,
Ming
next prev parent reply other threads:[~2025-11-14 13:09 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-13 11:59 [PATCH v3 00/10] BPF controlled io_uring Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 01/10] io_uring: rename the wait queue entry field Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 02/10] io_uring: simplify io_cqring_wait_schedule results Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 03/10] io_uring: export __io_run_local_work Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 04/10] io_uring: extract waiting parameters into a struct Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 05/10] io_uring/bpf: add stubs for bpf struct_ops Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 06/10] io_uring/bpf: add handle events callback Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 07/10] io_uring/bpf: implement struct_ops registration Pavel Begunkov
2025-11-24 3:44 ` Ming Lei
2025-11-24 13:12 ` Pavel Begunkov
2025-11-24 14:29 ` Ming Lei
2025-11-25 12:46 ` Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 08/10] io_uring/bpf: add basic kfunc helpers Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 09/10] selftests/io_uring: update mini liburing Pavel Begunkov
2025-11-13 11:59 ` [PATCH v3 10/10] selftests/io_uring: add bpf io_uring selftests Pavel Begunkov
2025-11-14 13:08 ` Ming Lei [this message]
2025-11-19 19:00 ` Pavel Begunkov
2025-11-20 1:41 ` Ming Lei
2025-11-21 16:12 ` Pavel Begunkov
2025-11-22 0:19 ` Ming Lei
2025-11-24 11:57 ` Pavel Begunkov
2025-11-24 13:28 ` Ming Lei
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=aRcp5Gi41i-g64ov@fedora \
--to=ming.lei@redhat.com \
--cc=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=asml.silence@gmail.com \
--cc=axboe@kernel.dk \
--cc=bpf@vger.kernel.org \
--cc=io-uring@vger.kernel.org \
--cc=martin.lau@linux.dev \
/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