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

selftests: net: py: support ksft ready without wait

There's a common synchronization problem when a script (Python test)
uses a C program to set up some state (usually start a receiving
process for traffic). The script needs to know when the process
has fully initialized. The inverse of the problem exists for shutting
the process down - we need a reliable way to tell the process to exit.

We added helpers to do this safely in
commit 71477137994f ("selftests: drv-net: add a way to wait for a local process")
unfortunately the two operations (wait for init, and shutdown) are
controlled by a single parameter (ksft_wait). Add support for using
ksft_ready without using the second fd for exit.

This is useful for programs which wait for a specific number of packets
to rx so exit_wait is a good match, but we still need to wait for init.

Reviewed-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20251120021024.2944527-7-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+12 -8
+12 -8
tools/testing/selftests/net/lib/py/utils.py
··· 32 32 Use bkg() instead to run a command in the background. 33 33 """ 34 34 def __init__(self, comm, shell=None, fail=True, ns=None, background=False, 35 - host=None, timeout=5, ksft_wait=None): 35 + host=None, timeout=5, ksft_ready=None, ksft_wait=None): 36 36 if ns: 37 37 comm = f'ip netns exec {ns} ' + comm 38 38 ··· 52 52 # ksft_wait lets us wait for the background process to fully start, 53 53 # we pass an FD to the child process, and wait for it to write back. 54 54 # Similarly term_fd tells child it's time to exit. 55 - pass_fds = () 55 + pass_fds = [] 56 56 env = os.environ.copy() 57 57 if ksft_wait is not None: 58 - rfd, ready_fd = os.pipe() 59 58 wait_fd, self.ksft_term_fd = os.pipe() 60 - pass_fds = (ready_fd, wait_fd, ) 61 - env["KSFT_READY_FD"] = str(ready_fd) 59 + pass_fds.append(wait_fd) 62 60 env["KSFT_WAIT_FD"] = str(wait_fd) 61 + ksft_ready = True # ksft_wait implies ready 62 + if ksft_ready is not None: 63 + rfd, ready_fd = os.pipe() 64 + pass_fds.append(ready_fd) 65 + env["KSFT_READY_FD"] = str(ready_fd) 63 66 64 67 self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, 65 68 stderr=subprocess.PIPE, pass_fds=pass_fds, 66 69 env=env) 67 70 if ksft_wait is not None: 68 - os.close(ready_fd) 69 71 os.close(wait_fd) 72 + if ksft_ready is not None: 73 + os.close(ready_fd) 70 74 msg = fd_read_timeout(rfd, ksft_wait) 71 75 os.close(rfd) 72 76 if not msg: ··· 120 116 with bkg("my_binary", ksft_wait=5): 121 117 """ 122 118 def __init__(self, comm, shell=None, fail=None, ns=None, host=None, 123 - exit_wait=False, ksft_wait=None): 119 + exit_wait=False, ksft_ready=None, ksft_wait=None): 124 120 super().__init__(comm, background=True, 125 121 shell=shell, fail=fail, ns=ns, host=host, 126 - ksft_wait=ksft_wait) 122 + ksft_ready=ksft_ready, ksft_wait=ksft_wait) 127 123 self.terminate = not exit_wait and not ksft_wait 128 124 self._exit_wait = exit_wait 129 125 self.check_fail = fail