From: Bochao Cao 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 --- 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 From: Bochao Cao 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 --- 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 -#include +#include +#include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include #include #include #include -#include -#include - -#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