* [PATCH net-next v4 0/2] selftests: Fix and migrate XDP feature test
@ 2026-09-21 6:00 Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 1/2] selftests/bpf: Track test_xdp_features DUT processes Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 2/2] selftests/net: Move XDP feature test to driver framework Bochao Cao via B4 Relay
0 siblings, 2 replies; 3+ messages in thread
From: Bochao Cao via B4 Relay @ 2026-09-21 6:00 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, David S. Miller,
Jakub Kicinski, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Andrii Nakryiko, Eduard Zingerman,
Ihor Solodrai, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Andrew Lunn, Eric Dumazet, Paolo Abeni
Cc: Alexis Lothoré,
Lorenzo Bianconi, Tiezhu Yang, netdev, bpf, linux-kselftest,
linux-kernel, Bochao Cao
Fix DUT process tracking, readiness retries, and signal cleanup, then
move the XDP feature test to drivers/net/hw as requested during v3 review.
The NetDrvEpEnv runner uses configured DUT and remote interfaces and
reports detected-versus-advertised mismatches as test failures.
Collect results before teardown so the echo used to wake the DUT thread
cannot contaminate tester counters and cause a false XDP_DROP failure.
Changes in v4:
- Correct the description of the set -e readiness behavior.
- Handle signals arriving before DUT PID assignment, including QUIT/ABRT.
- Move the test and build rules to drivers/net/hw and use NetDrvEpEnv.
- Report feature mismatches as failures and sample counters before cleanup.
Testing on Ubuntu 7.0.0-30-generic:
- Built xdp_features; checked shell and Python syntax.
- Original and v4 REDIRECT/NDO_XMIT results matched in three repeated runs
with GRO enabled and TX checksum offload disabled, as in the old setup.
- Confirmed default veth offload settings reproduce mismatches in both
versions; these are not migration-specific regressions.
- After fixing cleanup counter contamination, XDP_DROP passed five runs
and the complete six-test runner passed twice (12 passes, zero failures).
- SIGTERM interruption left no xdp_features processes, veths, or netns.
- Rebased onto net-next commit 8830e65ed46d; repeated the build, Python
syntax, diff-check, and per-patch checkpatch validation.
Native-XDP hardware traffic testing remains unverified: the available
e1000e interface rejects native-mode attachment with EOPNOTSUPP.
Link: https://lore.kernel.org/r/20260904-xdp-dut-process-lifecycle-gmail-v3-1-5b7eee4f7009@gmail.com
Signed-off-by: Bochao Cao <bochaolucky@gmail.com>
---
Bochao Cao (2):
selftests/bpf: Track test_xdp_features DUT processes
selftests/net: Move XDP feature test to driver framework
tools/testing/selftests/bpf/Makefile | 11 +-
tools/testing/selftests/bpf/test_xdp_features.sh | 107 -----------------
tools/testing/selftests/drivers/net/hw/.gitignore | 3 +
tools/testing/selftests/drivers/net/hw/Makefile | 22 ++++
.../net/hw/xdp_features.bpf.c} | 0
.../{bpf => drivers/net/hw}/xdp_features.c | 126 +++++++++++++++------
.../{bpf => drivers/net/hw}/xdp_features.h | 0
.../selftests/drivers/net/hw/xdp_features.py | 109 ++++++++++++++++++
8 files changed, 226 insertions(+), 152 deletions(-)
---
base-commit: 8830e65ed46de41f849eefb8ba227d4852c460f6
change-id: 20260921-xdp-features-v4-send-2c0da5db22d7
Best regards,
--
Bochao Cao <bochaolucky@gmail.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net-next v4 1/2] selftests/bpf: Track test_xdp_features DUT processes
2026-09-21 6:00 [PATCH net-next v4 0/2] selftests: Fix and migrate XDP feature test Bochao Cao via B4 Relay
@ 2026-09-21 6:00 ` Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 2/2] selftests/net: Move XDP feature test to driver framework Bochao Cao via B4 Relay
1 sibling, 0 replies; 3+ messages in thread
From: Bochao Cao via B4 Relay @ 2026-09-21 6:00 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, David S. Miller,
Jakub Kicinski, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Andrii Nakryiko, Eduard Zingerman,
Ihor Solodrai, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Andrew Lunn, Eric Dumazet, Paolo Abeni
Cc: Alexis Lothoré,
Lorenzo Bianconi, Tiezhu Yang, netdev, bpf, linux-kselftest,
linux-kernel, Bochao Cao
From: Bochao Cao <bochaolucky@gmail.com>
Track the DUT process instead of matching processes by name to avoid
interference between concurrent tests. Fix readiness retries under
set -e and clean up the DUT and network state on exit or interruption.
Signed-off-by: Bochao Cao <bochaolucky@gmail.com>
---
tools/testing/selftests/bpf/test_xdp_features.sh | 88 ++++++++++++++++++------
1 file changed, 68 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh b/tools/testing/selftests/bpf/test_xdp_features.sh
index 0aa71c445..a78e0fb9e 100755
--- a/tools/testing/selftests/bpf/test_xdp_features.sh
+++ b/tools/testing/selftests/bpf/test_xdp_features.sh
@@ -8,6 +8,7 @@ readonly V0_IP6=2001:db8::11
readonly V1_IP6=2001:db8::1
ret=1
+dut_pid=""
setup() {
{
@@ -30,77 +31,124 @@ setup() {
} > /dev/null 2>&1
}
+terminate_dut_server() {
+ local pid
+
+ # Use the shell job instead of dut_pid, which may not have been assigned
+ # yet if a signal arrived immediately after the server was started.
+ pid=$(jobs -pr %% 2> /dev/null) || true
+ if [ -z "$pid" ]; then
+ dut_pid=""
+ return
+ fi
+
+ kill -KILL %% 2> /dev/null || true
+ wait "$pid" 2> /dev/null || true
+ dut_pid=""
+}
+
cleanup() {
- ip link del v1 2> /dev/null
- ip netns del ${NS} 2> /dev/null
- [ "$(pidof xdp_features)" = "" ] || kill $(pidof xdp_features) 2> /dev/null
+ terminate_dut_server
+ ip link del v1 2> /dev/null || true
+ ip netns del "${NS}" 2> /dev/null || true
}
wait_for_dut_server() {
- while sleep 1; do
- ss -tlp | grep -q xdp_features
- [ $? -eq 0 ] && break
+ local i
+
+ for ((i = 0; i < 10; i++)); do
+ if [ "$(jobs -pr %% 2> /dev/null)" != "$dut_pid" ]; then
+ echo "xdp_features server $dut_pid exited before accepting connections" >&2
+ return 1
+ fi
+
+ if ss -tlp 2> /dev/null | grep -q "pid=$dut_pid,"; then
+ return 0
+ fi
+
+ sleep 1
done
+
+ echo "Timed out waiting for xdp_features server $dut_pid" >&2
+ return 1
+}
+
+start_dut_server() {
+ ./xdp_features "$@" &
+ dut_pid=$!
+ wait_for_dut_server
+}
+
+reap_dut_server() {
+ local status=0
+
+ wait "$dut_pid" || status=$?
+ dut_pid=""
+ return "$status"
}
test_xdp_features() {
setup
## XDP_PASS
- ./xdp_features -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_PASS \
-D $V1_IP6 -C $V1_IP6 \
-T $V0_IP6 v0
[ $? -ne 0 ] && exit
+ reap_dut_server
## XDP_DROP
- ./xdp_features -f XDP_DROP -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_DROP -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_DROP \
-D ::ffff:$V1_IP4 \
-C ::ffff:$V1_IP4 \
-T ::ffff:$V0_IP4 v0
[ $? -ne 0 ] && exit
+ reap_dut_server
## XDP_ABORTED
- ./xdp_features -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_ABORTED \
-D $V1_IP6 -C $V1_IP6 \
-T $V0_IP6 v0
[ $? -ne 0 ] && exit
+ reap_dut_server
## XDP_TX
- ./xdp_features -f XDP_TX -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_TX -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_TX \
-D ::ffff:$V1_IP4 \
-C ::ffff:$V1_IP4 \
-T ::ffff:$V0_IP4 v0
[ $? -ne 0 ] && exit
+ reap_dut_server
## XDP_REDIRECT
- ./xdp_features -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_REDIRECT \
-D $V1_IP6 -C $V1_IP6 \
-T $V0_IP6 v0
[ $? -ne 0 ] && exit
+ reap_dut_server
## XDP_NDO_XMIT
- ./xdp_features -f XDP_NDO_XMIT -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1 &
- wait_for_dut_server
+ start_dut_server -f XDP_NDO_XMIT -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
ip netns exec ${NS} ./xdp_features -t -f XDP_NDO_XMIT \
-D ::ffff:$V1_IP4 \
-C ::ffff:$V1_IP4 \
-T ::ffff:$V0_IP4 v0
ret=$?
- cleanup
+ reap_dut_server
}
set -e
-trap cleanup 2 3 6 9
+trap cleanup EXIT
+trap 'exit 129' HUP
+trap 'exit 130' INT
+trap 'exit 131' QUIT
+trap 'exit 134' ABRT
+trap 'exit 143' TERM
test_xdp_features
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net-next v4 2/2] selftests/net: Move XDP feature test to driver framework
2026-09-21 6:00 [PATCH net-next v4 0/2] selftests: Fix and migrate XDP feature test Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 1/2] selftests/bpf: Track test_xdp_features DUT processes Bochao Cao via B4 Relay
@ 2026-09-21 6:00 ` Bochao Cao via B4 Relay
1 sibling, 0 replies; 3+ messages in thread
From: Bochao Cao via B4 Relay @ 2026-09-21 6:00 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, David S. Miller,
Jakub Kicinski, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Andrii Nakryiko, Eduard Zingerman,
Ihor Solodrai, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Andrew Lunn, Eric Dumazet, Paolo Abeni
Cc: Alexis Lothoré,
Lorenzo Bianconi, Tiezhu Yang, netdev, bpf, linux-kselftest,
linux-kernel, Bochao Cao
From: Bochao Cao <bochaolucky@gmail.com>
Move the XDP feature test to drivers/net/hw and use NetDrvEpEnv to test
configured local and remote interfaces. Sample results before cleanup
traffic and fail tests when detected and advertised features disagree.
Signed-off-by: Bochao Cao <bochaolucky@gmail.com>
---
tools/testing/selftests/bpf/Makefile | 11 +-
tools/testing/selftests/bpf/test_xdp_features.sh | 155 ---------------------
tools/testing/selftests/drivers/net/hw/.gitignore | 3 +
tools/testing/selftests/drivers/net/hw/Makefile | 22 +++
.../net/hw/xdp_features.bpf.c} | 0
.../{bpf => drivers/net/hw}/xdp_features.c | 126 ++++++++++++-----
.../{bpf => drivers/net/hw}/xdp_features.h | 0
.../selftests/drivers/net/hw/xdp_features.py | 109 +++++++++++++++
8 files changed, 226 insertions(+), 200 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 93c707116..cb76b68c9 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -126,7 +126,7 @@ TEST_PROGS := test_kmod.sh \
test_bpftool_build.sh \
test_doc_build.sh \
test_xsk.sh \
- test_xdp_features.sh
+ #
TEST_PROGS_EXTENDED := \
ima_setup.sh verify_sig_setup.sh
@@ -142,7 +142,6 @@ TEST_GEN_PROGS_EXTENDED = \
test_cpp \
test_lirc_mode2_user \
veristat \
- xdp_features \
xdp_hw_metadata \
xdp_synproxy \
xskxceiver
@@ -544,7 +543,6 @@ test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton
test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o
xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o
xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o
-xdp_features.skel.h-deps := xdp_features.bpf.o
tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o tracing_multi_check.bpf.o
tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o tracing_multi_check.bpf.o
tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o tracing_multi_check.bpf.o
@@ -947,13 +945,6 @@ $(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c xsk.c network_helpers.c \
$(call msg,BINARY,,$@)
$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
-$(OUTPUT)/xdp_features: xdp_features.c network_helpers.c xdp_features.h \
- network_helpers.h \
- test_progs.h bpf_util.h $(OUTPUT)/xdp_features.skel.h \
- $(BPFOBJ) | $(OUTPUT)
- $(call msg,BINARY,,$@)
- $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
-
# Make sure we are able to include and link libbpf against c++.
CXXFLAGS += $(CFLAGS)
CXXFLAGS := $(subst -D_GNU_SOURCE=,,$(CXXFLAGS))
diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh b/tools/testing/selftests/bpf/test_xdp_features.sh
deleted file mode 100755
index a78e0fb9e..000000000
--- a/tools/testing/selftests/bpf/test_xdp_features.sh
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-
-readonly NS="ns1-$(mktemp -u XXXXXX)"
-readonly V0_IP4=10.10.0.11
-readonly V1_IP4=10.10.0.1
-readonly V0_IP6=2001:db8::11
-readonly V1_IP6=2001:db8::1
-
-ret=1
-dut_pid=""
-
-setup() {
- {
- ip netns add ${NS}
-
- ip link add v1 type veth peer name v0 netns ${NS}
-
- ip link set v1 up
- ip addr add $V1_IP4/24 dev v1
- ip addr add $V1_IP6/64 nodad dev v1
- ip -n ${NS} link set dev v0 up
- ip -n ${NS} addr add $V0_IP4/24 dev v0
- ip -n ${NS} addr add $V0_IP6/64 nodad dev v0
-
- # Enable XDP mode and disable checksum offload
- ethtool -K v1 gro on
- ethtool -K v1 tx-checksumming off
- ip netns exec ${NS} ethtool -K v0 gro on
- ip netns exec ${NS} ethtool -K v0 tx-checksumming off
- } > /dev/null 2>&1
-}
-
-terminate_dut_server() {
- local pid
-
- # Use the shell job instead of dut_pid, which may not have been assigned
- # yet if a signal arrived immediately after the server was started.
- pid=$(jobs -pr %% 2> /dev/null) || true
- if [ -z "$pid" ]; then
- dut_pid=""
- return
- fi
-
- kill -KILL %% 2> /dev/null || true
- wait "$pid" 2> /dev/null || true
- dut_pid=""
-}
-
-cleanup() {
- terminate_dut_server
- ip link del v1 2> /dev/null || true
- ip netns del "${NS}" 2> /dev/null || true
-}
-
-wait_for_dut_server() {
- local i
-
- for ((i = 0; i < 10; i++)); do
- if [ "$(jobs -pr %% 2> /dev/null)" != "$dut_pid" ]; then
- echo "xdp_features server $dut_pid exited before accepting connections" >&2
- return 1
- fi
-
- if ss -tlp 2> /dev/null | grep -q "pid=$dut_pid,"; then
- return 0
- fi
-
- sleep 1
- done
-
- echo "Timed out waiting for xdp_features server $dut_pid" >&2
- return 1
-}
-
-start_dut_server() {
- ./xdp_features "$@" &
- dut_pid=$!
- wait_for_dut_server
-}
-
-reap_dut_server() {
- local status=0
-
- wait "$dut_pid" || status=$?
- dut_pid=""
- return "$status"
-}
-
-test_xdp_features() {
- setup
-
- ## XDP_PASS
- start_dut_server -f XDP_PASS -D $V1_IP6 -T $V0_IP6 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_PASS \
- -D $V1_IP6 -C $V1_IP6 \
- -T $V0_IP6 v0
- [ $? -ne 0 ] && exit
- reap_dut_server
-
- ## XDP_DROP
- start_dut_server -f XDP_DROP -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_DROP \
- -D ::ffff:$V1_IP4 \
- -C ::ffff:$V1_IP4 \
- -T ::ffff:$V0_IP4 v0
- [ $? -ne 0 ] && exit
- reap_dut_server
-
- ## XDP_ABORTED
- start_dut_server -f XDP_ABORTED -D $V1_IP6 -T $V0_IP6 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_ABORTED \
- -D $V1_IP6 -C $V1_IP6 \
- -T $V0_IP6 v0
- [ $? -ne 0 ] && exit
- reap_dut_server
-
- ## XDP_TX
- start_dut_server -f XDP_TX -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_TX \
- -D ::ffff:$V1_IP4 \
- -C ::ffff:$V1_IP4 \
- -T ::ffff:$V0_IP4 v0
- [ $? -ne 0 ] && exit
- reap_dut_server
-
- ## XDP_REDIRECT
- start_dut_server -f XDP_REDIRECT -D $V1_IP6 -T $V0_IP6 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_REDIRECT \
- -D $V1_IP6 -C $V1_IP6 \
- -T $V0_IP6 v0
- [ $? -ne 0 ] && exit
- reap_dut_server
-
- ## XDP_NDO_XMIT
- start_dut_server -f XDP_NDO_XMIT -D ::ffff:$V1_IP4 -T ::ffff:$V0_IP4 v1
- ip netns exec ${NS} ./xdp_features -t -f XDP_NDO_XMIT \
- -D ::ffff:$V1_IP4 \
- -C ::ffff:$V1_IP4 \
- -T ::ffff:$V0_IP4 v0
- ret=$?
- reap_dut_server
-}
-
-set -e
-trap cleanup EXIT
-trap 'exit 129' HUP
-trap 'exit 130' INT
-trap 'exit 131' QUIT
-trap 'exit 134' ABRT
-trap 'exit 143' TERM
-
-test_xdp_features
-
-exit $ret
diff --git a/tools/testing/selftests/drivers/net/hw/.gitignore b/tools/testing/selftests/drivers/net/hw/.gitignore
index 46540468a..a0cd8bc37 100644
--- a/tools/testing/selftests/drivers/net/hw/.gitignore
+++ b/tools/testing/selftests/drivers/net/hw/.gitignore
@@ -2,3 +2,6 @@
iou-zcrx
ncdevmem
toeplitz
+tools/
+xdp_features
+xdp_features.skel.h
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 8aebdc6fe..a5dea02ee 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -50,6 +50,7 @@ TEST_PROGS = \
tso.py \
userns_devmem.py \
uso.py \
+ xdp_features.py \
xdp_metadata.py \
xsk_reconfig.py \
#
@@ -80,6 +81,7 @@ YNL_GEN_FILES := \
# end of YNL_GEN_FILES
TEST_GEN_FILES += $(YNL_GEN_FILES)
TEST_GEN_FILES += $(patsubst %.c,%.o,$(wildcard *.bpf.c))
+TEST_GEN_PROGS += xdp_features
include ../../../lib.mk
@@ -93,6 +95,26 @@ include ../../../net/ynl.mk
include ../../../net/bpf.mk
+BPFTOOL ?= bpftool
+
+CFLAGS += -I$(OUTPUT) -I$(SCRATCH_DIR)/include
+CFLAGS += -I$(top_srcdir)/tools/include
+CFLAGS += -I$(top_srcdir)/tools/arch/$(SRCARCH)/include
+CFLAGS += -I$(top_srcdir)/tools/include/uapi
+CFLAGS += -I../../../net/lib
+
+$(OUTPUT)/xdp_features.skel.h: $(OUTPUT)/xdp_features.bpf.o
+ $(call msg,GEN-SKEL,,$@)
+ $(Q)$(BPFTOOL) gen skeleton $< name xdp_features > $@
+
+$(OUTPUT)/xdp_features: xdp_features.c xdp_features.h \
+ $(OUTPUT)/xdp_features.skel.h $(BPFOBJ)
+ $(call msg,BINARY,,$@)
+ $(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) \
+ $(LDLIBS) -lelf -lz -lpthread -o $@
+
+EXTRA_CLEAN += $(OUTPUT)/xdp_features.skel.h
+
ifeq ($(HAS_IOURING_ZCRX),y)
$(OUTPUT)/iou-zcrx: LDLIBS += -luring -lpthread
endif
diff --git a/tools/testing/selftests/bpf/progs/xdp_features.c b/tools/testing/selftests/drivers/net/hw/xdp_features.bpf.c
similarity index 100%
rename from tools/testing/selftests/bpf/progs/xdp_features.c
rename to tools/testing/selftests/drivers/net/hw/xdp_features.bpf.c
diff --git a/tools/testing/selftests/bpf/xdp_features.c b/tools/testing/selftests/drivers/net/hw/xdp_features.c
similarity index 86%
rename from tools/testing/selftests/bpf/xdp_features.c
rename to tools/testing/selftests/drivers/net/hw/xdp_features.c
index a27ed6639..ccc0630bb 100644
--- a/tools/testing/selftests/bpf/xdp_features.c
+++ b/tools/testing/selftests/drivers/net/hw/xdp_features.c
@@ -1,22 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
-#include <uapi/linux/bpf.h>
-#include <uapi/linux/netdev.h>
+#include <linux/bpf.h>
+#include <linux/netdev.h>
#include <linux/if_link.h>
-#include <signal.h>
#include <argp.h>
+#include <errno.h>
#include <net/if.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/socket.h>
+#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
-#include <pthread.h>
-#include <network_helpers.h>
-
-#include "bpf_util.h"
+#include "ksft.h"
#include "xdp_features.skel.h"
#include "xdp_features.h"
@@ -40,7 +44,63 @@ static struct env {
#define BUFSIZE 128
-void test__fail(void) { /* for network_helpers.c */ }
+static int make_sockaddr(const char *addr_str, __u16 port,
+ struct sockaddr_storage *addr)
+{
+ struct sockaddr_in6 *sin6 = (void *)addr;
+
+ memset(addr, 0, sizeof(*addr));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ if (addr_str && inet_pton(AF_INET6, addr_str, &sin6->sin6_addr) != 1)
+ return -1;
+
+ return 0;
+}
+
+static int settimeo(int fd, int timeout_ms)
+{
+ struct timeval timeout = { .tv_sec = 3 };
+
+ if (timeout_ms > 0) {
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
+ sizeof(timeout)) ||
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
+ sizeof(timeout)))
+ return -1;
+
+ return 0;
+}
+
+static int start_server(int type, __u16 port)
+{
+ struct sockaddr_storage addr;
+ int fd, on = 1;
+
+ fd = socket(AF_INET6, type, 0);
+ if (fd < 0)
+ return -1;
+
+ if (settimeo(fd, 0) ||
+ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) ||
+ (type == SOCK_STREAM &&
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) ||
+ make_sockaddr(NULL, port, &addr) ||
+ bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) ||
+ (type == SOCK_STREAM && listen(fd, 1))) {
+ int err = errno;
+
+ close(fd);
+ errno = err;
+ return -1;
+ }
+
+ return fd;
+}
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
@@ -151,8 +211,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
}
break;
case 'D':
- if (make_sockaddr(AF_INET6, arg, DUT_ECHO_PORT,
- &env.dut_addr, NULL)) {
+ if (make_sockaddr(arg, DUT_ECHO_PORT, &env.dut_addr)) {
fprintf(stderr,
"Invalid address assigned to the Device Under Test: %s\n",
arg);
@@ -160,8 +219,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
}
break;
case 'C':
- if (make_sockaddr(AF_INET6, arg, DUT_CTRL_PORT,
- &env.dut_ctrl_addr, NULL)) {
+ if (make_sockaddr(arg, DUT_CTRL_PORT, &env.dut_ctrl_addr)) {
fprintf(stderr,
"Invalid address assigned to the Device Under Test: %s\n",
arg);
@@ -169,7 +227,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
}
break;
case 'T':
- if (make_sockaddr(AF_INET6, arg, 0, &env.tester_addr, NULL)) {
+ if (make_sockaddr(arg, 0, &env.tester_addr)) {
fprintf(stderr,
"Invalid address assigned to the Tester device: %s\n",
arg);
@@ -213,12 +271,11 @@ static void set_env_default(void)
env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
env.feature.action = -EINVAL;
env.ifindex = -ENODEV;
- strscpy(env.ifname, "unknown");
- make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_CTRL_PORT,
- &env.dut_ctrl_addr, NULL);
- make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_ECHO_PORT,
- &env.dut_addr, NULL);
- make_sockaddr(AF_INET6, "::ffff:127.0.0.1", 0, &env.tester_addr, NULL);
+ snprintf(env.ifname, sizeof(env.ifname), "unknown");
+ make_sockaddr("::ffff:127.0.0.1", DUT_CTRL_PORT,
+ &env.dut_ctrl_addr);
+ make_sockaddr("::ffff:127.0.0.1", DUT_ECHO_PORT, &env.dut_addr);
+ make_sockaddr("::ffff:127.0.0.1", 0, &env.tester_addr);
}
static void *dut_echo_thread(void *arg)
@@ -229,7 +286,7 @@ static void *dut_echo_thread(void *arg)
while (!exiting) {
struct tlv_hdr *tlv = (struct tlv_hdr *)buf;
struct sockaddr_storage addr;
- socklen_t addrlen;
+ socklen_t addrlen = sizeof(addr);
size_t n;
n = recvfrom(sockfd, buf, sizeof(buf), MSG_WAITALL,
@@ -244,7 +301,6 @@ static void *dut_echo_thread(void *arg)
(struct sockaddr *)&addr, addrlen);
}
- pthread_exit((void *)0);
close(sockfd);
return NULL;
@@ -254,9 +310,8 @@ static int dut_run_echo_thread(pthread_t *t, int *sockfd)
{
int err;
- sockfd = start_reuseport_server(AF_INET6, SOCK_DGRAM, NULL,
- DUT_ECHO_PORT, 0, 1);
- if (!sockfd) {
+ *sockfd = start_server(SOCK_DGRAM, DUT_ECHO_PORT);
+ if (*sockfd < 0) {
fprintf(stderr,
"Failed creating data UDP socket on device %s\n",
env.ifname);
@@ -269,7 +324,7 @@ static int dut_run_echo_thread(pthread_t *t, int *sockfd)
fprintf(stderr,
"Failed creating data UDP thread on device %s: %s\n",
env.ifname, strerror(-err));
- free_fds(sockfd, 1);
+ close(*sockfd);
return -EINVAL;
}
@@ -361,25 +416,25 @@ static int recv_msg(int sockfd, void *buf, size_t bufsize, void *val,
static int dut_run(struct xdp_features *skel)
{
int flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE;
- int state, err = 0, *sockfd, ctrl_sockfd, echo_sockfd;
+ int state = CMD_STOP, err = 0, sockfd, ctrl_sockfd, echo_sockfd;
struct sockaddr_storage ctrl_addr;
pthread_t dut_thread = 0;
- socklen_t addrlen;
+ socklen_t addrlen = sizeof(ctrl_addr);
- sockfd = start_reuseport_server(AF_INET6, SOCK_STREAM, NULL,
- DUT_CTRL_PORT, 0, 1);
- if (!sockfd) {
+ sockfd = start_server(SOCK_STREAM, DUT_CTRL_PORT);
+ if (sockfd < 0) {
fprintf(stderr,
"Failed creating control socket on device %s\n", env.ifname);
return -errno;
}
+ ksft_ready();
- ctrl_sockfd = accept(*sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);
+ ctrl_sockfd = accept(sockfd, (struct sockaddr *)&ctrl_addr, &addrlen);
if (ctrl_sockfd < 0) {
fprintf(stderr,
"Failed accepting connections on device %s control socket\n",
env.ifname);
- free_fds(sockfd, 1);
+ close(sockfd);
return -errno;
}
@@ -488,7 +543,7 @@ static int dut_run(struct xdp_features *skel)
out:
bpf_xdp_detach(env.ifindex, flags, NULL);
close(ctrl_sockfd);
- free_fds(sockfd, 1);
+ close(sockfd);
return err;
}
@@ -636,13 +691,14 @@ static int tester_run(struct xdp_features *skel)
if (err)
goto out;
+ /* Collect results before cleanup traffic can reach the tester. */
+ detected_cap = tester_collect_detected_cap(skel, ntohl(stats));
+
/* stop the test */
err = send_and_recv_msg(sockfd, CMD_STOP, NULL, 0);
/* send a new echo message to wake echo thread of the dut */
send_echo_msg();
- detected_cap = tester_collect_detected_cap(skel, ntohl(stats));
-
fprintf(stdout, "Feature %s: [%s][%s]\n", get_xdp_feature_str(),
detected_cap ? GREEN("DETECTED") : RED("NOT DETECTED"),
env.feature.drv_feature & advertised_feature ? GREEN("ADVERTISED")
diff --git a/tools/testing/selftests/bpf/xdp_features.h b/tools/testing/selftests/drivers/net/hw/xdp_features.h
similarity index 100%
rename from tools/testing/selftests/bpf/xdp_features.h
rename to tools/testing/selftests/drivers/net/hw/xdp_features.h
diff --git a/tools/testing/selftests/drivers/net/hw/xdp_features.py b/tools/testing/selftests/drivers/net/hw/xdp_features.py
new file mode 100755
index 000000000..5eeadf803
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/xdp_features.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Check that a device's advertised XDP features match its behavior."""
+
+import ipaddress
+from pathlib import Path
+import re
+import shlex
+
+from lib.py import bkg, cmd, ksft_eq, ksft_exit, ksft_run, NetDrvEpEnv
+
+
+ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*m")
+RESULT = re.compile(
+ r"Feature .*: \[(NOT )?DETECTED\]\[(NOT )?ADVERTISED\]"
+)
+
+
+def mapped_address(address):
+ """Return an IPv6 or IPv4-mapped IPv6 address for xdp_features."""
+ address = ipaddress.ip_address(address)
+ if address.version == 4:
+ return f"::ffff:{address}"
+ return str(address)
+
+
+def feature_command(binary, feature, dut_addr, tester_addr, ifname,
+ tester=False):
+ args = [str(binary)]
+ if tester:
+ args.append("-t")
+ args += ["-f", feature, "-D", dut_addr]
+ if tester:
+ args += ["-C", dut_addr]
+ args += ["-T", tester_addr, ifname]
+ return shlex.join(args)
+
+
+def run_feature(cfg, feature, ipver):
+ if not cfg.addr_v[ipver]:
+ ipver = "4" if ipver == "6" else "6"
+ cfg.require_ipver(ipver)
+ dut_addr = mapped_address(cfg.addr_v[ipver])
+ tester_addr = mapped_address(cfg.remote_addr_v[ipver])
+
+ dut_cmd = feature_command(cfg.xdp_features, feature, dut_addr,
+ tester_addr, cfg.ifname)
+ tester_cmd = feature_command(cfg.remote_xdp_features, feature, dut_addr,
+ tester_addr, cfg.remote_ifname, tester=True)
+
+ with bkg(dut_cmd, exit_wait=True, ksft_ready=True):
+ result = cmd(tester_cmd, host=cfg.remote)
+
+ output = ANSI_ESCAPE.sub("", result.stdout)
+ match = RESULT.search(output)
+ if not match:
+ raise Exception(f"Unable to parse xdp_features output: {output}")
+
+ detected = match.group(1) is None
+ advertised = match.group(2) is None
+ ksft_eq(detected, advertised,
+ comment=f"{feature}: detected and advertised support")
+
+
+def test_xdp_pass(cfg):
+ run_feature(cfg, "XDP_PASS", "6")
+
+
+def test_xdp_drop(cfg):
+ run_feature(cfg, "XDP_DROP", "4")
+
+
+def test_xdp_aborted(cfg):
+ run_feature(cfg, "XDP_ABORTED", "6")
+
+
+def test_xdp_tx(cfg):
+ run_feature(cfg, "XDP_TX", "4")
+
+
+def test_xdp_redirect(cfg):
+ run_feature(cfg, "XDP_REDIRECT", "6")
+
+
+def test_xdp_ndo_xmit(cfg):
+ run_feature(cfg, "XDP_NDO_XMIT", "4")
+
+
+def main():
+ with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+ cfg.xdp_features = (Path(__file__).parent / "xdp_features").resolve()
+ cfg.remote_xdp_features = cfg.remote.deploy(
+ cfg.xdp_features.as_posix()
+ )
+
+ ksft_run([
+ test_xdp_pass,
+ test_xdp_drop,
+ test_xdp_aborted,
+ test_xdp_tx,
+ test_xdp_redirect,
+ test_xdp_ndo_xmit,
+ ], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-21 6:00 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21 6:00 [PATCH net-next v4 0/2] selftests: Fix and migrate XDP feature test Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 1/2] selftests/bpf: Track test_xdp_features DUT processes Bochao Cao via B4 Relay
2026-09-21 6:00 ` [PATCH net-next v4 2/2] selftests/net: Move XDP feature test to driver framework Bochao Cao via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®