From 94720e01f5bb15dee5db8093fecc66f16d0a3368 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:34 -0700 Subject: [PATCH 01/10] selftests: rds: Increase selftest timeout The 400s time out was originally developed under a leaner kernel config that booted much faster than a default config. Boot up is included as part of the over all test runtime, as well as any log collection done when the test is complete. A slower config combined with the gcov enabled test means we'll need more time to accommodate the boot up and log collection. So, bump time out to 800s. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-2-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/settings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/rds/settings b/tools/testing/selftests/net/rds/settings index d2009a64589c..8cb41e6a83cc 100644 --- a/tools/testing/selftests/net/rds/settings +++ b/tools/testing/selftests/net/rds/settings @@ -1 +1 @@ -timeout=400 +timeout=800 From 4781c8b037a898cf3ebdf93787b0451a734ad76b Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:35 -0700 Subject: [PATCH 02/10] selftests: rds: Update USAGE string for run.sh The run.sh script does not have a -g flag. Update USAGE string with correct flags. Also fix typo packet_duplcate -> packet_duplicate Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-3-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/README.txt | 2 +- tools/testing/selftests/net/rds/run.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/rds/README.txt b/tools/testing/selftests/net/rds/README.txt index c6fe003d503b..8df6cc35dd10 100644 --- a/tools/testing/selftests/net/rds/README.txt +++ b/tools/testing/selftests/net/rds/README.txt @@ -12,7 +12,7 @@ kernel may optionally be configured to omit the coverage report as well. USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption] - [-u packet_duplcate] + [-u packet_duplicate] OPTIONS: -d Log directory. Defaults to tools/testing/selftests/net/rds/rds_logs diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh index 897d17d1b8db..73a9b986b0ef 100755 --- a/tools/testing/selftests/net/rds/run.sh +++ b/tools/testing/selftests/net/rds/run.sh @@ -171,7 +171,7 @@ while getopts "d:l:c:u:" opt; do ;; :) echo "USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption]" \ - "[-u packet_duplcate] [-g]" + "[-u packet_duplicate]" exit 1 ;; ?) From 5bdd59f906338780a0c977aae3f427937f6b7ec8 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:36 -0700 Subject: [PATCH 03/10] selftests: rds: Fix more pylint errors This patch fixes a few pylint errors in test.py. Remove unused exception variables from except blocks, and disable warnings for imports that cannot appear at the start of the module. Also disable warnings for the tcpdump processes. The suggestion to use a with block does not apply here since the process needs to outlive the parent to collect the dumps. Lastly add the module docstring at the top of the module. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-4-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index 93e23e8b256c..4b6ffbb3a81c 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -1,5 +1,8 @@ #! /usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 +""" +This module provides functional testing for the net/rds component. +""" import argparse import ctypes @@ -17,7 +20,8 @@ import shutil # Allow utils module to be imported from different directory this_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(this_dir, "../")) -from lib.py.utils import ip +# pylint: disable-next=wrong-import-position,import-error,no-name-in-module +from lib.py.utils import ip # noqa: E402 libc = ctypes.cdll.LoadLibrary('libc.so.6') setns = libc.setns @@ -129,6 +133,7 @@ tcpdump_procs = [] for net in [NET0, NET1]: pcap = logdir+'/'+net+'.pcap' fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") + # pylint: disable-next=consider-using-with p = subprocess.Popen( ['ip', 'netns', 'exec', net, '/usr/sbin/tcpdump', '-i', 'any', '-w', pcap_tmp]) @@ -192,7 +197,7 @@ while nr_send < NUM_PACKETS: send_hashes.setdefault((sender.fileno(), receiver.fileno()), hashlib.sha256()).update(f'<{send_data}>'.encode('utf-8')) nr_send = nr_send + 1 - except BlockingIOError as e: + except BlockingIOError: break except OSError as e: if e.errno in [errno.ENOBUFS, errno.ECONNRESET, errno.EPIPE]: @@ -214,7 +219,7 @@ while nr_send < NUM_PACKETS: receiver.fileno()), hashlib.sha256()).update( f'<{recv_data}>'.encode('utf-8')) nr_recv = nr_recv + 1 - except BlockingIOError as e: + except BlockingIOError: break # exercise net/rds/tcp.c:rds_tcp_sysctl_reset() From 7d6a32e7cb451fe24ecb6ed83f665e4babec11ba Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:37 -0700 Subject: [PATCH 04/10] selftests: rds: Add timeout flag to run.sh Add a -t flag to run.sh to optionally override the default timeout. The --timeout flag is already supported in test.py, so just add the shorthand -t flag Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-5-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/README.txt | 4 +++- tools/testing/selftests/net/rds/run.sh | 11 ++++++++--- tools/testing/selftests/net/rds/test.py | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/rds/README.txt b/tools/testing/selftests/net/rds/README.txt index 8df6cc35dd10..be9c7e25694e 100644 --- a/tools/testing/selftests/net/rds/README.txt +++ b/tools/testing/selftests/net/rds/README.txt @@ -12,7 +12,7 @@ kernel may optionally be configured to omit the coverage report as well. USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption] - [-u packet_duplicate] + [-u packet_duplicate] [-t timeout] OPTIONS: -d Log directory. Defaults to tools/testing/selftests/net/rds/rds_logs @@ -23,6 +23,8 @@ OPTIONS: -u Simulates a percentage of packet duplication. + -t Test timeout. Defaults to tools/testing/selftests/net/rds/settings + EXAMPLE: # Create a suitable gcov enabled .config diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh index 73a9b986b0ef..bc2e53126aab 100755 --- a/tools/testing/selftests/net/rds/run.sh +++ b/tools/testing/selftests/net/rds/run.sh @@ -154,8 +154,9 @@ LOG_DIR="$current_dir"/rds_logs PLOSS=0 PCORRUPT=0 PDUP=0 +TIMEOUT=$timeout GENERATE_GCOV_REPORT=1 -while getopts "d:l:c:u:" opt; do +while getopts "d:l:c:u:t:" opt; do case ${opt} in d) LOG_DIR=${OPTARG} @@ -166,12 +167,15 @@ while getopts "d:l:c:u:" opt; do c) PCORRUPT=${OPTARG} ;; + t) + TIMEOUT=${OPTARG} + ;; u) PDUP=${OPTARG} ;; :) echo "USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption]" \ - "[-u packet_duplicate]" + "[-u packet_duplicate] [-t timeout]" exit 1 ;; ?) @@ -198,7 +202,8 @@ echo running RDS tests... echo Traces will be logged to "$TRACE_FILE" rm -f "$TRACE_FILE" strace -T -tt -o "$TRACE_FILE" python3 "$(dirname "$0")/test.py" \ - --timeout "$timeout" -d "$LOG_DIR" -l "$PLOSS" -c "$PCORRUPT" -u "$PDUP" + -t "$TIMEOUT" -d "$LOG_DIR" -l "$PLOSS" -c "$PCORRUPT" \ + -u "$PDUP" test_rc=$? dmesg > "${LOG_DIR}/dmesg.out" diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index 4b6ffbb3a81c..d48533505f0f 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -83,7 +83,7 @@ parser = argparse.ArgumentParser(description="init script args", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-d", "--logdir", action="store", help="directory to store logs", default="/tmp") -parser.add_argument('--timeout', help="timeout to terminate hung test", +parser.add_argument('-t', '--timeout', help="timeout to terminate hung test", type=int, default=0) parser.add_argument('-l', '--loss', help="Simulate tcp packet loss", type=int, default=0) From ad070d9037591b5f86969ce8f4e5b20f8f215441 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:38 -0700 Subject: [PATCH 05/10] selftests: rds: Add RDS_LOG_DIR env variable This patch modifies the rds selftest to look for an env variable RDS_LOG_DIR, and log all traces, pcaps and gcov collections to the folder specified in RDS_LOG_DIR. If RDS_LOG_DIR is unset, logs are not collected. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-6-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/README.txt | 19 +++++++-- tools/testing/selftests/net/rds/run.sh | 45 ++++++++++++---------- tools/testing/selftests/net/rds/test.py | 34 ++++++++-------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/tools/testing/selftests/net/rds/README.txt b/tools/testing/selftests/net/rds/README.txt index be9c7e25694e..e629e08f04ef 100644 --- a/tools/testing/selftests/net/rds/README.txt +++ b/tools/testing/selftests/net/rds/README.txt @@ -15,7 +15,9 @@ USAGE: [-u packet_duplicate] [-t timeout] OPTIONS: - -d Log directory. Defaults to tools/testing/selftests/net/rds/rds_logs + -d Log directory. If set, logs will be stored in the + given dir, or skipped if unset. Log dir can also be + set through the RDS_LOG_DIR env variable -l Simulates a percentage of packet loss @@ -25,6 +27,16 @@ OPTIONS: -t Test timeout. Defaults to tools/testing/selftests/net/rds/settings +ENV VARIABLES: + RDS_LOG_DIR Log directory. If set, logs will be stored in + the given dir, or skipped if unset. Log dir + can also be set with the -d flag. + + Use with --rwdir on the CI path to retain logs after + test compleation. Log dir end point must be within + the specified --rwdir path for logs to persist on + the host. + EXAMPLE: # Create a suitable gcov enabled .config @@ -41,6 +53,7 @@ EXAMPLE: # launch the tests in a VM vng -v --rwdir ./ --run . --user root --cpus 4 -- \ - "export PYTHONPATH=tools/testing/selftests/net/; tools/testing/selftests/net/rds/run.sh" + "export PYTHONPATH=tools/testing/selftests/net/; \ + export RDS_LOG_DIR=tools/testing/selftests/net/rds/rds_logs; \ + tools/testing/selftests/net/rds/run.sh" -An HTML coverage report will be output in tools/testing/selftests/net/rds/rds_logs/coverage/. diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh index bc2e53126aab..edc021f4dec9 100755 --- a/tools/testing/selftests/net/rds/run.sh +++ b/tools/testing/selftests/net/rds/run.sh @@ -150,28 +150,26 @@ check_env() fi } -LOG_DIR="$current_dir"/rds_logs -PLOSS=0 -PCORRUPT=0 -PDUP=0 +LOG_DIR="${RDS_LOG_DIR:-}" TIMEOUT=$timeout GENERATE_GCOV_REPORT=1 +FLAGS=() while getopts "d:l:c:u:t:" opt; do case ${opt} in d) LOG_DIR=${OPTARG} ;; l) - PLOSS=${OPTARG} + FLAGS+=("-l" "${OPTARG}") ;; c) - PCORRUPT=${OPTARG} + FLAGS+=("-c" "${OPTARG}") ;; t) TIMEOUT=${OPTARG} ;; u) - PDUP=${OPTARG} + FLAGS+=("-u" "${OPTARG}") ;; :) echo "USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption]" \ @@ -185,30 +183,37 @@ while getopts "d:l:c:u:t:" opt; do esac done - check_env check_conf check_gcov_conf +TRACE_CMD=() +if [[ -n "$LOG_DIR" ]]; then + rm -fr "$LOG_DIR" + FLAGS+=("-d" "$LOG_DIR") -rm -fr "$LOG_DIR" -TRACE_FILE="${LOG_DIR}/rds-strace.txt" -COVR_DIR="${LOG_DIR}/coverage/" -mkdir -p "$LOG_DIR" -mkdir -p "$COVR_DIR" + TRACE_FILE="${LOG_DIR}/rds-strace.txt" + COVR_DIR="${LOG_DIR}/coverage/" + mkdir -p "$LOG_DIR" + mkdir -p "$COVR_DIR" + + echo Traces will be logged to "${TRACE_FILE}" + rm -f "$TRACE_FILE" + + TRACE_CMD=(strace -T -tt -o "${TRACE_FILE}") +fi set +e echo running RDS tests... -echo Traces will be logged to "$TRACE_FILE" -rm -f "$TRACE_FILE" -strace -T -tt -o "$TRACE_FILE" python3 "$(dirname "$0")/test.py" \ - -t "$TIMEOUT" -d "$LOG_DIR" -l "$PLOSS" -c "$PCORRUPT" \ - -u "$PDUP" +"${TRACE_CMD[@]}" python3 "$(dirname "$0")/test.py" "${FLAGS[@]}" -t "$TIMEOUT" test_rc=$? -dmesg > "${LOG_DIR}/dmesg.out" -if [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then +if [[ -n "$LOG_DIR" ]]; then + dmesg > "${LOG_DIR}/dmesg.out" +fi + +if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then echo saving coverage data... (set +x; cd /sys/kernel/debug/gcov; find ./* -name '*.gcda' | \ while read -r f diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index d48533505f0f..0ece8324d255 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -82,7 +82,7 @@ def signal_handler(_sig, _frame): parser = argparse.ArgumentParser(description="init script args", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-d", "--logdir", action="store", - help="directory to store logs", default="/tmp") + help="directory to store logs", default=None) parser.add_argument('-t', '--timeout', help="timeout to terminate hung test", type=int, default=0) parser.add_argument('-l', '--loss', help="Simulate tcp packet loss", @@ -128,16 +128,17 @@ ip(f"-n {NET1} route add {addrs[0][0]}/32 dev {VETH1}") # and communicating by doing a single ping ip(f"netns exec {NET0} ping -c 1 {addrs[1][0]}") -# Start a packet capture on each network tcpdump_procs = [] -for net in [NET0, NET1]: - pcap = logdir+'/'+net+'.pcap' - fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") - # pylint: disable-next=consider-using-with - p = subprocess.Popen( - ['ip', 'netns', 'exec', net, - '/usr/sbin/tcpdump', '-i', 'any', '-w', pcap_tmp]) - tcpdump_procs.append((p, pcap_tmp, pcap, fd)) +# Start a packet capture on each network +if logdir is not None: + for net in [NET0, NET1]: + pcap = logdir+'/'+net+'.pcap' + fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") + # pylint: disable-next=consider-using-with + p = subprocess.Popen( + ['ip', 'netns', 'exec', net, + '/usr/sbin/tcpdump', '-i', 'any', '-w', pcap_tmp]) + tcpdump_procs.append((p, pcap_tmp, pcap, fd)) # simulate packet loss, duplication and corruption for net, iface in [(NET0, VETH0), (NET1, VETH1)]: @@ -252,12 +253,13 @@ for s in sockets: print(f"getsockopt(): {nr_success}/{nr_error}") -print("Stopping network packet captures") -for p, pcap_tmp, pcap, fd in tcpdump_procs: - p.terminate() - p.wait() - os.close(fd) - shutil.move(pcap_tmp, pcap) +if logdir is not None: + print("Stopping network packet captures") + for p, pcap_tmp, pcap, fd in tcpdump_procs: + p.terminate() + p.wait() + os.close(fd) + shutil.move(pcap_tmp, pcap) # We're done sending and receiving stuff, now let's check if what # we received is what we sent. From d601239dcc268fa5b9217e379bd5bb02100a55e3 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:39 -0700 Subject: [PATCH 06/10] selftests: rds: Add SUDO_USER env variable This patch modifies rds selftests to use the environment variable SUDO_USER for tcpdumps if it is set. This is needed to avoid chown operations on the vng 9pfs which is not supported. Passing a user listed in sudoers avoids the tcpdump privilege drop which may otherwise create empty pcaps Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-7-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/README.txt | 7 +++++++ tools/testing/selftests/net/rds/test.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/rds/README.txt b/tools/testing/selftests/net/rds/README.txt index e629e08f04ef..295dc82c0770 100644 --- a/tools/testing/selftests/net/rds/README.txt +++ b/tools/testing/selftests/net/rds/README.txt @@ -37,6 +37,12 @@ ENV VARIABLES: the specified --rwdir path for logs to persist on the host. + SUDO_USER The user name that should be used for tcpdump + --relinquish-privileges. Set this to a user + belonging to the sudoers group to avoid drop + privilege errors with the vng 9p filesystem + which may result in empty pcaps + EXAMPLE: # Create a suitable gcov enabled .config @@ -54,6 +60,7 @@ EXAMPLE: # launch the tests in a VM vng -v --rwdir ./ --run . --user root --cpus 4 -- \ "export PYTHONPATH=tools/testing/selftests/net/; \ + export SUDO_USER=example_user; \ export RDS_LOG_DIR=tools/testing/selftests/net/rds/rds_logs; \ tools/testing/selftests/net/rds/run.sh" diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index 0ece8324d255..7a3616ac288f 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -134,10 +134,15 @@ if logdir is not None: for net in [NET0, NET1]: pcap = logdir+'/'+net+'.pcap' fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") + + tcpdump_cmd = ['ip', 'netns', 'exec', net, '/usr/sbin/tcpdump'] + sudo_user = os.environ.get('SUDO_USER') + if sudo_user: + tcpdump_cmd.extend(['-Z', sudo_user]) + tcpdump_cmd.extend(['-i', 'any', '-w', pcap_tmp]) + # pylint: disable-next=consider-using-with - p = subprocess.Popen( - ['ip', 'netns', 'exec', net, - '/usr/sbin/tcpdump', '-i', 'any', '-w', pcap_tmp]) + p = subprocess.Popen(tcpdump_cmd) tcpdump_procs.append((p, pcap_tmp, pcap, fd)) # simulate packet loss, duplication and corruption From c726bc68fffd2fe031eff258abd1283b063d0880 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:40 -0700 Subject: [PATCH 07/10] selftests: rds: Remove tmp pcaps This patch removes the initial tmp tcpdumps and instead saves the pcaps directly to the logdir if it is set. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-8-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/test.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index 7a3616ac288f..a7be57ef6ece 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -14,8 +14,6 @@ import signal import socket import subprocess import sys -import tempfile -import shutil # Allow utils module to be imported from different directory this_dir = os.path.dirname(os.path.realpath(__file__)) @@ -133,17 +131,16 @@ tcpdump_procs = [] if logdir is not None: for net in [NET0, NET1]: pcap = logdir+'/'+net+'.pcap' - fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") tcpdump_cmd = ['ip', 'netns', 'exec', net, '/usr/sbin/tcpdump'] sudo_user = os.environ.get('SUDO_USER') if sudo_user: tcpdump_cmd.extend(['-Z', sudo_user]) - tcpdump_cmd.extend(['-i', 'any', '-w', pcap_tmp]) + tcpdump_cmd.extend(['-i', 'any', '-w', pcap]) # pylint: disable-next=consider-using-with p = subprocess.Popen(tcpdump_cmd) - tcpdump_procs.append((p, pcap_tmp, pcap, fd)) + tcpdump_procs.append(p) # simulate packet loss, duplication and corruption for net, iface in [(NET0, VETH0), (NET1, VETH1)]: @@ -260,11 +257,9 @@ print(f"getsockopt(): {nr_success}/{nr_error}") if logdir is not None: print("Stopping network packet captures") - for p, pcap_tmp, pcap, fd in tcpdump_procs: + for p in tcpdump_procs: p.terminate() p.wait() - os.close(fd) - shutil.move(pcap_tmp, pcap) # We're done sending and receiving stuff, now let's check if what # we received is what we sent. From ec91483634fe9ae30ba50bc20e2050ce7eec2e8e Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:41 -0700 Subject: [PATCH 08/10] selftests: rds: Stop tcpdump on timeout The timeout signal handler for the rds selftests currently just exits when the time limit is exceeded, and forgets to stop the network dumps. Fix this by hoisting the tcpdump terminate commands into a helper function, and call it from the signal handler before exiting Bound proc.wait() with a timeout (and fall back to proc.kill()) so an unresponsive tcpdump cannot hang the timeout path itself. We also pop() tcpdump_procs as we iterate, so stop_pcaps() is safe to call from both the normal cleanup path and the signal handler, since the second invocation simply has nothing to do Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-9-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/test.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index a7be57ef6ece..faf751863478 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -68,11 +68,29 @@ def netns_socket(netns, *sock_args): u1.close() return socket.fromfd(fds[0], *sock_args) +def stop_pcaps(): + """Stop tcpdump processes. + + We use pop() here to drain the list in the event that the test + completes after the signal handler is fired. List will be empty + if logdir is not set + """ + print("Stopping network packet captures") + while tcpdump_procs: + proc = tcpdump_procs.pop() + proc.terminate() + try: + proc.wait(timeout=5) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + def signal_handler(_sig, _frame): """ Test timed out signal handler """ print('Test timed out') + stop_pcaps() sys.exit(1) #Parse out command line arguments. We take an optional @@ -255,11 +273,7 @@ for s in sockets: print(f"getsockopt(): {nr_success}/{nr_error}") -if logdir is not None: - print("Stopping network packet captures") - for p in tcpdump_procs: - p.terminate() - p.wait() +stop_pcaps() # We're done sending and receiving stuff, now let's check if what # we received is what we sent. From c8781c61fd60552c8c65c6abb7146e9ef4b31dcc Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:42 -0700 Subject: [PATCH 09/10] selftests: rds: Fix gcov collection debugfs is not mounted automatically in a virtme-ng guest, so the gcov data copy from /sys/kernel/debug/gcov/ silently finds nothing depending on whether debugfs is mounted by default on the host OS. Fix this by mounting debugfs in run.sh before copying the gcda files. Finally when invoked through the kselftest runner, the working directory is the test directory rather than the kernel source root. gcovr defaults --root to the current working directory, which causes it to filter out all coverage data for files under net/rds/ since they are not under the test directory. Fix this by passing --root to gcovr explicitly. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-10-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/run.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh index edc021f4dec9..c07e3785ff79 100755 --- a/tools/testing/selftests/net/rds/run.sh +++ b/tools/testing/selftests/net/rds/run.sh @@ -215,6 +215,12 @@ fi if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then echo saving coverage data... + + # Ensure debugfs is mounted before reading gcov data. + if ! mountpoint -q /sys/kernel/debug 2>/dev/null; then + mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null || true + fi + (set +x; cd /sys/kernel/debug/gcov; find ./* -name '*.gcda' | \ while read -r f do @@ -223,7 +229,7 @@ if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then echo running gcovr... gcovr -s --html-details --gcov-executable "$GCOV_CMD" --gcov-ignore-parse-errors \ - -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" + --root "${ksrc_dir}" -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" else echo "Coverage report will be skipped" fi From 06d5c34517e0a9359db233bee71265fad25e3bf5 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sun, 3 May 2026 22:41:43 -0700 Subject: [PATCH 10/10] selftests: rds: Make rds selftests TAP compliant This patch updates the rds selftests output to be TAP compliant. Use ksft_pr() to mark debug output with a leading '# ' so that TAP parsers treat it as commentary, and convert all informational print() calls to use ksft_pr(). sys.exit(0) is changed to os._exit(0) to avoid duplicate prints from the buffered TAP output. The console output from the tcpdump subprocess is silenced, and the gcov console output is redirected to a gcovr.log. Finally adjust the exit path so that the hash check loop sets a return code instead exiting directly. Then print the TAP results and totals lines before exiting. Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260504054143.4027538-11-achender@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/rds/run.sh | 18 +++++---- tools/testing/selftests/net/rds/test.py | 51 ++++++++++++++++--------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh index c07e3785ff79..2404a889767a 100755 --- a/tools/testing/selftests/net/rds/run.sh +++ b/tools/testing/selftests/net/rds/run.sh @@ -197,14 +197,14 @@ if [[ -n "$LOG_DIR" ]]; then mkdir -p "$LOG_DIR" mkdir -p "$COVR_DIR" - echo Traces will be logged to "${TRACE_FILE}" + echo "#Traces will be logged to ${TRACE_FILE}" rm -f "$TRACE_FILE" TRACE_CMD=(strace -T -tt -o "${TRACE_FILE}") fi set +e -echo running RDS tests... +echo "#running RDS tests..." "${TRACE_CMD[@]}" python3 "$(dirname "$0")/test.py" "${FLAGS[@]}" -t "$TIMEOUT" test_rc=$? @@ -214,7 +214,7 @@ if [[ -n "$LOG_DIR" ]]; then fi if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then - echo saving coverage data... + echo "# saving coverage data..." # Ensure debugfs is mounted before reading gcov data. if ! mountpoint -q /sys/kernel/debug 2>/dev/null; then @@ -227,17 +227,19 @@ if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then cat < "/sys/kernel/debug/gcov/$f" > "/$f" done) - echo running gcovr... + echo "# running gcovr..." gcovr -s --html-details --gcov-executable "$GCOV_CMD" --gcov-ignore-parse-errors \ - --root "${ksrc_dir}" -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" + --root "${ksrc_dir}" -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" \ + > "${LOG_DIR}/gcovr.log" 2>&1 + echo "# gcovr log: ${LOG_DIR}/gcovr.log" else - echo "Coverage report will be skipped" + echo "# Coverage report will be skipped" fi if [ "$test_rc" -eq 0 ]; then - echo "PASS: Test completed successfully" + echo "# PASS: Test completed successfully" else - echo "FAIL: Test failed" + echo "# FAIL: Test failed" fi exit "$test_rc" diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index faf751863478..d19d30e5ec6f 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -20,6 +20,8 @@ this_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(this_dir, "../")) # pylint: disable-next=wrong-import-position,import-error,no-name-in-module from lib.py.utils import ip # noqa: E402 +# pylint: disable-next=wrong-import-position,import-error,no-name-in-module +from lib.py.ksft import ksft_pr # noqa: E402 libc = ctypes.cdll.LoadLibrary('libc.so.6') setns = libc.setns @@ -59,7 +61,7 @@ def netns_socket(netns, *sock_args): # send resulting socket to parent socket.send_fds(u0, [], [sock.fileno()]) - sys.exit(0) + os._exit(0) # receive socket from child _, fds, _, _ = socket.recv_fds(u1, 0, 1) @@ -75,7 +77,7 @@ def stop_pcaps(): completes after the signal handler is fired. List will be empty if logdir is not set """ - print("Stopping network packet captures") + ksft_pr("Stopping network packet captures") while tcpdump_procs: proc = tcpdump_procs.pop() proc.terminate() @@ -89,8 +91,9 @@ def signal_handler(_sig, _frame): """ Test timed out signal handler """ - print('Test timed out') + ksft_pr("Test timed out") stop_pcaps() + print("not ok 1 rds selftest") sys.exit(1) #Parse out command line arguments. We take an optional @@ -157,7 +160,8 @@ if logdir is not None: tcpdump_cmd.extend(['-i', 'any', '-w', pcap]) # pylint: disable-next=consider-using-with - p = subprocess.Popen(tcpdump_cmd) + p = subprocess.Popen(tcpdump_cmd, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) tcpdump_procs.append(p) # simulate packet loss, duplication and corruption @@ -166,6 +170,9 @@ for net, iface in [(NET0, VETH0), (NET1, VETH1)]: corrupt {PACKET_CORRUPTION} loss {PACKET_LOSS} duplicate \ {PACKET_DUPLICATE}") +print("TAP version 13") +print("1..1") + # add a timeout if args.timeout > 0: signal.alarm(args.timeout) @@ -204,7 +211,7 @@ nr_recv = 0 while nr_send < NUM_PACKETS: # Send as much as we can without blocking - print("sending...", nr_send, nr_recv) + ksft_pr("sending...", nr_send, nr_recv) while nr_send < NUM_PACKETS: send_data = hashlib.sha256( f'packet {nr_send}'.encode('utf-8')).hexdigest().encode('utf-8') @@ -226,7 +233,7 @@ while nr_send < NUM_PACKETS: raise # Receive as much as we can without blocking - print("receiving...", nr_send, nr_recv) + ksft_pr("receiving...", nr_send, nr_recv) while nr_recv < nr_send: for fileno, eventmask in ep.poll(): receiver = fileno_to_socket[fileno] @@ -248,7 +255,7 @@ while nr_send < NUM_PACKETS: ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_rcvbuf=10000") ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_sndbuf=10000") -print("done", nr_send, nr_recv) +ksft_pr("done", nr_send, nr_recv) # the Python socket module doesn't know these RDS_INFO_FIRST = 10000 @@ -271,26 +278,34 @@ for s in sockets: # ignore pass -print(f"getsockopt(): {nr_success}/{nr_error}") - +ksft_pr(f"getsockopt(): {nr_success}/{nr_error}") stop_pcaps() # We're done sending and receiving stuff, now let's check if what # we received is what we sent. +ret = 0 for (sender, receiver), send_hash in send_hashes.items(): recv_hash = recv_hashes.get((sender, receiver)) if recv_hash is None: - print("FAIL: No data received") - sys.exit(1) + ksft_pr("FAIL: No data received") + ret = 1 + break if send_hash.hexdigest() != recv_hash.hexdigest(): - print("FAIL: Send/recv mismatch") - print("hash expected:", send_hash.hexdigest()) - print("hash received:", recv_hash.hexdigest()) - sys.exit(1) + ksft_pr("FAIL: Send/recv mismatch") + ksft_pr("hash expected:", send_hash.hexdigest()) + ksft_pr("hash received:", recv_hash.hexdigest()) + ret = 1 + break - print(f"{sender}/{receiver}: ok") + ksft_pr(f"{sender}/{receiver}: ok") -print("Success") -sys.exit(0) +if ret == 0: + ksft_pr("Success") + print("ok 1 rds selftest") +else: + print("not ok 1 rds selftest") + +ksft_pr(f"Totals: pass:{1-ret} fail:{ret} skip:0") +sys.exit(ret)