From: Bobby Eshleman bobbyeshleman@meta.com
Add -A flag to ncdevmem to set autorelease mode.
Add tests for the SO_DEVMEM_AUTORELEASE socket option:
New tests include: - check_sockopt_autorelease_default: Verifies default value is 0 - check_sockopt_autorelease_set_0: Tests setting to 0 and reading back - check_sockopt_autorelease_set_1: Tests toggling from 0 to 1 - check_sockopt_autorelease_invalid: Tests invalid value (2) returns EINVAL - check_autorelease_disabled: Tests ncdevmem in manual token release mode - check_autorelease_enabled: Tests ncdevmem in autorelease mode
All check_sockopt tests gracefully skip with KsftSkipEx if SO_DEVMEM_AUTORELEASE is not supported by the kernel.
Signed-off-by: Bobby Eshleman bobbyeshleman@meta.com --- tools/testing/selftests/drivers/net/hw/devmem.py | 115 +++++++++++++++++++++- tools/testing/selftests/drivers/net/hw/ncdevmem.c | 20 +++- 2 files changed, 133 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py index 45c2d49d55b6..29ec179d651f 100755 --- a/tools/testing/selftests/drivers/net/hw/devmem.py +++ b/tools/testing/selftests/drivers/net/hw/devmem.py @@ -1,6 +1,9 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0
+import socket +import errno + from os import path from lib.py import ksft_run, ksft_exit from lib.py import ksft_eq, KsftSkipEx @@ -63,12 +66,122 @@ def check_tx_chunks(cfg) -> None: ksft_eq(socat.stdout.strip(), "hello\nworld")
+@ksft_disruptive +def check_autorelease_disabled(cfg) -> None: + """Test RX with autorelease disabled (requires manual token release in ncdevmem)""" + require_devmem(cfg) + + port = rand_port() + socat = f"socat -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},bind={cfg.remote_baddr}:{port}" + listen_cmd = f"{cfg.bin_local} -l -f {cfg.ifname} -s {cfg.addr} -p {port} -c {cfg.remote_addr} -v 7 -A 0" + + with bkg(listen_cmd, exit_wait=True) as ncdevmem: + wait_port_listen(port) + cmd(f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | \ + head -c 1K | {socat}", host=cfg.remote, shell=True) + + ksft_eq(ncdevmem.ret, 0) + + +@ksft_disruptive +def check_autorelease_enabled(cfg) -> None: + """Test RX with autorelease enabled (requires token autorelease in ncdevmem)""" + require_devmem(cfg) + + port = rand_port() + socat = f"socat -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},bind={cfg.remote_baddr}:{port}" + listen_cmd = f"{cfg.bin_local} -l -f {cfg.ifname} -s {cfg.addr} -p {port} -c {cfg.remote_addr} -v 7 -A 1" + + with bkg(listen_cmd, exit_wait=True) as ncdevmem: + wait_port_listen(port) + cmd(f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | \ + head -c 1K | {socat}", host=cfg.remote, shell=True) + + ksft_eq(ncdevmem.ret, 0) + + +def check_sockopt_autorelease_default(cfg) -> None: + """Test that SO_DEVMEM_AUTORELEASE default is 0""" + SO_DEVMEM_AUTORELEASE = 85 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + val = sock.getsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE) + ksft_eq(val, 0, "Default autorelease should be 0") + except OSError as e: + if e.errno == errno.ENOPROTOOPT: + raise KsftSkipEx("SO_DEVMEM_AUTORELEASE not supported") + raise + finally: + sock.close() + + +def check_sockopt_autorelease_set_0(cfg) -> None: + """Test setting SO_DEVMEM_AUTORELEASE to 0""" + SO_DEVMEM_AUTORELEASE = 85 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.setsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE, 0) + val = sock.getsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE) + ksft_eq(val, 0, "Autorelease should be 0 after setting") + except OSError as e: + if e.errno == errno.ENOPROTOOPT: + raise KsftSkipEx("SO_DEVMEM_AUTORELEASE not supported") + raise + finally: + sock.close() + + +def check_sockopt_autorelease_set_1(cfg) -> None: + """Test setting SO_DEVMEM_AUTORELEASE to 1""" + SO_DEVMEM_AUTORELEASE = 85 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # First set to 0 + sock.setsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE, 0) + # Then set back to 1 + sock.setsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE, 1) + val = sock.getsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE) + ksft_eq(val, 1, "Autorelease should be 1 after setting") + except OSError as e: + if e.errno == errno.ENOPROTOOPT: + raise KsftSkipEx("SO_DEVMEM_AUTORELEASE not supported") + raise + finally: + sock.close() + + +def check_sockopt_autorelease_invalid(cfg) -> None: + """Test that SO_DEVMEM_AUTORELEASE rejects invalid values""" + SO_DEVMEM_AUTORELEASE = 85 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + try: + sock.setsockopt(socket.SOL_SOCKET, SO_DEVMEM_AUTORELEASE, 2) + raise Exception("setsockopt should have failed with EINVAL") + except OSError as e: + if e.errno == errno.ENOPROTOOPT: + raise KsftSkipEx("SO_DEVMEM_AUTORELEASE not supported") + ksft_eq(e.errno, errno.EINVAL, "Should fail with EINVAL for invalid value") + finally: + sock.close() + + def main() -> None: with NetDrvEpEnv(__file__) as cfg: cfg.bin_local = path.abspath(path.dirname(__file__) + "/ncdevmem") cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
- ksft_run([check_rx, check_tx, check_tx_chunks], + ksft_run([check_rx, check_tx, check_tx_chunks, + check_autorelease_enabled, + check_autorelease_disabled, + check_sockopt_autorelease_default, + check_sockopt_autorelease_set_0, + check_sockopt_autorelease_set_1, + check_sockopt_autorelease_invalid], args=(cfg, )) ksft_exit()
diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c index 3288ed04ce08..34d608d07bec 100644 --- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c +++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c @@ -83,6 +83,10 @@ #define MSG_SOCK_DEVMEM 0x2000000 #endif
+#ifndef SO_DEVMEM_AUTORELEASE +#define SO_DEVMEM_AUTORELEASE 85 +#endif + #define MAX_IOV 1024
static size_t max_chunk; @@ -97,6 +101,7 @@ static unsigned int ifindex; static unsigned int dmabuf_id; static uint32_t tx_dmabuf_id; static int waittime_ms = 500; +static int autorelease = -1;
/* System state loaded by current_config_load() */ #define MAX_FLOWS 8 @@ -890,6 +895,16 @@ static int do_server(struct memory_buffer *mem) if (enable_reuseaddr(socket_fd)) goto err_close_socket;
+ if (autorelease >= 0) { + ret = setsockopt(socket_fd, SOL_SOCKET, SO_DEVMEM_AUTORELEASE, + &autorelease, sizeof(autorelease)); + if (ret) { + pr_err("SO_DEVMEM_AUTORELEASE failed"); + goto err_close_socket; + } + fprintf(stderr, "Set SO_DEVMEM_AUTORELEASE to %d\n", autorelease); + } + fprintf(stderr, "binding to address %s:%d\n", server_ip, ntohs(server_sin.sin6_port));
@@ -1397,7 +1412,7 @@ int main(int argc, char *argv[]) int is_server = 0, opt; int ret, err = 1;
- while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:z:")) != -1) { + while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:z:A:")) != -1) { switch (opt) { case 'l': is_server = 1; @@ -1426,6 +1441,9 @@ int main(int argc, char *argv[]) case 'z': max_chunk = atoi(optarg); break; + case 'A': + autorelease = atoi(optarg); + break; case '?': fprintf(stderr, "unknown option: %c\n", optopt); break;