From: Jakub Kicinski <kuba@kernel.org>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, edumazet@google.com, pabeni@redhat.com,
andrew+netdev@lunn.ch, horms@kernel.org, shuah@kernel.org,
hawk@kernel.org, petrm@nvidia.com, jdamato@fastly.com,
willemdebruijn.kernel@gmail.com, Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH net-next 2/4] selftests: drv-net: add a way to wait for a local process
Date: Tue, 18 Feb 2025 11:50:46 -0800 [thread overview]
Message-ID: <20250218195048.74692-3-kuba@kernel.org> (raw)
In-Reply-To: <20250218195048.74692-1-kuba@kernel.org>
We use wait_port_listen() extensively to wait for a process
we spawned to be ready. Not all processes will open listening
sockets. Add a method of explicitly waiting for a child to
be ready. Pass a FD to the spawned process and wait for it
to write a message to us. FD number is passed via KSFT_READY_FD
env variable.
Make use of this method in the queues test to make it less flaky.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
.../selftests/drivers/net/xdp_helper.c | 22 ++++++-
tools/testing/selftests/drivers/net/queues.py | 46 ++++++---------
tools/testing/selftests/net/lib/py/utils.py | 58 +++++++++++++++++--
3 files changed, 93 insertions(+), 33 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/xdp_helper.c b/tools/testing/selftests/drivers/net/xdp_helper.c
index cf06a88b830b..8f77da4f798f 100644
--- a/tools/testing/selftests/drivers/net/xdp_helper.c
+++ b/tools/testing/selftests/drivers/net/xdp_helper.c
@@ -14,6 +14,25 @@
#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)
+ return;
+
+ fd = atoi(env_str);
+ if (!fd)
+ return;
+
+ write(fd, msg, sizeof(msg));
+ close(fd);
+}
+
/* this is a simple helper program that creates an XDP socket and does the
* minimum necessary to get bind() to succeed.
*
@@ -85,8 +104,7 @@ int main(int argc, char **argv)
return 1;
}
- /* give the parent program some data when the socket is ready*/
- fprintf(stdout, "%d\n", sock_fd);
+ ksft_ready();
/* parent program will write a byte to stdin when its ready for this
* helper to exit
diff --git a/tools/testing/selftests/drivers/net/queues.py b/tools/testing/selftests/drivers/net/queues.py
index b6896a57a5fd..91e344d108ee 100755
--- a/tools/testing/selftests/drivers/net/queues.py
+++ b/tools/testing/selftests/drivers/net/queues.py
@@ -5,13 +5,12 @@ from lib.py import ksft_disruptive, ksft_exit, ksft_run
from lib.py import ksft_eq, ksft_raises, KsftSkipEx, KsftFailEx
from lib.py import EthtoolFamily, NetdevFamily, NlError
from lib.py import NetDrvEnv
-from lib.py import cmd, defer, ip
+from lib.py import bkg, cmd, defer, ip
import errno
import glob
import os
import socket
import struct
-import subprocess
def sys_get_queues(ifname, qtype='rx') -> int:
folders = glob.glob(f'/sys/class/net/{ifname}/queues/{qtype}-*')
@@ -25,37 +24,30 @@ import subprocess
return None
def check_xdp(cfg, nl, xdp_queue_id=0) -> None:
- xdp = subprocess.Popen([cfg.rpath("xdp_helper"), f"{cfg.ifindex}", f"{xdp_queue_id}"],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1,
- text=True)
- defer(xdp.kill)
+ with bkg(f'{cfg.rpath("xdp_helper")} {cfg.ifindex} {xdp_queue_id}',
+ wait_init=3):
- stdout, stderr = xdp.communicate(timeout=10)
- rx = tx = False
+ rx = tx = False
- if xdp.returncode == 255:
- raise KsftSkipEx('AF_XDP unsupported')
- elif xdp.returncode > 0:
- raise KsftFailEx('unable to create AF_XDP socket')
+ queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True)
+ if not queues:
+ raise KsftSkipEx("Netlink reports no queues")
- queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True)
- if not queues:
- raise KsftSkipEx("Netlink reports no queues")
+ for q in queues:
+ if q['id'] == 0:
+ if q['type'] == 'rx':
+ rx = True
+ if q['type'] == 'tx':
+ tx = True
- for q in queues:
- if q['id'] == 0:
- if q['type'] == 'rx':
- rx = True
- if q['type'] == 'tx':
- tx = True
+ ksft_eq(q['xsk'], {})
+ else:
+ if 'xsk' in q:
+ _fail("Check failed: xsk attribute set.")
- ksft_eq(q['xsk'], {})
- else:
- if 'xsk' in q:
- _fail("Check failed: xsk attribute set.")
+ ksft_eq(rx, True)
+ ksft_eq(tx, True)
- ksft_eq(rx, True)
- ksft_eq(tx, True)
def get_queues(cfg, nl) -> None:
snl = NetdevFamily(recv_size=4096)
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 9e3bcddcf3e8..efb9b8fc1447 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -2,8 +2,10 @@
import errno
import json as _json
+import os
import random
import re
+import select
import socket
import subprocess
import time
@@ -15,8 +17,22 @@ import time
self.cmd = cmd_obj
+def fd_read_timeout(fd, timeout):
+ rlist, _, _ = select.select([fd], [], [], timeout)
+ if rlist:
+ return os.read(fd, 1024)
+ else:
+ raise TimeoutError("Timeout waiting for fd read")
+
+
class cmd:
- def __init__(self, comm, shell=True, fail=True, ns=None, background=False, host=None, timeout=5):
+ """
+ Execute a command on local or remote host.
+
+ Use bkg() instead to run a command in the background.
+ """
+ def __init__(self, comm, shell=True, fail=True, ns=None, background=False,
+ host=None, timeout=5, wait_init=None):
if ns:
comm = f'ip netns exec {ns} ' + comm
@@ -28,8 +44,23 @@ import time
if host:
self.proc = host.cmd(comm)
else:
+ # wait_init lets us wait for the background process to fully start,
+ # we pass an FD to the child process, and wait for it to write back
+ pass_fds = ()
+ env = os.environ.copy()
+ if wait_init is not None:
+ rfd, wfd = os.pipe()
+ pass_fds = (wfd, )
+ env["KSFT_READY_FD"] = str(wfd)
self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stderr=subprocess.PIPE, pass_fds=pass_fds,
+ env=env)
+ if wait_init is not None:
+ os.close(wfd)
+ msg = fd_read_timeout(rfd, wait_init)
+ os.close(rfd)
+ if not msg:
+ raise Exception("Did not receive ready message")
if not background:
self.process(terminate=False, fail=fail, timeout=timeout)
@@ -54,10 +85,29 @@ import time
class bkg(cmd):
+ """
+ Run a command in the background.
+
+ Examples usage:
+
+ Run a command on remote host, and wait for it to finish.
+ This is usually paired with wait_port_listen() to make sure
+ the command has initialized:
+
+ with bkg("socat ...", exit_wait=True, host=cfg.remote) as nc:
+ ...
+
+ Run a command and expect it to let us know that it's ready
+ by writing to a special file decriptor passed via KSFT_READY_FD.
+ Command will be terminated when we exit the context manager:
+
+ with bkg("my_binary", wait_init=5):
+ """
def __init__(self, comm, shell=True, fail=None, ns=None, host=None,
- exit_wait=False):
+ exit_wait=False, wait_init=None):
super().__init__(comm, background=True,
- shell=shell, fail=fail, ns=ns, host=host)
+ shell=shell, fail=fail, ns=ns, host=host,
+ wait_init=wait_init)
self.terminate = not exit_wait
self.check_fail = fail
--
2.48.1
next prev parent reply other threads:[~2025-02-18 19:50 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-18 19:50 [PATCH net-next 0/4] selftests: drv-net: improve the queue test for XSK Jakub Kicinski
2025-02-18 19:50 ` [PATCH net-next 1/4] selftests: drv-net: use cfg.rpath() in netlink xsk attr test Jakub Kicinski
2025-02-18 21:24 ` Joe Damato
2025-02-18 19:50 ` Jakub Kicinski [this message]
2025-02-18 21:10 ` [PATCH net-next 2/4] selftests: drv-net: add a way to wait for a local process Stanislav Fomichev
2025-02-18 21:21 ` Jakub Kicinski
2025-02-18 21:29 ` Stanislav Fomichev
2025-02-18 21:52 ` Joe Damato
2025-02-18 23:05 ` Jakub Kicinski
2025-02-19 1:37 ` Jakub Kicinski
2025-02-19 18:40 ` Joe Damato
2025-02-19 18:39 ` Joe Damato
2025-02-19 22:48 ` Jakub Kicinski
2025-02-20 17:45 ` Joe Damato
2025-02-18 19:50 ` [PATCH net-next 3/4] selftests: drv-net: improve the use of ksft helpers in XSK queue test Jakub Kicinski
2025-02-18 21:25 ` Joe Damato
2025-02-18 19:50 ` [PATCH net-next 4/4] selftests: drv-net: rename queues check_xdp to check_xsk Jakub Kicinski
2025-02-18 21:25 ` Joe Damato
2025-02-18 21:29 ` [PATCH net-next 0/4] selftests: drv-net: improve the queue test for XSK Stanislav Fomichev
2025-02-18 21:55 ` Joe Damato
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250218195048.74692-3-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hawk@kernel.org \
--cc=horms@kernel.org \
--cc=jdamato@fastly.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=petrm@nvidia.com \
--cc=shuah@kernel.org \
--cc=willemdebruijn.kernel@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.