From: Bochao Cao test_xdp_features.sh waits for any xdp_features listener to appear and uses pidof during cleanup. A concurrent test can therefore make another test proceed before its own DUT is ready, and cleanup kills every xdp_features process on the host. The readiness loop also has no timeout, so a DUT that exits before listening leaves the test hung indefinitely. Track one active DUT at a time, wait for ss to report that exact PID with a bounded retry loop, and reap it after each test. Consult the shell job table before signaling the DUT so a stale PID cannot target an unrelated process. On failure, terminate the shell job with SIGKILL and reap it so blocked I/O cannot hang cleanup. Install an EXIT trap and signal handlers so failure paths also remove network setup. Fixes: 4dba3e7852b7 ("selftests/bpf: introduce XDP compliance test tool") Closes: https://bugs.debian.org/1136522 Signed-off-by: Bochao Cao --- Tests: - bash -n tools/testing/selftests/bpf/test_xdp_features.sh - make -C tools/testing/selftests/bpf xdp_features - sudo tools/testing/selftests/bpf/test_xdp_features.sh - verified cleanup terminates a blocked DUT without affecting an unrelated process --- Changes in v2: - Clarify that avoiding name-wide process matching, rather than dropping a dependency, is the motivation. - Track and reap one active DUT at a time instead of retaining historical PIDs. - Address PID reuse by signaling only the current Bash job during cleanup. - Use SIGKILL on failure cleanup so blocked DUT I/O cannot hang wait indefinitely. - Link to v1: https://patch.msgid.link/20260805-xdp-dut-process-lifecycle-gmail-v1-1-45984df8d295@gmail.com --- tools/testing/selftests/bpf/test_xdp_features.sh | 82 ++++++++++++++++++------ 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/bpf/test_xdp_features.sh b/tools/testing/selftests/bpf/test_xdp_features.sh index 0aa71c4455c0..35ce0d4e2418 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,118 @@ setup() { } > /dev/null 2>&1 } +terminate_dut_server() { + [ -z "$dut_pid" ] && return + + # Use the shell job instead of a PID which may have been reused. + if [ "$(jobs -pr %% 2> /dev/null)" = "$dut_pid" ]; then + kill -KILL %% 2> /dev/null || true + fi + + wait "$dut_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 143' TERM test_xdp_features --- base-commit: 07cb86aa50816b070b99c89bf948762ef035a1f2 change-id: 20260805-xdp-dut-process-lifecycle-gmail-6d60bf8dcdf1 Best regards, -- Bochao Cao