Greetings:
Welcome to v3.
This series fixes netdevsim to correctly set the NAPI ID on the skb. This is helpful for writing tests around features that use SO_INCOMING_NAPI_ID.
In addition to the netdevsim fix in patch 1, patches 2 & 3 do some self test refactoring and add a test for NAPI IDs. The test itself (patch 4) introduces a C helper because apparently python doesn't have socket.SO_INCOMING_NAPI_ID.
Thanks, Joe
v3: - Dropped patch 3 from v2 as it is no longer necessary. - Patch 3 from this series (which was patch 4 in the v2) - Sorted .gitignore alphabetically - added cfg.remote_deploy so the test supports real remote machines - Dropped the NetNSEnter as it is unnecessary - Fixed a string interpolation issue that Paolo hit with his Python version
v2: https://lore.kernel.org/netdev/20250417013301.39228-1-jdamato@fastly.com/ - No longer an RFC - Minor whitespace change in patch 1 (no functional change). - Patches 2-4 new in v2
rfcv1: https://lore.kernel.org/netdev/20250329000030.39543-1-jdamato@fastly.com/
Joe Damato (3): netdevsim: Mark NAPI ID on skb in nsim_rcv selftests: drv-net: Factor out ksft C helpers selftests: drv-net: Test that NAPI ID is non-zero
drivers/net/netdevsim/netdev.c | 2 + .../testing/selftests/drivers/net/.gitignore | 1 + tools/testing/selftests/drivers/net/Makefile | 6 +- tools/testing/selftests/drivers/net/ksft.h | 56 +++++++++++++ .../testing/selftests/drivers/net/napi_id.py | 24 ++++++ .../selftests/drivers/net/napi_id_helper.c | 83 +++++++++++++++++++ .../selftests/drivers/net/xdp_helper.c | 49 +---------- 7 files changed, 173 insertions(+), 48 deletions(-) create mode 100644 tools/testing/selftests/drivers/net/ksft.h create mode 100755 tools/testing/selftests/drivers/net/napi_id.py create mode 100644 tools/testing/selftests/drivers/net/napi_id_helper.c
base-commit: 22ab6b9467c1822291a1175a0eb825b7ec057ef9
Factor ksft C helpers to a header so they can be used by other C-based tests.
Signed-off-by: Joe Damato jdamato@fastly.com --- tools/testing/selftests/drivers/net/ksft.h | 56 +++++++++++++++++++ .../selftests/drivers/net/xdp_helper.c | 49 +--------------- 2 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 tools/testing/selftests/drivers/net/ksft.h
diff --git a/tools/testing/selftests/drivers/net/ksft.h b/tools/testing/selftests/drivers/net/ksft.h new file mode 100644 index 000000000000..3fd084006a16 --- /dev/null +++ b/tools/testing/selftests/drivers/net/ksft.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(__KSFT_H__) +#define __KSFT_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static void ksft_ready(void) +{ + const char msg[7] = "ready\n"; + char *env_str; + int fd; + + env_str = getenv("KSFT_READY_FD"); + if (env_str) { + fd = atoi(env_str); + if (!fd) { + fprintf(stderr, "invalid KSFT_READY_FD = '%s'\n", + env_str); + return; + } + } else { + fd = STDOUT_FILENO; + } + + write(fd, msg, sizeof(msg)); + if (fd != STDOUT_FILENO) + close(fd); +} + +static void ksft_wait(void) +{ + char *env_str; + char byte; + int fd; + + env_str = getenv("KSFT_WAIT_FD"); + if (env_str) { + fd = atoi(env_str); + if (!fd) { + fprintf(stderr, "invalid KSFT_WAIT_FD = '%s'\n", + env_str); + return; + } + } else { + /* Not running in KSFT env, wait for input from STDIN instead */ + fd = STDIN_FILENO; + } + + read(fd, &byte, sizeof(byte)); + if (fd != STDIN_FILENO) + close(fd); +} + +#endif diff --git a/tools/testing/selftests/drivers/net/xdp_helper.c b/tools/testing/selftests/drivers/net/xdp_helper.c index aeed25914104..d5bb8ac33efa 100644 --- a/tools/testing/selftests/drivers/net/xdp_helper.c +++ b/tools/testing/selftests/drivers/net/xdp_helper.c @@ -11,56 +11,11 @@ #include <net/if.h> #include <inttypes.h>
+#include "ksft.h" + #define UMEM_SZ (1U << 16) #define NUM_DESC (UMEM_SZ / 2048)
-/* Move this to a common header when reused! */ -static void ksft_ready(void) -{ - const char msg[7] = "ready\n"; - char *env_str; - int fd; - - env_str = getenv("KSFT_READY_FD"); - if (env_str) { - fd = atoi(env_str); - if (!fd) { - fprintf(stderr, "invalid KSFT_READY_FD = '%s'\n", - env_str); - return; - } - } else { - fd = STDOUT_FILENO; - } - - write(fd, msg, sizeof(msg)); - if (fd != STDOUT_FILENO) - close(fd); -} - -static void ksft_wait(void) -{ - char *env_str; - char byte; - int fd; - - env_str = getenv("KSFT_WAIT_FD"); - if (env_str) { - fd = atoi(env_str); - if (!fd) { - fprintf(stderr, "invalid KSFT_WAIT_FD = '%s'\n", - env_str); - return; - } - } else { - /* Not running in KSFT env, wait for input from STDIN instead */ - fd = STDIN_FILENO; - } - - read(fd, &byte, sizeof(byte)); - if (fd != STDIN_FILENO) - close(fd); -}
/* this is a simple helper program that creates an XDP socket and does the * minimum necessary to get bind() to succeed.
Test that the SO_INCOMING_NAPI_ID of a network file descriptor is non-zero. This ensures that either the core networking stack or, in some cases like netdevsim, the driver correctly sets the NAPI ID.
Signed-off-by: Joe Damato jdamato@fastly.com --- .../testing/selftests/drivers/net/.gitignore | 1 + tools/testing/selftests/drivers/net/Makefile | 6 +- .../testing/selftests/drivers/net/napi_id.py | 24 ++++++ .../selftests/drivers/net/napi_id_helper.c | 83 +++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/drivers/net/napi_id.py create mode 100644 tools/testing/selftests/drivers/net/napi_id_helper.c
diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore index ec746f374e85..72d2124fd513 100644 --- a/tools/testing/selftests/drivers/net/.gitignore +++ b/tools/testing/selftests/drivers/net/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only +napi_id_helper xdp_helper diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile index 0c95bd944d56..47247c2ef948 100644 --- a/tools/testing/selftests/drivers/net/Makefile +++ b/tools/testing/selftests/drivers/net/Makefile @@ -6,9 +6,13 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \ ../../net/net_helper.sh \ ../../net/lib.sh \
-TEST_GEN_FILES := xdp_helper +TEST_GEN_FILES := \ + napi_id_helper \ + xdp_helper \ +# end of TEST_GEN_FILES
TEST_PROGS := \ + napi_id.py \ netcons_basic.sh \ netcons_fragmented_msg.sh \ netcons_overflow.sh \ diff --git a/tools/testing/selftests/drivers/net/napi_id.py b/tools/testing/selftests/drivers/net/napi_id.py new file mode 100755 index 000000000000..54e51633a70a --- /dev/null +++ b/tools/testing/selftests/drivers/net/napi_id.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +from lib.py import ksft_run, ksft_exit +from lib.py import ksft_eq, NetDrvEpEnv +from lib.py import bkg, cmd, rand_port, NetNSEnter + +def test_napi_id(cfg) -> None: + port = rand_port() + bin_remote = cfg.remote.deploy(cfg.test_dir / "napi_id_helper") + listen_cmd = f"{bin_remote} {cfg.addr_v['4']} {port}" + + with bkg(listen_cmd, ksft_wait=3) as server: + cmd(f"echo a | socat - TCP:{cfg.addr_v['4']}:{port}", host=cfg.remote, shell=True) + + ksft_eq(0, server.ret) + +def main() -> None: + with NetDrvEpEnv(__file__) as cfg: + ksft_run([test_napi_id], args=(cfg,)) + ksft_exit() + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/napi_id_helper.c b/tools/testing/selftests/drivers/net/napi_id_helper.c new file mode 100644 index 000000000000..7e8e7d373b61 --- /dev/null +++ b/tools/testing/selftests/drivers/net/napi_id_helper.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +#include "ksft.h" + +int main(int argc, char *argv[]) +{ + struct sockaddr_in address; + unsigned int napi_id; + unsigned int port; + socklen_t optlen; + char buf[1024]; + int opt = 1; + int server; + int client; + int ret; + + server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (server < 0) { + perror("socket creation failed"); + if (errno == EAFNOSUPPORT) + return -1; + return 1; + } + + port = atoi(argv[2]); + + if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + perror("setsockopt"); + return 1; + } + + address.sin_family = AF_INET; + inet_pton(AF_INET, argv[1], &address.sin_addr); + address.sin_port = htons(port); + + if (bind(server, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("bind failed"); + return 1; + } + + if (listen(server, 1) < 0) { + perror("listen"); + return 1; + } + + ksft_ready(); + + client = accept(server, NULL, 0); + if (client < 0) { + perror("accept"); + return 1; + } + + optlen = sizeof(napi_id); + ret = getsockopt(client, SOL_SOCKET, SO_INCOMING_NAPI_ID, &napi_id, + &optlen); + if (ret != 0) { + perror("getsockopt"); + return 1; + } + + read(client, buf, 1024); + + ksft_wait(); + + if (napi_id == 0) { + fprintf(stderr, "napi ID is 0\n"); + return 1; + } + + close(client); + close(server); + + return 0; +}
On Fri, 18 Apr 2025 01:37:05 +0000 Joe Damato wrote:
- bin_remote = cfg.remote.deploy(cfg.test_dir / "napi_id_helper")
- listen_cmd = f"{bin_remote} {cfg.addr_v['4']} {port}"
- with bkg(listen_cmd, ksft_wait=3) as server:
Sorry, not sure how I misread v2 but you are running the helper locally. So you don't have to deploy it to the remote machine :(
BTW does removing the ksft_wait() from the binary work? Or does it cause trouble? Don't think we need to wait for anything in this case. With the XSK test we had to wait for the test to do the inspection before we unbound. Here once we get the connection we can just exit, no?
On Wed, Apr 23, 2025 at 04:16:12PM -0700, Jakub Kicinski wrote:
On Fri, 18 Apr 2025 01:37:05 +0000 Joe Damato wrote:
- bin_remote = cfg.remote.deploy(cfg.test_dir / "napi_id_helper")
- listen_cmd = f"{bin_remote} {cfg.addr_v['4']} {port}"
- with bkg(listen_cmd, ksft_wait=3) as server:
Sorry, not sure how I misread v2 but you are running the helper locally. So you don't have to deploy it to the remote machine :(
OK I can remove that and fix the macro guard for the v4.
BTW does removing the ksft_wait() from the binary work? Or does it cause trouble? Don't think we need to wait for anything in this case. With the XSK test we had to wait for the test to do the inspection before we unbound. Here once we get the connection we can just exit, no?
I agree that we can just exit, but removing the wait breaks ksft utils:
# Exception| Traceback (most recent call last): # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/ksft.py", line 223, in ksft_run # Exception| case(*args) # Exception| File "/home/jdamato/code/net-next/./tools/testing/selftests/drivers/net/napi_id.py", line 13, in test_napi_id # Exception| with bkg(listen_cmd, ksft_wait=3) as server: # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/utils.py", line 130, in __exit__ # Exception| return self.process(terminate=self.terminate, fail=self.check_fail) # Exception| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/utils.py", line 78, in process # Exception| os.write(self.ksft_term_fd, b"1") # Exception| BrokenPipeError: [Errno 32] Broken pipe
LMK how you'd like me to proceed ?
I'm thinking: - Leave ksft_wait() - macro guard - don't deploy helper to remote machine
On Wed, 23 Apr 2025 17:06:05 -0700 Joe Damato wrote:
# Exception| Traceback (most recent call last): # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/ksft.py", line 223, in ksft_run # Exception| case(*args) # Exception| File "/home/jdamato/code/net-next/./tools/testing/selftests/drivers/net/napi_id.py", line 13, in test_napi_id # Exception| with bkg(listen_cmd, ksft_wait=3) as server: # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/utils.py", line 130, in __exit__ # Exception| return self.process(terminate=self.terminate, fail=self.check_fail) # Exception| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # Exception| File "/home/jdamato/code/net-next/tools/testing/selftests/net/lib/py/utils.py", line 78, in process # Exception| os.write(self.ksft_term_fd, b"1") # Exception| BrokenPipeError: [Errno 32] Broken pipe
Thanks for testing! Makes sense, I don't think it's worth complicating the Python side to handle the "ready but no wait" case if it doesnt work as is.
LMK how you'd like me to proceed ?
I'm thinking:
- Leave ksft_wait()
- macro guard
- don't deploy helper to remote machine
SG!
linux-kselftest-mirror@lists.linaro.org