* [PATCH bpf-next v7 01/11] selftests/bpf: Add BPF_STRICT_BUILD toggle
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 02/11] selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels Ricardo B. Marlière
` (9 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
Add BPF_STRICT_BUILD (default 1) to control whether BPF compilation
failures are fatal. When set to 0, failures are tolerated and skipped
objects are reported, as implemented by subsequent patches in this
series. When set to 1 (the default), the build fails on any error,
preserving the existing behavior for CI and direct builds.
Users can opt in to permissive mode on the command line:
make -C tools/testing/selftests \
TARGETS=bpf SKIP_TARGETS= BPF_STRICT_BUILD=0
Suggested-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 78e60040811e..c8ba8ccc2c6d 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -44,6 +44,12 @@ SKIP_LLVM ?=
SKIP_LIBBFD ?=
SKIP_CRYPTO ?=
+# When BPF_STRICT_BUILD is 1, any BPF object, skeleton, test object, or
+# benchmark compilation failure is fatal. Set to 0 to tolerate failures
+# and continue building the remaining tests.
+BPF_STRICT_BUILD ?= 1
+PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))
+
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 02/11] selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 01/11] selftests/bpf: Add BPF_STRICT_BUILD toggle Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:52 ` sashiko-bot
2026-04-16 19:30 ` [PATCH bpf-next v7 03/11] selftests/bpf: Tolerate BPF and skeleton generation failures Ricardo B. Marlière
` (8 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
test_kmods/Makefile always pointed KDIR at the kernel source tree root,
ignoring O= and KBUILD_OUTPUT. On distro kernels where the source tree has
not been built, the Makefile had no fallback and would fail
unconditionally.
When O= or KBUILD_OUTPUT is set, pass it through so kbuild can locate the
correct kernel build directory (Module.symvers, scripts, etc.). Note that
the module artifacts themselves still land in the M= directory, which is
test_kmods/; O= only controls where kbuild finds its build infrastructure.
Fall back to /lib/modules/$(uname -r)/build when neither an explicit build
directory nor an in-tree Module.symvers is present.
Guard both all and clean against a missing KDIR so the step is silently
skipped rather than fatal. Make the parent Makefile's cp conditional so it
does not abort when modules were not built.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 4 ++--
tools/testing/selftests/bpf/test_kmods/Makefile | 21 ++++++++++++++++++---
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index c8ba8ccc2c6d..52176dc226f1 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -296,7 +296,7 @@ $(addprefix test_kmods/,$(subst .ko,%ko,$(TEST_KMODS))): $(VMLINUX_BTF) $(RESOLV
$(TEST_KMOD_TARGETS): $(addprefix test_kmods/,$(TEST_KMODS))
$(call msg,MOD,,$@)
- $(Q)cp test_kmods/$(@F) $@
+ $(Q)$(if $(PERMISSIVE),if [ -f test_kmods/$(@F) ]; then )cp test_kmods/$(@F) $@$(if $(PERMISSIVE),; fi)
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
@@ -711,7 +711,7 @@ $(TRUNNER_LIB_OBJS): $(TRUNNER_OUTPUT)/%.o:$(TOOLSDIR)/lib/%.c
$(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT)
ifneq ($2:$(OUTPUT),:$(shell pwd))
$$(call msg,EXT-COPY,$(TRUNNER_BINARY),$(TRUNNER_EXTRA_FILES))
- $(Q)rsync -aq $$^ $(TRUNNER_OUTPUT)/
+ $(Q)rsync -aq $(if $(PERMISSIVE),--ignore-missing-args) $$^ $(TRUNNER_OUTPUT)/
endif
# some X.test.o files have runtime dependencies on Y.bpf.o files
diff --git a/tools/testing/selftests/bpf/test_kmods/Makefile b/tools/testing/selftests/bpf/test_kmods/Makefile
index 63c4d3f6a12f..ba6f6ed6ab8c 100644
--- a/tools/testing/selftests/bpf/test_kmods/Makefile
+++ b/tools/testing/selftests/bpf/test_kmods/Makefile
@@ -1,5 +1,7 @@
TEST_KMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
-KDIR ?= $(abspath $(TEST_KMOD_DIR)/../../../../..)
+SRCTREE_KDIR := $(abspath $(TEST_KMOD_DIR)/../../../../..)
+KMOD_O := $(abspath $(or $(O),$(KBUILD_OUTPUT)))
+KDIR ?= $(if $(KMOD_O),$(SRCTREE_KDIR),$(if $(wildcard $(SRCTREE_KDIR)/Module.symvers),$(SRCTREE_KDIR),/lib/modules/$(shell uname -r)/build))
ifeq ($(V),1)
Q =
@@ -14,8 +16,21 @@ $(foreach m,$(MODULES),$(eval obj-m += $(m:.ko=.o)))
CFLAGS_bpf_testmod.o = -I$(src)
+# When BPF_STRICT_BUILD != 0, a missing KDIR is fatal (the default).
+# When permissive, skip silently.
+PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))
+
all:
- $(Q)$(MAKE) -C $(KDIR) M=$(TEST_KMOD_DIR) modules
+ifeq ($(PERMISSIVE),)
+ $(Q)$(MAKE) -C $(KDIR) $(if $(KMOD_O),O=$(KMOD_O),KBUILD_OUTPUT=) \
+ M=$(TEST_KMOD_DIR) modules
+else ifneq ("$(wildcard $(KDIR))", "")
+ $(Q)$(MAKE) -C $(KDIR) $(if $(KMOD_O),O=$(KMOD_O),KBUILD_OUTPUT=) \
+ M=$(TEST_KMOD_DIR) modules
+endif
clean:
- $(Q)$(MAKE) -C $(KDIR) M=$(TEST_KMOD_DIR) clean
+ifneq ("$(wildcard $(KDIR))", "")
+ $(Q)$(MAKE) -C $(KDIR) $(if $(KMOD_O),O=$(KMOD_O),KBUILD_OUTPUT=) \
+ M=$(TEST_KMOD_DIR) clean
+endif
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH bpf-next v7 02/11] selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels
2026-04-16 19:30 ` [PATCH bpf-next v7 02/11] selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels Ricardo B. Marlière
@ 2026-04-16 19:52 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-04-16 19:52 UTC (permalink / raw)
To: Ricardo B. Marlière; +Cc: bpf
Sashiko AI review found 2 potential issue(s):
- [High] Overloading of O= incorrectly aliases the selftest output directory to the kernel build directory.
- [Medium] Relative paths passed to O= are incorrectly resolved against the sub-make's working directory.
--
commit 7a429745e6e5a3d1081ae76477bd32a034698ba8
Author: Ricardo B. Marlière <rbm@suse.com>
selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels
This commit modifies the test_kmods Makefile to respect the O= and
KBUILD_OUTPUT variables for locating the correct kernel build directory.
It falls back to /lib/modules/$(uname -r)/build when neither an explicit
build directory nor an in-tree Module.symvers is present.
> diff --git a/tools/testing/selftests/bpf/test_kmods/Makefile b/tools/testing/selftests/bpf/test_kmods/Makefile
> index 63c4d3f6a12f6..ba6f6ed6ab8c0 100644
> --- a/tools/testing/selftests/bpf/test_kmods/Makefile
> +++ b/tools/testing/selftests/bpf/test_kmods/Makefile
> @@ -1,5 +1,7 @@
> TEST_KMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
> -KDIR ?= $(abspath $(TEST_KMOD_DIR)/../../../../..)
> +SRCTREE_KDIR := $(abspath $(TEST_KMOD_DIR)/../../../../..)
> +KMOD_O := $(abspath $(or $(O),$(KBUILD_OUTPUT)))
If a user specifies a relative path like "make O=out", will this resolve
incorrectly against the sub-make's current working directory instead of the
parent directory?
Since abspath is evaluated inside the test_kmods sub-make, a relative
O=out would evaluate to .../test_kmods/out instead of .../bpf/out,
potentially breaking out-of-tree builds. Does the path need to be
resolved to an absolute path before the sub-make transition?
Additionally, when building selftests out-of-tree via
"make O=/tmp/selftests -C tools/testing/selftests", O= strictly defines the
selftests output directory.
Since O takes precedence in $(or $(O),$(KBUILD_OUTPUT)), will it shadow a
valid KBUILD_OUTPUT? This could cause kbuild to fail due to a missing
.config or Module.symvers in the selftests directory.
Could the Makefile differentiate the selftest output directory from the
kernel build directory, or verify that $(KMOD_O) actually contains a
prepared kernel build before forcing kbuild to use it?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260416-selftests-bpf_misconfig-v7-0-a078e18012e4@suse.com?part=2
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH bpf-next v7 03/11] selftests/bpf: Tolerate BPF and skeleton generation failures
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 01/11] selftests/bpf: Add BPF_STRICT_BUILD toggle Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 02/11] selftests/bpf: Fix test_kmods KDIR to honor O= and distro kernels Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 04/11] selftests/bpf: Avoid rebuilds when running emit_tests Ricardo B. Marlière
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
Some BPF programs cannot be built on distro kernels because required BTF
types or features are missing. A single failure currently aborts the
selftests/bpf build.
Make BPF object and skeleton generation best effort: emit SKIP-BPF or
SKIP-SKEL, remove failed outputs so downstream rules can detect absence,
and continue with remaining tests. Apply the same tolerance to linked
skeletons (TRUNNER_BPF_SKELS_LINKED), which depend on multiple .bpf.o files
and would otherwise abort the build when any dependency is missing.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 108 +++++++++++++++++++++++------------
1 file changed, 73 insertions(+), 35 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 52176dc226f1..b23703f2cf89 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -477,22 +477,26 @@ $(OUTPUT)/cgroup_getset_retval_hooks.o: cgroup_getset_retval_hooks.h
# $4 - binary name
define CLANG_BPF_BUILD_RULE
$(call msg,CLNG-BPF,$4,$2)
- $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v3 -o $2
+ $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v3 -o $2 $(if $(PERMISSIVE),|| \
+ ($(RM) $2; printf ' %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
endef
# Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32
define CLANG_NOALU32_BPF_BUILD_RULE
$(call msg,CLNG-BPF,$4,$2)
- $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v2 -o $2
+ $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v2 -o $2 $(if $(PERMISSIVE),|| \
+ ($(RM) $2; printf ' %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
endef
# Similar to CLANG_BPF_BUILD_RULE, but with cpu-v4
define CLANG_CPUV4_BPF_BUILD_RULE
$(call msg,CLNG-BPF,$4,$2)
- $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v4 -o $2
+ $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v4 -o $2 $(if $(PERMISSIVE),|| \
+ ($(RM) $2; printf ' %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
endef
# Build BPF object using GCC
define GCC_BPF_BUILD_RULE
$(call msg,GCC-BPF,$4,$2)
- $(Q)$(BPF_GCC) $3 -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $1 -o $2
+ $(Q)$(BPF_GCC) $3 -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $1 -o $2 $(if $(PERMISSIVE),|| \
+ ($(RM) $2; printf ' %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
endef
SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
@@ -598,47 +602,81 @@ $(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.bpf.o: \
$$($$<-$2-CFLAGS),$(TRUNNER_BINARY))
$(TRUNNER_BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
- $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@)
- $(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 $$(notdir $$(<:.bpf.o=)) > $$@
- $(Q)$$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.bpf.o=)) > $$(@:.skel.h=.subskel.h)
- $(Q)rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)
+ $(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \
+ $$(RM) $$@ $$(@:.skel.h=.subskel.h); \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ exit 0; \
+ fi;) \
+ printf ' %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2; \
+ $$(BPFTOOL) gen object $$(<:.o=.linked1.o) $$< && \
+ $$(BPFTOOL) gen object $$(<:.o=.linked2.o) $$(<:.o=.linked1.o) && \
+ $$(BPFTOOL) gen object $$(<:.o=.linked3.o) $$(<:.o=.linked2.o) && \
+ diff $$(<:.o=.linked2.o) $$(<:.o=.linked3.o) && \
+ $$(BPFTOOL) gen skeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.bpf.o=)) > $$@ && \
+ $$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.bpf.o=)) > $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \
+ $$(RM) $$@ $$(@:.skel.h=.subskel.h); \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ }) && \
+ rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)
$(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
- $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@)
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$<
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o)
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o)
- $(Q)diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
- $(Q)$$(BPFTOOL) gen skeleton -L $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
- $(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
+ $(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \
+ $$(RM) $$@; \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ exit 0; \
+ fi;) \
+ printf ' %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2; \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$< && \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o) && \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o) && \
+ diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) && \
+ $$(BPFTOOL) gen skeleton -L $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ $(if $(PERMISSIVE),|| { \
+ $$(RM) $$@; \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ }) && \
+ rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
- $$(call msg,GEN-SKEL,$(TRUNNER_BINARY) (signed),$$@)
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$<
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o)
- $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o)
- $(Q)diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
- $(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
- $(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
+ $(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \
+ $$(RM) $$@; \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ exit 0; \
+ fi;) \
+ printf ' %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY) (signed)] $$(notdir $$@)' 1>&2; \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$< && \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o) && \
+ $$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o) && \
+ diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) && \
+ $$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ $(if $(PERMISSIVE),|| { \
+ $$(RM) $$@; \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ }) && \
+ rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
$(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/%
# .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites
.SECONDEXPANSION:
$(TRUNNER_BPF_SKELS_LINKED): $(TRUNNER_OUTPUT)/%: $$$$(%-deps) $(BPFTOOL) | $(TRUNNER_OUTPUT)
- $$(call msg,LINK-BPF,$(TRUNNER_BINARY),$$(@:.skel.h=.bpf.o))
- $(Q)$$(BPFTOOL) gen object $$(@:.skel.h=.linked1.o) $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps))
- $(Q)$$(BPFTOOL) gen object $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked1.o)
- $(Q)$$(BPFTOOL) gen object $$(@:.skel.h=.linked3.o) $$(@:.skel.h=.linked2.o)
- $(Q)diff $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o)
- $$(call msg,GEN-SKEL,$(TRUNNER_BINARY),$$@)
- $(Q)$$(BPFTOOL) gen skeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$@
- $(Q)$$(BPFTOOL) gen subskeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$(@:.skel.h=.subskel.h)
- $(Q)rm -f $$(@:.skel.h=.linked1.o) $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o)
+ $(Q)$(if $(PERMISSIVE),for f in $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)); do \
+ if [ ! -f $$$$f ]; then \
+ $$(RM) $$@ $$(@:.skel.h=.subskel.h); \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ exit 0; \
+ fi; \
+ done;) \
+ printf ' %-12s %s\n' 'LINK-BPF' '[$(TRUNNER_BINARY)] $$(notdir $$(@:.skel.h=.bpf.o))' 1>&2; \
+ $$(BPFTOOL) gen object $$(@:.skel.h=.linked1.o) $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)) && \
+ $$(BPFTOOL) gen object $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked1.o) && \
+ $$(BPFTOOL) gen object $$(@:.skel.h=.linked3.o) $$(@:.skel.h=.linked2.o) && \
+ diff $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o) && \
+ printf ' %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2 && \
+ $$(BPFTOOL) gen skeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$@ && \
+ $$(BPFTOOL) gen subskeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \
+ $$(RM) $$@ $$(@:.skel.h=.subskel.h); \
+ printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
+ }) && \
+ rm -f $$(@:.skel.h=.linked1.o) $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o)
# When the compiler generates a %.d file, only skel basenames (not
# full paths) are specified as prerequisites for corresponding %.o
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 04/11] selftests/bpf: Avoid rebuilds when running emit_tests
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (2 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 03/11] selftests/bpf: Tolerate BPF and skeleton generation failures Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 05/11] selftests/bpf: Make skeleton headers order-only prerequisites of .test.d Ricardo B. Marlière
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
emit_tests is used while installing selftests to generate the kselftest
list. Pulling in .d files for this goal can trigger BPF rebuild rules and
mix build output into list generation.
Skip dependency file inclusion for emit_tests, like clean goals, so list
generation stays side-effect free. Also add emit_tests to
NON_CHECK_FEAT_TARGETS so that feature detection is skipped; without this,
Makefile.feature's $(info) output leaks into stdout and corrupts the test
list captured by the top-level selftests Makefile.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index b23703f2cf89..7d1e5d6c30ec 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -164,7 +164,7 @@ endef
include ../lib.mk
-NON_CHECK_FEAT_TARGETS := clean docs-clean
+NON_CHECK_FEAT_TARGETS := clean docs-clean emit_tests
CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS), "none"))
ifneq ($(CHECK_FEAT),)
FEATURE_USER := .selftests
@@ -723,7 +723,7 @@ $(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d: \
$(TRUNNER_BPF_SKELS_LINKED) \
$$(BPFOBJ) | $(TRUNNER_OUTPUT)
-ifeq ($(filter clean docs-clean,$(MAKECMDGOALS)),)
+ifeq ($(filter clean docs-clean emit_tests,$(MAKECMDGOALS)),)
include $(wildcard $(TRUNNER_TEST_OBJS:.o=.d))
endif
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 05/11] selftests/bpf: Make skeleton headers order-only prerequisites of .test.d
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (3 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 04/11] selftests/bpf: Avoid rebuilds when running emit_tests Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 06/11] selftests/bpf: Tolerate test file compilation failures Ricardo B. Marlière
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
The .test.d dependency files are generated by the C preprocessor and list
the headers each test file actually #includes. Skeleton headers appear in
those generated lists, so the .test.o -> .skel.h dependency is already
tracked by the .d file content.
Making skeletons order-only prerequisites of .test.d means that a missing
or skipped skeleton does not prevent .test.d generation, and regenerating a
skeleton does not force .test.d to be recreated. This avoids unnecessary
recompilation and, more importantly, avoids build errors when a skeleton
was intentionally skipped due to a BPF compilation failure.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 7d1e5d6c30ec..b3eaa4a93707 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -717,11 +717,11 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \
$(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d: \
$(TRUNNER_TESTS_DIR)/%.c \
$(TRUNNER_EXTRA_HDRS) \
+ $$(BPFOBJ) | $(TRUNNER_OUTPUT) \
$(TRUNNER_BPF_SKELS) \
$(TRUNNER_BPF_LSKELS) \
$(TRUNNER_BPF_LSKELS_SIGNED) \
- $(TRUNNER_BPF_SKELS_LINKED) \
- $$(BPFOBJ) | $(TRUNNER_OUTPUT)
+ $(TRUNNER_BPF_SKELS_LINKED)
ifeq ($(filter clean docs-clean emit_tests,$(MAKECMDGOALS)),)
include $(wildcard $(TRUNNER_TEST_OBJS:.o=.d))
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 06/11] selftests/bpf: Tolerate test file compilation failures
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (4 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 05/11] selftests/bpf: Make skeleton headers order-only prerequisites of .test.d Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:55 ` sashiko-bot
2026-04-16 19:30 ` [PATCH bpf-next v7 07/11] selftests/bpf: Skip tests whose objects were not built Ricardo B. Marlière
` (4 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
Individual test files may fail to compile when headers or kernel features
required by that test are absent. Currently this aborts the entire build.
Make the per-test compilation non-fatal: remove the output object on
failure and print a SKIP-TEST marker to stderr. Guard the BTFIDS
post-processing step so it is skipped when the object file is absent. The
linker step will later ignore absent objects, allowing the remaining tests
to build and run.
Use plain printf and $(Q) instead of $(call msg,...) in the BTFIDS block:
the msg macro expands to @printf which is a make-recipe construct and is
invalid as a shell command inside an if-then-fi body; $(Q) restores echo
suppression at the recipe level where it is meaningful.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index b3eaa4a93707..5ad50d3ff2f8 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -708,11 +708,14 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \
$(TRUNNER_TESTS_DIR)/%.c \
| $(TRUNNER_OUTPUT)/%.test.d
$$(call msg,TEST-OBJ,$(TRUNNER_BINARY),$$@)
- $(Q)cd $$(@D) && $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)
+ $(Q)cd $$(@D) && $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F) $(if $(PERMISSIVE),|| \
+ ($(RM) $$(@F); printf ' %-12s %s\n' 'SKIP-TEST' '$$(notdir $$@)' 1>&2))
$$(if $$(TEST_NEEDS_BTFIDS), \
- $$(call msg,BTFIDS,$(TRUNNER_BINARY),$$@) \
+ $(Q)if [ -f $$@ ]; then \
+ printf ' %-8s%s %s\n' "BTFIDS" " [$(TRUNNER_BINARY)]" "$$(notdir $$@)"; \
$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@; \
- $(RESOLVE_BTFIDS) --patch_btfids $$@.BTF_ids $$@)
+ $(RESOLVE_BTFIDS) --patch_btfids $$@.BTF_ids $$@; \
+ fi)
$(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d: \
$(TRUNNER_TESTS_DIR)/%.c \
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH bpf-next v7 06/11] selftests/bpf: Tolerate test file compilation failures
2026-04-16 19:30 ` [PATCH bpf-next v7 06/11] selftests/bpf: Tolerate test file compilation failures Ricardo B. Marlière
@ 2026-04-16 19:55 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-04-16 19:55 UTC (permalink / raw)
To: Ricardo B. Marlière; +Cc: bpf
Sashiko AI review found 2 potential issue(s):
- [High] Tolerating test compilation failures breaks the build at the linker step.
- [High] Omitting test objects causes the test suite runner to abort at startup.
--
commit 3b7eac5b82169326acf885f820b425d42e131d71
Author: Ricardo B. Marlière <rbm@suse.com>
selftests/bpf: Tolerate test file compilation failures
This commit modifies the selftests build system to make per-test compilation
failures non-fatal when permitted. It removes the output object on failure
and skips the post-processing step for missing objects.
> Make the per-test compilation non-fatal: remove the output object on
> failure and print a SKIP-TEST marker to stderr. Guard the BTFIDS
> post-processing step so it is skipped when the object file is absent. The
> linker step will later ignore absent objects, allowing the remaining tests
> to build and run.
Is this description accurate regarding the linker step?
During linking, the Makefile rules pass the literal list of prerequisites to
the compiler. When a compilation fails and the object file is deleted, that
missing file is still passed to the linker.
This causes a fatal "No such file or directory" error that halts the build.
I notice this is addressed later in the series by the patch "selftests/bpf:
Allow test_progs to link with a partial object set", but it causes a build
regression in this specific commit.
> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> index b3eaa4a93707c..5ad50d3ff2f8c 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile
> @@ -708,11 +708,14 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \
> $(TRUNNER_TESTS_DIR)/%.c \
> | $(TRUNNER_OUTPUT)/%.test.d
> $$(call msg,TEST-OBJ,$(TRUNNER_BINARY),$$@)
> - $(Q)cd $$(@D) && $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)
> + $(Q)cd $$(@D) && $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F) $(if $(PERMISSIVE),|| \
> + ($(RM) $$(@F); printf ' %-12s %s\n' 'SKIP-TEST' '$$(notdir $$@)' 1>&2))
How does the test suite handle the missing objects at runtime?
The test framework generates the test header by scanning the source files
directly, so a test definition is emitted even if its object file was
skipped.
Because the compiled object is missing, the weak function pointers for that
test resolve to a null pointer during linking. When the test runner validates
these pointers at startup, it prints an error and aborts the entire test
framework setup.
This prevents the remaining tests from running.
I see this is resolved later in the series by the patch "selftests/bpf: Skip
tests whose objects were not built", but it introduces a runtime regression
here.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260416-selftests-bpf_misconfig-v7-0-a078e18012e4@suse.com?part=6
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH bpf-next v7 07/11] selftests/bpf: Skip tests whose objects were not built
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (5 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 06/11] selftests/bpf: Tolerate test file compilation failures Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 08/11] selftests/bpf: Allow test_progs to link with a partial object set Ricardo B. Marlière
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
When both run_test and run_serial_test are NULL (because the corresponding
.test.o was not compiled), mark the test as not built instead of fatally
aborting.
Report these tests as "SKIP (not built)" in per-test output and include
them in the skip count so they remain visible in CI results and JSON
output. The summary line shows the not-built count when nonzero:
Summary: 50/55 PASSED, 5 SKIPPED (3 not built), 0 FAILED
Tests filtered out by -t/-n remain invisible as before; only genuinely
unbuilt tests are surfaced.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/test_progs.c | 50 +++++++++++++++++++++++++++-----
tools/testing/selftests/bpf/test_progs.h | 1 +
2 files changed, 44 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index 7fe16b5131b1..f04a579ecd4d 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -165,6 +165,8 @@ struct prog_test_def {
void (*run_test)(void);
void (*run_serial_test)(void);
bool should_run;
+ bool not_built;
+ bool selected;
bool need_cgroup_cleanup;
bool should_tmon;
};
@@ -372,6 +374,8 @@ static void print_test_result(const struct prog_test_def *test, const struct tes
fprintf(env.stdout_saved, "#%-*d %s:", TEST_NUM_WIDTH, test->test_num, test->test_name);
if (test_state->error_cnt)
fprintf(env.stdout_saved, "FAIL");
+ else if (test->not_built)
+ fprintf(env.stdout_saved, "SKIP (not built)");
else if (!skipped_cnt)
fprintf(env.stdout_saved, "OK");
else if (skipped_cnt == subtests_cnt || !subtests_cnt)
@@ -1700,8 +1704,13 @@ static void calculate_summary_and_print_errors(struct test_env *env)
if (env->json)
fclose(env->json);
- printf("Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
- succ_cnt, sub_succ_cnt, skip_cnt, fail_cnt);
+ if (env->not_built_cnt)
+ printf("Summary: %d/%d PASSED, %d SKIPPED (%d not built), %d FAILED\n",
+ succ_cnt, sub_succ_cnt, skip_cnt, env->not_built_cnt,
+ fail_cnt);
+ else
+ printf("Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
+ succ_cnt, sub_succ_cnt, skip_cnt, fail_cnt);
env->succ_cnt = succ_cnt;
env->sub_succ_cnt = sub_succ_cnt;
@@ -1772,6 +1781,19 @@ static void server_main(void)
run_one_test(i);
}
+ /* mark not-built tests as skipped */
+ for (int i = 0; i < prog_test_cnt; i++) {
+ struct prog_test_def *test = &prog_test_defs[i];
+ struct test_state *state = &test_states[i];
+
+ if (test->not_built && test->selected) {
+ state->tested = true;
+ state->skip_cnt = 1;
+ env.not_built_cnt++;
+ print_test_result(test, state);
+ }
+ }
+
/* generate summary */
fflush(stderr);
fflush(stdout);
@@ -2046,15 +2068,20 @@ int main(int argc, char **argv)
struct prog_test_def *test = &prog_test_defs[i];
test->test_num = i + 1;
- test->should_run = should_run(&env.test_selector,
- test->test_num, test->test_name);
+ test->selected = should_run(&env.test_selector,
+ test->test_num, test->test_name);
+ test->should_run = test->selected;
- if ((test->run_test == NULL && test->run_serial_test == NULL) ||
- (test->run_test != NULL && test->run_serial_test != NULL)) {
+ if (test->run_test && test->run_serial_test) {
fprintf(stderr, "Test %d:%s must have either test_%s() or serial_test_%sl() defined.\n",
test->test_num, test->test_name, test->test_name, test->test_name);
exit(EXIT_ERR_SETUP_INFRA);
}
+ if (!test->run_test && !test->run_serial_test) {
+ test->not_built = true;
+ test->should_run = false;
+ continue;
+ }
if (test->should_run)
test->should_tmon = should_tmon(&env.tmon_selector, test->test_name);
}
@@ -2106,9 +2133,18 @@ int main(int argc, char **argv)
for (i = 0; i < prog_test_cnt; i++) {
struct prog_test_def *test = &prog_test_defs[i];
+ struct test_state *state = &test_states[i];
- if (!test->should_run)
+ if (!test->should_run) {
+ if (test->not_built && test->selected &&
+ !env.get_test_cnt && !env.list_test_names) {
+ state->tested = true;
+ state->skip_cnt = 1;
+ env.not_built_cnt++;
+ print_test_result(test, state);
+ }
continue;
+ }
if (env.get_test_cnt) {
env.succ_cnt++;
diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
index 1a44467f4310..6ee10360e314 100644
--- a/tools/testing/selftests/bpf/test_progs.h
+++ b/tools/testing/selftests/bpf/test_progs.h
@@ -125,6 +125,7 @@ struct test_env {
int sub_succ_cnt; /* successful sub-tests */
int fail_cnt; /* total failed tests + sub-tests */
int skip_cnt; /* skipped tests */
+ int not_built_cnt; /* tests not built */
int saved_netns_fd;
int workers; /* number of worker process */
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 08/11] selftests/bpf: Allow test_progs to link with a partial object set
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (6 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 07/11] selftests/bpf: Skip tests whose objects were not built Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 20:05 ` sashiko-bot
2026-04-16 19:30 ` [PATCH bpf-next v7 09/11] selftests/bpf: Tolerate benchmark build failures Ricardo B. Marlière
` (2 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
When individual test files are skipped due to compilation failures, their
.test.o files are absent. The linker step currently lists all expected
.test.o files as explicit prerequisites, so make considers any missing one
an error.
Move TRUNNER_TEST_OBJS to the order-only prerequisite list so that their
absence does not prevent linking. Use $(wildcard ...) in the filter
expression passed to the linker so that only the objects that were actually
built are linked in.
Place the wildcard expansion before $^ in the filter to preserve the
original link order: test objects must precede libbpf.a so that GNU ld,
which scans static archives left-to-right, pulls in archive members that
are referenced exclusively by test objects (e.g. ring_buffer__new from
ringbuf.c).
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 5ad50d3ff2f8..a5597e8431a0 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -758,14 +758,15 @@ endif
# some X.test.o files have runtime dependencies on Y.bpf.o files
$(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS)
-$(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \
+$(OUTPUT)/$(TRUNNER_BINARY): $(if $(PERMISSIVE),,$(TRUNNER_TEST_OBJS)) \
$(TRUNNER_EXTRA_OBJS) $$(BPFOBJ) \
$(TRUNNER_LIB_OBJS) \
$(TRUNNER_BPFTOOL) \
$(OUTPUT)/veristat \
- | $(TRUNNER_BINARY)-extras
+ | $(TRUNNER_BINARY)-extras \
+ $(if $(PERMISSIVE),$(TRUNNER_TEST_OBJS))
$$(call msg,BINARY,,$$@)
- $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) $$(LLVM_LDLIBS) $$(LDFLAGS) $$(LLVM_LDFLAGS) -o $$@
+ $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$(if $(PERMISSIVE),$$(wildcard $(TRUNNER_TEST_OBJS))) $$^) $$(LDLIBS) $$(LLVM_LDLIBS) $$(LDFLAGS) $$(LLVM_LDFLAGS) -o $$@
$(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \
$(OUTPUT)/$(if $2,$2/)bpftool
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH bpf-next v7 08/11] selftests/bpf: Allow test_progs to link with a partial object set
2026-04-16 19:30 ` [PATCH bpf-next v7 08/11] selftests/bpf: Allow test_progs to link with a partial object set Ricardo B. Marlière
@ 2026-04-16 20:05 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-04-16 20:05 UTC (permalink / raw)
To: Ricardo B. Marlière; +Cc: bpf
Sashiko AI review found 1 potential issue(s):
- [Medium] Moving TRUNNER_TEST_OBJS to order-only prerequisites in permissive mode breaks incremental builds for test binaries.
--
commit bb28ef3f04df5f17cc120a9836e147154e3974e2
Author: Ricardo B. Marlière <rbm@suse.com>
selftests/bpf: Allow test_progs to link with a partial object set
This commit modifies the Makefile to allow linking of the test runner binary
even when some test object files are missing due to compilation failures. It
moves the test objects to an order-only prerequisite list and uses a wildcard
filter when permissive mode is active.
> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> index 5ad50d3ff2f8c..a5597e8431a0b 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile
[ ... ]
> @@ -758,14 +758,15 @@ endif
> # some X.test.o files have runtime dependencies on Y.bpf.o files
> $(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS)
>
> -$(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \
> +$(OUTPUT)/$(TRUNNER_BINARY): $(if $(PERMISSIVE),,$(TRUNNER_TEST_OBJS)) \
> $(TRUNNER_EXTRA_OBJS) $$(BPFOBJ) \
> $(TRUNNER_LIB_OBJS) \
> $(TRUNNER_BPFTOOL) \
> $(OUTPUT)/veristat \
> - | $(TRUNNER_BINARY)-extras
> + | $(TRUNNER_BINARY)-extras \
> + $(if $(PERMISSIVE),$(TRUNNER_TEST_OBJS))
Does moving TRUNNER_TEST_OBJS to the order-only prerequisites break
incremental builds when in permissive mode?
According to GNU Make semantics, order-only prerequisites enforce build
execution order but do not trigger a target rebuild if they are newer than
the target.
If a developer edits a test file, it looks like Make will recompile the
object file, but might consider the test runner binary up-to-date and not
relink it because the object is no longer a normal prerequisite.
Could this lead to developers silently running outdated test binaries after
modifying code?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260416-selftests-bpf_misconfig-v7-0-a078e18012e4@suse.com?part=8
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH bpf-next v7 09/11] selftests/bpf: Tolerate benchmark build failures
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (7 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 08/11] selftests/bpf: Allow test_progs to link with a partial object set Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:30 ` [PATCH bpf-next v7 10/11] selftests/bpf: Provide weak definitions for cross-test functions Ricardo B. Marlière
2026-04-16 19:31 ` [PATCH bpf-next v7 11/11] selftests/bpf: Tolerate missing files during install Ricardo B. Marlière
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
Benchmark objects depend on skeletons that may be missing when some BPF
programs fail to build. In that case, benchmark object compilation or final
bench linking should not abort the full selftests/bpf build.
Keep both steps non-fatal, emit SKIP-BENCH or SKIP-LINK, and remove failed
outputs so stale objects or binaries are not reused by later incremental
builds. Note that because bench.c statically references every benchmark via
extern symbols, partial linking is not possible: if any single benchmark
object fails, the entire bench binary is skipped. This is by design -- the
error handler catches all compilation failures including genuine ones, but
those are caught by full-config CI runs.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index a5597e8431a0..88a03366f6aa 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -896,7 +896,8 @@ $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h $(BPFOBJ)
# Benchmark runner
$(OUTPUT)/bench_%.o: benchs/bench_%.c bench.h $(BPFOBJ)
$(call msg,CC,,$@)
- $(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) $(LDLIBS) -o $@
+ $(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \
+ ($(RM) $@; printf ' %-12s %s\n' 'SKIP-BENCH' '$(notdir $@)' 1>&2))
$(OUTPUT)/bench_rename.o: $(OUTPUT)/test_overhead.skel.h
$(OUTPUT)/bench_trigger.o: $(OUTPUT)/trigger_bench.skel.h
$(OUTPUT)/bench_ringbufs.o: $(OUTPUT)/ringbuf_bench.skel.h \
@@ -939,7 +940,8 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(OUTPUT)/usdt_2.o \
#
$(call msg,BINARY,,$@)
- $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
+ $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$(if $(PERMISSIVE),$(wildcard $^),$^)) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \
+ ($(RM) $@; printf ' %-12s %s\n' 'SKIP-LINK' '$(notdir $@) (some benchmarks may have been skipped)' 1>&2))
# This works around GCC warning about snprintf truncating strings like:
#
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 10/11] selftests/bpf: Provide weak definitions for cross-test functions
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (8 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 09/11] selftests/bpf: Tolerate benchmark build failures Ricardo B. Marlière
@ 2026-04-16 19:30 ` Ricardo B. Marlière
2026-04-16 19:31 ` [PATCH bpf-next v7 11/11] selftests/bpf: Tolerate missing files during install Ricardo B. Marlière
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:30 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
Some test files reference functions defined in other translation units that
may not be compiled when skeletons are missing. Replace forward
declarations of uprobe_multi_func_{1,2,3}() with weak no-op stubs so the
linker resolves them regardless of which objects are present.
Move stack_mprotect() from test_lsm.c into testing_helpers.c so it is
always available. The previous weak-stub approach returned 0, which would
cause callers expecting -1/EPERM to fail their assertions
deterministically. Having the real implementation in a shared utility
avoids this problem entirely.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
.../testing/selftests/bpf/prog_tests/bpf_cookie.c | 17 +++++++++++------
tools/testing/selftests/bpf/prog_tests/iters.c | 3 +--
tools/testing/selftests/bpf/prog_tests/test_lsm.c | 22 ----------------------
tools/testing/selftests/bpf/testing_helpers.c | 17 +++++++++++++++++
tools/testing/selftests/bpf/testing_helpers.h | 1 +
5 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c
index 35adc3f6d443..5a864cd8ad1b 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c
@@ -252,10 +252,17 @@ static void kprobe_multi_attach_api_subtest(void)
kprobe_multi__destroy(skel);
}
-/* defined in prog_tests/uprobe_multi_test.c */
-void uprobe_multi_func_1(void);
-void uprobe_multi_func_2(void);
-void uprobe_multi_func_3(void);
+/*
+ * Weak uprobe target stubs. noinline is required because
+ * uprobe_multi_test_run() takes their addresses to configure the BPF
+ * program's attachment points; an inlined function has no stable
+ * address in the binary to probe. The strong definitions in
+ * uprobe_multi_test.c take precedence when that translation unit is
+ * linked.
+ */
+noinline __weak void uprobe_multi_func_1(void) {}
+noinline __weak void uprobe_multi_func_2(void) {}
+noinline __weak void uprobe_multi_func_3(void) {}
static void uprobe_multi_test_run(struct uprobe_multi *skel)
{
@@ -574,8 +581,6 @@ static void tracing_subtest(struct test_bpf_cookie *skel)
close(fmod_ret_fd);
}
-int stack_mprotect(void);
-
static void lsm_subtest(struct test_bpf_cookie *skel)
{
__u64 cookie;
diff --git a/tools/testing/selftests/bpf/prog_tests/iters.c b/tools/testing/selftests/bpf/prog_tests/iters.c
index a539980a2fbe..7c0b59fdf498 100644
--- a/tools/testing/selftests/bpf/prog_tests/iters.c
+++ b/tools/testing/selftests/bpf/prog_tests/iters.c
@@ -7,6 +7,7 @@
#include <unistd.h>
#include <malloc.h>
#include <stdlib.h>
+#include <linux/compiler.h>
#include <test_progs.h>
#include "cgroup_helpers.h"
@@ -202,8 +203,6 @@ static void subtest_task_iters(void)
iters_task__destroy(skel);
}
-extern int stack_mprotect(void);
-
static void subtest_css_task_iters(void)
{
struct iters_css_task *skel = NULL;
diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
index bdc4fc06bc5a..d7495efd4a56 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -5,36 +5,14 @@
*/
#include <test_progs.h>
-#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <malloc.h>
-#include <stdlib.h>
#include "lsm.skel.h"
#include "lsm_tailcall.skel.h"
char *CMD_ARGS[] = {"true", NULL};
-#define GET_PAGE_ADDR(ADDR, PAGE_SIZE) \
- (char *)(((unsigned long) (ADDR + PAGE_SIZE)) & ~(PAGE_SIZE-1))
-
-int stack_mprotect(void)
-{
- void *buf;
- long sz;
- int ret;
-
- sz = sysconf(_SC_PAGESIZE);
- if (sz < 0)
- return sz;
-
- buf = alloca(sz * 3);
- ret = mprotect(GET_PAGE_ADDR(buf, sz), sz,
- PROT_READ | PROT_WRITE | PROT_EXEC);
- return ret;
-}
-
int exec_cmd(int *monitored_pid)
{
int child_pid, child_status;
diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c
index 6fbe1e995660..3f7ae76c209b 100644
--- a/tools/testing/selftests/bpf/testing_helpers.c
+++ b/tools/testing/selftests/bpf/testing_helpers.c
@@ -5,6 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <sys/mman.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "disasm.h"
@@ -516,3 +517,19 @@ bool is_jit_enabled(void)
return enabled;
}
+
+int stack_mprotect(void)
+{
+ void *buf;
+ long sz;
+ int ret;
+
+ sz = sysconf(_SC_PAGESIZE);
+ if (sz < 0)
+ return sz;
+
+ buf = alloca(sz * 3);
+ ret = mprotect((void *)(((unsigned long)(buf + sz)) & ~(sz - 1)), sz,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+ return ret;
+}
diff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h
index 2ca2356a0b58..2edc6fb7fc52 100644
--- a/tools/testing/selftests/bpf/testing_helpers.h
+++ b/tools/testing/selftests/bpf/testing_helpers.h
@@ -59,5 +59,6 @@ struct bpf_insn;
int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt);
int testing_prog_flags(void);
bool is_jit_enabled(void);
+int stack_mprotect(void);
#endif /* __TESTING_HELPERS_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH bpf-next v7 11/11] selftests/bpf: Tolerate missing files during install
2026-04-16 19:30 [PATCH bpf-next v7 00/11] selftests/bpf: Tolerate partial builds across kernel configs Ricardo B. Marlière
` (9 preceding siblings ...)
2026-04-16 19:30 ` [PATCH bpf-next v7 10/11] selftests/bpf: Provide weak definitions for cross-test functions Ricardo B. Marlière
@ 2026-04-16 19:31 ` Ricardo B. Marlière
10 siblings, 0 replies; 15+ messages in thread
From: Ricardo B. Marlière @ 2026-04-16 19:31 UTC (permalink / raw)
To: Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Kumar Kartikeya Dwivedi,
Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt
Cc: bpf, linux-kselftest, linux-kernel, llvm, Alan Maguire,
Ricardo B. Marliere
With partial builds, some TEST_GEN_FILES entries can be absent at install
time. rsync treats missing source arguments as fatal and aborts kselftest
installation.
Override INSTALL_SINGLE_RULE in selftests/bpf to use --ignore-missing-args,
while keeping the existing bpf-specific INSTALL_RULE extension logic. Also
add --ignore-missing-args to the TEST_INST_SUBDIRS rsync loop so that
subdirectories with no .bpf.o files (e.g. when a test runner flavor was
skipped) do not abort installation.
Note that the INSTALL_SINGLE_RULE override applies globally to all file
categories including static source files (TEST_PROGS, TEST_FILES). These
are version-controlled and should always be present, so the practical risk
is negligible.
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/bpf/Makefile | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 88a03366f6aa..53c3808c0c88 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -974,11 +974,22 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
# Delete partially updated (corrupted) files on error
.DELETE_ON_ERROR:
+# When permissive, tell rsync to ignore missing source arguments so that
+# partial builds do not abort installation.
+ifneq ($(PERMISSIVE),)
+override define INSTALL_SINGLE_RULE
+ $(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH))
+ $(if $(INSTALL_LIST),rsync -a --copy-unsafe-links --ignore-missing-args $(INSTALL_LIST) $(INSTALL_PATH)/)
+endef
+endif
+
DEFAULT_INSTALL_RULE := $(INSTALL_RULE)
override define INSTALL_RULE
$(DEFAULT_INSTALL_RULE)
- @for DIR in $(TEST_INST_SUBDIRS); do \
- mkdir -p $(INSTALL_PATH)/$$DIR; \
- rsync -a $(OUTPUT)/$$DIR/*.bpf.o $(INSTALL_PATH)/$$DIR;\
+ @for DIR in $(TEST_INST_SUBDIRS); do \
+ mkdir -p $(INSTALL_PATH)/$$DIR; \
+ rsync -a $(if $(PERMISSIVE),--ignore-missing-args) \
+ $(OUTPUT)/$$DIR/*.bpf.o \
+ $(INSTALL_PATH)/$$DIR; \
done
endef
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread