* [PATCH net-next v2] selftests/net: convert so_txtime to drv-net
@ 2026-04-05 1:44 Willem de Bruijn
2026-04-05 2:20 ` Willem de Bruijn
0 siblings, 1 reply; 4+ messages in thread
From: Willem de Bruijn @ 2026-04-05 1:44 UTC (permalink / raw)
To: netdev; +Cc: davem, kuba, edumazet, pabeni, horms, Willem de Bruijn
From: Willem de Bruijn <willemb@google.com>
In preparation for extending to pacing hardware offload, convert the
so_txtime.sh test to a drv-net test that can be run against netdevsim
and real hardware.
Also update so_txtime.c to not exit on first failure, but run to
completion and report exit code there. This helps with debugging
unexpected results, especially when processing multiple packets,
as in the "reverse_order" testcase.
Signed-off-by: Willem de Bruijn <willemb@google.com>
----
v1 -> v2
- move so_txtime.c for net/lib to drivers/net (Jakub)
- fix drivers/net/config order (Jakub)
- detect passing when failure is expected (Jakub, Sashiko)
- pass pylint --disable=R (Jakub)
- only call ksft_run once (Jakub)
- do not sleep if waiting time is negative (Sashiko)
- add \n when converting error() to fprintf() (Sashiko)
- 4 space indentation, instead of 2 space
- increase sync delay from 100 to 200ms, to fix rare vng flakes
v1: https://lore.kernel.org/netdev/20260403175047.152646-1-willemdebruijn.kernel@gmail.com/
---
.../testing/selftests/drivers/net/.gitignore | 1 +
tools/testing/selftests/drivers/net/Makefile | 5 +-
tools/testing/selftests/drivers/net/config | 2 +
.../selftests/{ => drivers}/net/so_txtime.c | 24 +++-
.../selftests/drivers/net/so_txtime.py | 88 ++++++++++++++
tools/testing/selftests/net/.gitignore | 1 -
tools/testing/selftests/net/Makefile | 2 -
tools/testing/selftests/net/so_txtime.sh | 110 ------------------
8 files changed, 114 insertions(+), 119 deletions(-)
rename tools/testing/selftests/{ => drivers}/net/so_txtime.c (96%)
create mode 100755 tools/testing/selftests/drivers/net/so_txtime.py
delete mode 100755 tools/testing/selftests/net/so_txtime.sh
diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore
index 585ecb4d5dc4..e5314ce4bb2d 100644
--- a/tools/testing/selftests/drivers/net/.gitignore
+++ b/tools/testing/selftests/drivers/net/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
napi_id_helper
psp_responder
+so_txtime
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 7c7fa75b80c2..85d6a7e26627 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -20,12 +20,15 @@ TEST_PROGS := \
queues.py \
ring_reconfig.py \
shaper.py \
+ so_txtime.py \
stats.py \
xdp.py \
# end of TEST_PROGS
# YNL files, must be before "include ..lib.mk"
-YNL_GEN_FILES := psp_responder
+YNL_GEN_FILES := \
+ psp_responder \
+ so_txtime
TEST_GEN_FILES += $(YNL_GEN_FILES)
include ../../lib.mk
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index 77ccf83d87e0..2d39f263e03b 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -7,4 +7,6 @@ CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_FQ=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c
similarity index 96%
rename from tools/testing/selftests/net/so_txtime.c
rename to tools/testing/selftests/drivers/net/so_txtime.c
index b76df1efc2ef..7d144001ecf2 100644
--- a/tools/testing/selftests/net/so_txtime.c
+++ b/tools/testing/selftests/drivers/net/so_txtime.c
@@ -33,6 +33,8 @@
#include <unistd.h>
#include <poll.h>
+#include "kselftest.h"
+
static int cfg_clockid = CLOCK_TAI;
static uint16_t cfg_port = 8000;
static int cfg_variance_us = 4000;
@@ -43,6 +45,8 @@ static bool cfg_rx;
static uint64_t glob_tstart;
static uint64_t tdeliver_max;
+static int errors;
+
/* encode one timed transmission (of a 1B payload) */
struct timed_send {
char data;
@@ -131,13 +135,15 @@ static void do_recv_one(int fdr, struct timed_send *ts)
fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
rbuf[0], (long long)tstop, (long long)texpect);
- if (rbuf[0] != ts->data)
- error(1, 0, "payload mismatch. expected %c", ts->data);
+ if (rbuf[0] != ts->data) {
+ fprintf(stderr, "payload mismatch. expected %c\n", ts->data);
+ errors++;
+ }
if (llabs(tstop - texpect) > cfg_variance_us) {
fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us);
if (!getenv("KSFT_MACHINE_SLOW"))
- exit(1);
+ errors++;
}
}
@@ -255,8 +261,11 @@ static void start_time_wait(void)
return;
now = gettime_ns(CLOCK_REALTIME);
- if (cfg_start_time_ns < now)
+ if (cfg_start_time_ns < now) {
+ fprintf(stderr, "FAIL: start time already passed\n");
+ errors++;
return;
+ }
err = usleep((cfg_start_time_ns - now) / 1000);
if (err)
@@ -513,5 +522,10 @@ int main(int argc, char **argv)
else
do_test_tx((void *)&cfg_src_addr, cfg_alen);
- return 0;
+ if (errors) {
+ fprintf(stderr, "FAIL: %d errors\n", errors);
+ return KSFT_FAIL;
+ }
+
+ return KSFT_PASS;
}
diff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py
new file mode 100755
index 000000000000..31b95a7b5b8b
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/so_txtime.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Regression tests for the SO_TXTIME interface.
+
+Test delivery time in FQ and ETF qdiscs.
+"""
+
+import time
+
+from lib.py import ksft_exit, ksft_run, ksft_variants
+from lib.py import KsftNamedVariant, KsftSkipEx
+from lib.py import NetDrvEpEnv, bkg, cmd
+
+
+def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_fail):
+ """Main function. Run so_txtime as sender and receiver."""
+ bin_path = cfg.test_dir / "so_txtime"
+
+ tstart = time.time_ns() + 200_000_000
+
+ cmd_addr = f"-S {cfg.addr_v[ipver]} -D {cfg.remote_addr_v[ipver]}"
+ cmd_base = f"{bin_path} -{ipver} -c {clockid} -t {tstart} {cmd_addr}"
+ cmd_rx = f"{cmd_base} {args_rx} -r"
+ cmd_tx = f"{cmd_base} {args_tx}"
+
+ with bkg(cmd_rx, host=cfg.remote, fail=(not expect_fail), exit_wait=True):
+ cmd(cmd_tx)
+
+
+def _test_variants_mono():
+ for ipver in ["4", "6"]:
+ for testcase in [
+ ["no_delay", "a,-1", "a,-1"],
+ ["zero_delay", "a,0", "a,0"],
+ ["one_pkt", "a,10", "a,10"],
+ ["in_order", "a,10,b,20", "a,10,b,20"],
+ ["reverse_order", "a,20,b,10", "b,20,a,20"],
+ ]:
+ name = f"_v{ipver}_{testcase[0]}"
+ yield KsftNamedVariant(name, ipver, testcase[1], testcase[2])
+
+
+@ksft_variants(_test_variants_mono())
+def test_so_txtime_mono(cfg, ipver, args_tx, args_rx):
+ """Run all variants of monotonic (fq) tests."""
+ cmd(f"tc qdisc replace dev {cfg.ifname} root fq")
+ test_so_txtime(cfg, "mono", ipver, args_tx, args_rx, False)
+
+
+def _test_variants_etf():
+ for ipver in ["4", "6"]:
+ for testcase in [
+ ["no_delay", "a,-1", "a,-1", True],
+ ["zero_delay", "a,0", "a,0", True],
+ ["one_pkt", "a,10", "a,10", False],
+ ["in_order", "a,10,b,20", "a,10,b,20", False],
+ ["reverse_order", "a,20,b,10", "b,10,a,20", False],
+ ]:
+ name = f"_v{ipver}_{testcase[0]}"
+ yield KsftNamedVariant(
+ name, ipver, testcase[1], testcase[2], testcase[3]
+ )
+
+
+@ksft_variants(_test_variants_etf())
+def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail):
+ """Run all variants of etf tests."""
+ try:
+ # ETF does not support change, so remove and re-add it instead.
+ cmd_prefix = f"tc qdisc replace dev {cfg.ifname} root"
+ cmd(f"{cmd_prefix} pfifo_fast")
+ cmd(f"{cmd_prefix} etf clockid CLOCK_TAI delta 400000")
+ except Exception as e:
+ raise KsftSkipEx("tc does not support qdisc etf. skipping") from e
+
+ test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, expect_fail)
+
+
+def main() -> None:
+ """Boilerplate ksft main."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_so_txtime_mono, test_so_txtime_etf], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 97ad4d551d44..02ad4c99a2b4 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -40,7 +40,6 @@ skf_net_off
socket
so_incoming_cpu
so_netns_cookie
-so_txtime
so_rcv_listener
stress_reuseport_listen
tap
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 6bced3ed798b..b7f51e8f190f 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -81,7 +81,6 @@ TEST_PROGS := \
rxtimestamp.sh \
sctp_vrf.sh \
skf_net_off.sh \
- so_txtime.sh \
srv6_end_dt46_l3vpn_test.sh \
srv6_end_dt4_l3vpn_test.sh \
srv6_end_dt6_l3vpn_test.sh \
@@ -154,7 +153,6 @@ TEST_GEN_FILES := \
skf_net_off \
so_netns_cookie \
so_rcv_listener \
- so_txtime \
socket \
stress_reuseport_listen \
tcp_fastopen_backup_key \
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
deleted file mode 100755
index 5e861ad32a42..000000000000
--- a/tools/testing/selftests/net/so_txtime.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Regression tests for the SO_TXTIME interface
-
-set -e
-
-readonly ksft_skip=4
-readonly DEV="veth0"
-readonly BIN="./so_txtime"
-
-readonly RAND="$(mktemp -u XXXXXX)"
-readonly NSPREFIX="ns-${RAND}"
-readonly NS1="${NSPREFIX}1"
-readonly NS2="${NSPREFIX}2"
-
-readonly SADDR4='192.168.1.1'
-readonly DADDR4='192.168.1.2'
-readonly SADDR6='fd::1'
-readonly DADDR6='fd::2'
-
-cleanup() {
- ip netns del "${NS2}"
- ip netns del "${NS1}"
-}
-
-trap cleanup EXIT
-
-# Create virtual ethernet pair between network namespaces
-ip netns add "${NS1}"
-ip netns add "${NS2}"
-
-ip link add "${DEV}" netns "${NS1}" type veth \
- peer name "${DEV}" netns "${NS2}"
-
-# Bring the devices up
-ip -netns "${NS1}" link set "${DEV}" up
-ip -netns "${NS2}" link set "${DEV}" up
-
-# Set fixed MAC addresses on the devices
-ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02
-ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06
-
-# Add fixed IP addresses to the devices
-ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}"
-ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}"
-ip -netns "${NS1}" addr add fd::1/64 dev "${DEV}" nodad
-ip -netns "${NS2}" addr add fd::2/64 dev "${DEV}" nodad
-
-run_test() {
- local readonly IP="$1"
- local readonly CLOCK="$2"
- local readonly TXARGS="$3"
- local readonly RXARGS="$4"
-
- if [[ "${IP}" == "4" ]]; then
- local readonly SADDR="${SADDR4}"
- local readonly DADDR="${DADDR4}"
- elif [[ "${IP}" == "6" ]]; then
- local readonly SADDR="${SADDR6}"
- local readonly DADDR="${DADDR6}"
- else
- echo "Invalid IP version ${IP}"
- exit 1
- fi
-
- local readonly START="$(date +%s%N --date="+ 0.1 seconds")"
-
- ip netns exec "${NS2}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${RXARGS}" -r &
- ip netns exec "${NS1}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${TXARGS}"
- wait "$!"
-}
-
-do_test() {
- run_test $@
- [ $? -ne 0 ] && ret=1
-}
-
-do_fail_test() {
- run_test $@
- [ $? -eq 0 ] && ret=1
-}
-
-ip netns exec "${NS1}" tc qdisc add dev "${DEV}" root fq
-set +e
-ret=0
-do_test 4 mono a,-1 a,-1
-do_test 6 mono a,0 a,0
-do_test 6 mono a,10 a,10
-do_test 4 mono a,10,b,20 a,10,b,20
-do_test 6 mono a,20,b,10 b,20,a,20
-
-if ip netns exec "${NS1}" tc qdisc replace dev "${DEV}" root etf clockid CLOCK_TAI delta 400000; then
- do_fail_test 4 tai a,-1 a,-1
- do_fail_test 6 tai a,0 a,0
- do_test 6 tai a,10 a,10
- do_test 4 tai a,10,b,20 a,10,b,20
- do_test 6 tai a,20,b,10 b,10,a,20
-else
- echo "tc ($(tc -V)) does not support qdisc etf. skipping"
- [ $ret -eq 0 ] && ret=$ksft_skip
-fi
-
-if [ $ret -eq 0 ]; then
- echo OK. All tests passed
-elif [[ $ret -ne $ksft_skip && -n "$KSFT_MACHINE_SLOW" ]]; then
- echo "Ignoring errors due to slow environment" 1>&2
- ret=0
-fi
-exit $ret
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH net-next v2] selftests/net: convert so_txtime to drv-net
2026-04-05 1:44 [PATCH net-next v2] selftests/net: convert so_txtime to drv-net Willem de Bruijn
@ 2026-04-05 2:20 ` Willem de Bruijn
2026-04-06 16:02 ` Jakub Kicinski
0 siblings, 1 reply; 4+ messages in thread
From: Willem de Bruijn @ 2026-04-05 2:20 UTC (permalink / raw)
To: Willem de Bruijn, netdev
Cc: davem, kuba, edumazet, pabeni, horms, Willem de Bruijn
Willem de Bruijn wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> In preparation for extending to pacing hardware offload, convert the
> so_txtime.sh test to a drv-net test that can be run against netdevsim
> and real hardware.
>
> Also update so_txtime.c to not exit on first failure, but run to
> completion and report exit code there. This helps with debugging
> unexpected results, especially when processing multiple packets,
> as in the "reverse_order" testcase.
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
>
> ----
>
> v1 -> v2
> - move so_txtime.c for net/lib to drivers/net (Jakub)
> - fix drivers/net/config order (Jakub)
> - detect passing when failure is expected (Jakub, Sashiko)
> - pass pylint --disable=R (Jakub)
> - only call ksft_run once (Jakub)
> - do not sleep if waiting time is negative (Sashiko)
> - add \n when converting error() to fprintf() (Sashiko)
> - 4 space indentation, instead of 2 space
> - increase sync delay from 100 to 200ms, to fix rare vng flakes
>
> v1: https://lore.kernel.org/netdev/20260403175047.152646-1-willemdebruijn.kernel@gmail.com/
> ---
> .../testing/selftests/drivers/net/.gitignore | 1 +
> tools/testing/selftests/drivers/net/Makefile | 5 +-
> tools/testing/selftests/drivers/net/config | 2 +
> .../selftests/{ => drivers}/net/so_txtime.c | 24 +++-
> .../selftests/drivers/net/so_txtime.py | 88 ++++++++++++++
> tools/testing/selftests/net/.gitignore | 1 -
> tools/testing/selftests/net/Makefile | 2 -
> tools/testing/selftests/net/so_txtime.sh | 110 ------------------
> 8 files changed, 114 insertions(+), 119 deletions(-)
> rename tools/testing/selftests/{ => drivers}/net/so_txtime.c (96%)
> create mode 100755 tools/testing/selftests/drivers/net/so_txtime.py
> delete mode 100755 tools/testing/selftests/net/so_txtime.sh
>
> diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore
> index 585ecb4d5dc4..e5314ce4bb2d 100644
> --- a/tools/testing/selftests/drivers/net/.gitignore
> +++ b/tools/testing/selftests/drivers/net/.gitignore
> @@ -1,3 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0-only
> napi_id_helper
> psp_responder
> +so_txtime
> diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
> index 7c7fa75b80c2..85d6a7e26627 100644
> --- a/tools/testing/selftests/drivers/net/Makefile
> +++ b/tools/testing/selftests/drivers/net/Makefile
> @@ -20,12 +20,15 @@ TEST_PROGS := \
> queues.py \
> ring_reconfig.py \
> shaper.py \
> + so_txtime.py \
> stats.py \
> xdp.py \
> # end of TEST_PROGS
>
> # YNL files, must be before "include ..lib.mk"
> -YNL_GEN_FILES := psp_responder
> +YNL_GEN_FILES := \
> + psp_responder \
> + so_txtime
This should just go under TEST_GEN_FILES (sashiko)
I don't quite understand the check_selftest check_new_files_makefile
failure.
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH net-next v2] selftests/net: convert so_txtime to drv-net
2026-04-05 2:20 ` Willem de Bruijn
@ 2026-04-06 16:02 ` Jakub Kicinski
2026-04-06 16:55 ` Willem de Bruijn
0 siblings, 1 reply; 4+ messages in thread
From: Jakub Kicinski @ 2026-04-06 16:02 UTC (permalink / raw)
To: Willem de Bruijn; +Cc: netdev, davem, edumazet, pabeni, horms, Willem de Bruijn
On Sat, 04 Apr 2026 22:20:15 -0400 Willem de Bruijn wrote:
> > +YNL_GEN_FILES := \
> > + psp_responder \
> > + so_txtime
>
> This should just go under TEST_GEN_FILES (sashiko)
>
> I don't quite understand the check_selftest check_new_files_makefile
> failure.
Glancing at the code you were missing the
# end of YNL_GEN_FILES
trailer.
Those checkers are equally annoying and easy to run locally, they
take a path to the modified file and say "good" / "bad".
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v2] selftests/net: convert so_txtime to drv-net
2026-04-06 16:02 ` Jakub Kicinski
@ 2026-04-06 16:55 ` Willem de Bruijn
0 siblings, 0 replies; 4+ messages in thread
From: Willem de Bruijn @ 2026-04-06 16:55 UTC (permalink / raw)
To: Jakub Kicinski, Willem de Bruijn
Cc: netdev, davem, edumazet, pabeni, horms, Willem de Bruijn
Jakub Kicinski wrote:
> On Sat, 04 Apr 2026 22:20:15 -0400 Willem de Bruijn wrote:
> > > +YNL_GEN_FILES := \
> > > + psp_responder \
> > > + so_txtime
> >
> > This should just go under TEST_GEN_FILES (sashiko)
> >
> > I don't quite understand the check_selftest check_new_files_makefile
> > failure.
>
> Glancing at the code you were missing the
>
> # end of YNL_GEN_FILES
>
> trailer.
>
> Those checkers are equally annoying and easy to run locally, they
> take a path to the modified file and say "good" / "bad".
Thanks. Indeed, I only learned about this yesterday. Ran it before v3.
Was super easy. Sharing for anyone else new to this:
git format-patch -1
mkdir /tmp/patches
mv 000* /tmp/patches
cd /tmp
git clone https://github.com/linux-netdev/nipa.git
cd nipa
./ingest_mdir.py --mdir /tmp/patches --tree ~/src/kernel/netdev/nn -t patch/check_selftest
--- output
Saving output and logs to: /tmp/tmp7zb9n8ka
Tree name unknown
[1] selftests/net: convert so_txtime to drv-net
Series level tests:
Patch level tests:
check_selftest OKAY Good format (5); New files in Makefile checked (1)
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-06 16:55 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-05 1:44 [PATCH net-next v2] selftests/net: convert so_txtime to drv-net Willem de Bruijn
2026-04-05 2:20 ` Willem de Bruijn
2026-04-06 16:02 ` Jakub Kicinski
2026-04-06 16:55 ` Willem de Bruijn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox