Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'selftests-drv-net-improve-the-queue-test-for-xsk'

Jakub Kicinski says:

====================
selftests: drv-net: improve the queue test for XSK

We see some flakes in the the XSK test:

Exception| Traceback (most recent call last):
Exception| File "/home/virtme/testing-18/tools/testing/selftests/net/lib/py/ksft.py", line 218, in ksft_run
Exception| case(*args)
Exception| File "/home/virtme/testing-18/tools/testing/selftests/drivers/net/./queues.py", line 53, in check_xdp
Exception| ksft_eq(q['xsk'], {})
Exception| KeyError: 'xsk'

I think it's because the method of running the helper in the background
is racy. Add more solid infra for waiting for a background helper to be
initialized.

v1: https://lore.kernel.org/20250218195048.74692-1-kuba@kernel.org
====================

Link: https://patch.msgid.link/20250219234956.520599-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+161 -40
+31 -30
tools/testing/selftests/drivers/net/queues.py
··· 2 2 # SPDX-License-Identifier: GPL-2.0 3 3 4 4 from lib.py import ksft_disruptive, ksft_exit, ksft_run 5 - from lib.py import ksft_eq, ksft_raises, KsftSkipEx, KsftFailEx 5 + from lib.py import ksft_eq, ksft_not_in, ksft_raises, KsftSkipEx, KsftFailEx 6 6 from lib.py import EthtoolFamily, NetdevFamily, NlError 7 7 from lib.py import NetDrvEnv 8 - from lib.py import cmd, defer, ip 8 + from lib.py import bkg, cmd, defer, ip 9 9 import errno 10 10 import glob 11 11 import os 12 12 import socket 13 13 import struct 14 - import subprocess 15 14 16 15 def sys_get_queues(ifname, qtype='rx') -> int: 17 16 folders = glob.glob(f'/sys/class/net/{ifname}/queues/{qtype}-*') ··· 23 24 return len([q for q in queues if q['type'] == qtype]) 24 25 return None 25 26 26 - def check_xdp(cfg, nl, xdp_queue_id=0) -> None: 27 - test_dir = os.path.dirname(os.path.realpath(__file__)) 28 - xdp = subprocess.Popen([f"{test_dir}/xdp_helper", f"{cfg.ifindex}", f"{xdp_queue_id}"], 29 - stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1, 30 - text=True) 31 - defer(xdp.kill) 32 27 33 - stdout, stderr = xdp.communicate(timeout=10) 34 - rx = tx = False 35 - 36 - if xdp.returncode == 255: 28 + def check_xsk(cfg, nl, xdp_queue_id=0) -> None: 29 + # Probe for support 30 + xdp = cmd(cfg.rpath("xdp_helper") + ' - -', fail=False) 31 + if xdp.ret == 255: 37 32 raise KsftSkipEx('AF_XDP unsupported') 38 - elif xdp.returncode > 0: 33 + elif xdp.ret > 0: 39 34 raise KsftFailEx('unable to create AF_XDP socket') 40 35 41 - queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 42 - if not queues: 43 - raise KsftSkipEx("Netlink reports no queues") 36 + with bkg(f'{cfg.rpath("xdp_helper")} {cfg.ifindex} {xdp_queue_id}', 37 + ksft_wait=3): 44 38 45 - for q in queues: 46 - if q['id'] == 0: 47 - if q['type'] == 'rx': 48 - rx = True 49 - if q['type'] == 'tx': 50 - tx = True 39 + rx = tx = False 51 40 52 - ksft_eq(q['xsk'], {}) 53 - else: 54 - if 'xsk' in q: 55 - _fail("Check failed: xsk attribute set.") 41 + queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 42 + if not queues: 43 + raise KsftSkipEx("Netlink reports no queues") 56 44 57 - ksft_eq(rx, True) 58 - ksft_eq(tx, True) 45 + for q in queues: 46 + if q['id'] == 0: 47 + if q['type'] == 'rx': 48 + rx = True 49 + if q['type'] == 'tx': 50 + tx = True 51 + 52 + ksft_eq(q.get('xsk', None), {}, 53 + comment="xsk attr on queue we configured") 54 + else: 55 + ksft_not_in('xsk', q, 56 + comment="xsk attr on queue we didn't configure") 57 + 58 + ksft_eq(rx, True) 59 + ksft_eq(tx, True) 60 + 59 61 60 62 def get_queues(cfg, nl) -> None: 61 63 snl = NetdevFamily(recv_size=4096) ··· 117 117 118 118 def main() -> None: 119 119 with NetDrvEnv(__file__, queue_count=100) as cfg: 120 - ksft_run([get_queues, addremove_queues, check_down, check_xdp], args=(cfg, NetdevFamily())) 120 + ksft_run([get_queues, addremove_queues, check_down, check_xsk], 121 + args=(cfg, NetdevFamily())) 121 122 ksft_exit() 122 123 123 124
+58 -5
tools/testing/selftests/drivers/net/xdp_helper.c
··· 14 14 #define UMEM_SZ (1U << 16) 15 15 #define NUM_DESC (UMEM_SZ / 2048) 16 16 17 + /* Move this to a common header when reused! */ 18 + static void ksft_ready(void) 19 + { 20 + const char msg[7] = "ready\n"; 21 + char *env_str; 22 + int fd; 23 + 24 + env_str = getenv("KSFT_READY_FD"); 25 + if (env_str) { 26 + fd = atoi(env_str); 27 + if (!fd) { 28 + fprintf(stderr, "invalid KSFT_READY_FD = '%s'\n", 29 + env_str); 30 + return; 31 + } 32 + } else { 33 + fd = STDOUT_FILENO; 34 + } 35 + 36 + write(fd, msg, sizeof(msg)); 37 + if (fd != STDOUT_FILENO) 38 + close(fd); 39 + } 40 + 41 + static void ksft_wait(void) 42 + { 43 + char *env_str; 44 + char byte; 45 + int fd; 46 + 47 + env_str = getenv("KSFT_WAIT_FD"); 48 + if (env_str) { 49 + fd = atoi(env_str); 50 + if (!fd) { 51 + fprintf(stderr, "invalid KSFT_WAIT_FD = '%s'\n", 52 + env_str); 53 + return; 54 + } 55 + } else { 56 + /* Not running in KSFT env, wait for input from STDIN instead */ 57 + fd = STDIN_FILENO; 58 + } 59 + 60 + read(fd, &byte, sizeof(byte)); 61 + if (fd != STDIN_FILENO) 62 + close(fd); 63 + } 64 + 17 65 /* this is a simple helper program that creates an XDP socket and does the 18 66 * minimum necessary to get bind() to succeed. 19 67 * ··· 80 32 int ifindex; 81 33 int sock_fd; 82 34 int queue; 83 - char byte; 84 35 85 36 if (argc != 3) { 86 - fprintf(stderr, "Usage: %s ifindex queue_id", argv[0]); 37 + fprintf(stderr, "Usage: %s ifindex queue_id\n", argv[0]); 87 38 return 1; 88 39 } 89 40 ··· 95 48 if (errno == EAFNOSUPPORT) 96 49 return -1; 97 50 return 1; 51 + } 52 + 53 + /* "Probing mode", just checking if AF_XDP sockets are supported */ 54 + if (!strcmp(argv[1], "-") && !strcmp(argv[2], "-")) { 55 + printf("AF_XDP support detected\n"); 56 + close(sock_fd); 57 + return 0; 98 58 } 99 59 100 60 ifindex = atoi(argv[1]); ··· 139 85 return 1; 140 86 } 141 87 142 - /* give the parent program some data when the socket is ready*/ 143 - fprintf(stdout, "%d\n", sock_fd); 88 + ksft_ready(); 89 + ksft_wait(); 144 90 145 91 /* parent program will write a byte to stdin when its ready for this 146 92 * helper to exit 147 93 */ 148 - read(STDIN_FILENO, &byte, 1); 149 94 150 95 close(sock_fd); 151 96 return 0;
+5
tools/testing/selftests/net/lib/py/ksft.py
··· 71 71 _fail("Check failed", a, "not in", b, comment) 72 72 73 73 74 + def ksft_not_in(a, b, comment=""): 75 + if a in b: 76 + _fail("Check failed", a, "in", b, comment) 77 + 78 + 74 79 def ksft_is(a, b, comment=""): 75 80 if a is not b: 76 81 _fail("Check failed", a, "is not", b, comment)
+67 -5
tools/testing/selftests/net/lib/py/utils.py
··· 2 2 3 3 import errno 4 4 import json as _json 5 + import os 5 6 import random 6 7 import re 8 + import select 7 9 import socket 8 10 import subprocess 9 11 import time ··· 17 15 self.cmd = cmd_obj 18 16 19 17 18 + def fd_read_timeout(fd, timeout): 19 + rlist, _, _ = select.select([fd], [], [], timeout) 20 + if rlist: 21 + return os.read(fd, 1024) 22 + else: 23 + raise TimeoutError("Timeout waiting for fd read") 24 + 25 + 20 26 class cmd: 21 - def __init__(self, comm, shell=True, fail=True, ns=None, background=False, host=None, timeout=5): 27 + """ 28 + Execute a command on local or remote host. 29 + 30 + Use bkg() instead to run a command in the background. 31 + """ 32 + def __init__(self, comm, shell=True, fail=True, ns=None, background=False, 33 + host=None, timeout=5, ksft_wait=None): 22 34 if ns: 23 35 comm = f'ip netns exec {ns} ' + comm 24 36 25 37 self.stdout = None 26 38 self.stderr = None 27 39 self.ret = None 40 + self.ksft_term_fd = None 28 41 29 42 self.comm = comm 30 43 if host: 31 44 self.proc = host.cmd(comm) 32 45 else: 46 + # ksft_wait lets us wait for the background process to fully start, 47 + # we pass an FD to the child process, and wait for it to write back. 48 + # Similarly term_fd tells child it's time to exit. 49 + pass_fds = () 50 + env = os.environ.copy() 51 + if ksft_wait is not None: 52 + rfd, ready_fd = os.pipe() 53 + wait_fd, self.ksft_term_fd = os.pipe() 54 + pass_fds = (ready_fd, wait_fd, ) 55 + env["KSFT_READY_FD"] = str(ready_fd) 56 + env["KSFT_WAIT_FD"] = str(wait_fd) 57 + 33 58 self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, 34 - stderr=subprocess.PIPE) 59 + stderr=subprocess.PIPE, pass_fds=pass_fds, 60 + env=env) 61 + if ksft_wait is not None: 62 + os.close(ready_fd) 63 + os.close(wait_fd) 64 + msg = fd_read_timeout(rfd, ksft_wait) 65 + os.close(rfd) 66 + if not msg: 67 + raise Exception("Did not receive ready message") 35 68 if not background: 36 69 self.process(terminate=False, fail=fail, timeout=timeout) 37 70 ··· 74 37 if fail is None: 75 38 fail = not terminate 76 39 40 + if self.ksft_term_fd: 41 + os.write(self.ksft_term_fd, b"1") 77 42 if terminate: 78 43 self.proc.terminate() 79 44 stdout, stderr = self.proc.communicate(timeout) ··· 93 54 94 55 95 56 class bkg(cmd): 57 + """ 58 + Run a command in the background. 59 + 60 + Examples usage: 61 + 62 + Run a command on remote host, and wait for it to finish. 63 + This is usually paired with wait_port_listen() to make sure 64 + the command has initialized: 65 + 66 + with bkg("socat ...", exit_wait=True, host=cfg.remote) as nc: 67 + ... 68 + 69 + Run a command and expect it to let us know that it's ready 70 + by writing to a special file descriptor passed via KSFT_READY_FD. 71 + Command will be terminated when we exit the context manager: 72 + 73 + with bkg("my_binary", ksft_wait=5): 74 + """ 96 75 def __init__(self, comm, shell=True, fail=None, ns=None, host=None, 97 - exit_wait=False): 76 + exit_wait=False, ksft_wait=None): 98 77 super().__init__(comm, background=True, 99 - shell=shell, fail=fail, ns=ns, host=host) 100 - self.terminate = not exit_wait 78 + shell=shell, fail=fail, ns=ns, host=host, 79 + ksft_wait=ksft_wait) 80 + self.terminate = not exit_wait and not ksft_wait 101 81 self.check_fail = fail 82 + 83 + if shell and self.terminate: 84 + print("# Warning: combining shell and terminate is risky!") 85 + print("# SIGTERM may not reach the child on zsh/ksh!") 102 86 103 87 def __enter__(self): 104 88 return self