Thread (8 messages) flat view 8 messages, 2 authors, 3d ago

[RFC net-next 4/6] selftests: drv-net: add an iperf performance test

From: Stanislav Fomichev <hidden>
Date: 2026-09-16 19:04:20
Subsystem: kernel selftest framework, networking drivers, networking [general], the rest · Maintainers: Shuah Khan, Shuah Khan, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Report Iperf3Runner throughput and retransmits when metric collection is
active.

Tested on two mlx5 hosts. Benchmark metric output:

  # # ktap-metric-policy-json: {"name":"throughput","regression":{"better":"higher","compare":"p50","relative_tolerance":0.05},"summarize":"distribution"}
  # # ktap-metric-policy-json: {"name":"tcp.retransmits","summarize":"max"}
  # # ktap-metric-json: {"host":"local","name":"throughput","shape":"scalar","time":11.42686,"value":32.62285180182741}
  # # ktap-metric-json: {"host":"remote","name":"tcp.retransmits","shape":"scalar","time":11.426883,"value":75895.0}
  # ok 1 iperf.test_iperf
  # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
---
 .../testing/selftests/drivers/net/hw/Makefile |  1 +
 .../testing/selftests/drivers/net/hw/iperf.py | 21 ++++++++++++++++
 .../drivers/net/hw/lib/py/__init__.py         |  8 +++---
 .../selftests/drivers/net/lib/py/__init__.py  |  8 +++---
 .../selftests/drivers/net/lib/py/load.py      | 25 +++++++++++++++++--
 .../testing/selftests/net/lib/py/__init__.py  |  6 ++---
 tools/testing/selftests/net/lib/py/ksft.py    |  6 +++++
 7 files changed, 62 insertions(+), 13 deletions(-)
 create mode 100755 tools/testing/selftests/drivers/net/hw/iperf.py
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 8aebdc6feb17..03ecc81e18e0 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -32,6 +32,7 @@ TEST_PROGS = \
 	hw_stats_l3.sh \
 	hw_stats_l3_gre.sh \
 	iou-zcrx.py \
+	iperf.py \
 	ipsec_vxlan.py \
 	irq.py \
 	loopback.sh \
diff --git a/tools/testing/selftests/drivers/net/hw/iperf.py b/tools/testing/selftests/drivers/net/hw/iperf.py
new file mode 100755
index 000000000000..77e315c9547e
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/iperf.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+from lib.py import (Iperf3Runner, NetDrvEpEnv, SystemMonitor, ksft_exit,
+                    ksft_run)
+
+
+def test_iperf(cfg):
+    """Measure TCP throughput while sampling system utilization."""
+    with SystemMonitor(cfg):
+        Iperf3Runner(cfg).measure_bandwidth()
+
+
+def main():
+    with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+        ksft_run([test_iperf], args=(cfg,))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
index 1077930d1677..857bd016e5e0 100644
--- a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
@@ -28,8 +28,8 @@ KSFT_DIR = (Path(__file__).parent / "../../../../..").resolve()
     from net.lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
     from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx
     from net.lib.py import ksft_disruptive, ksft_exit, ksft_metric, \
-        ksft_metric_aggregate, ksft_metric_reporting, ksft_pr, ksft_run, \
-        ksft_setup, \
+        ksft_metric_aggregate, ksft_metric_reporting, \
+        ksft_metric_reporting_enabled, ksft_pr, ksft_run, ksft_setup, \
         ksft_variants, KsftNamedVariant
     from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \
         ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none
@@ -47,8 +47,8 @@ KSFT_DIR = (Path(__file__).parent / "../../../../..").resolve()
                "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids",
                "KsftSkipEx", "KsftFailEx", "KsftXfailEx",
                "ksft_disruptive", "ksft_exit", "ksft_metric",
-               "ksft_metric_aggregate", "ksft_metric_reporting", "ksft_pr",
-               "ksft_run",
+               "ksft_metric_aggregate", "ksft_metric_reporting",
+               "ksft_metric_reporting_enabled", "ksft_pr", "ksft_run",
                "ksft_setup", "ksft_variants",
                "KsftNamedVariant",
                "ksft_eq", "ksft_ge", "ksft_in", "ksft_is", "ksft_lt",
diff --git a/tools/testing/selftests/drivers/net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/lib/py/__init__.py
index d779f48ea6c5..e418837f0b00 100644
--- a/tools/testing/selftests/drivers/net/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/lib/py/__init__.py
@@ -28,8 +28,8 @@ KSFT_DIR = (Path(__file__).parent / "../../../..").resolve()
     from net.lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
     from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx
     from net.lib.py import ksft_disruptive, ksft_exit, ksft_metric, \
-        ksft_metric_aggregate, ksft_metric_reporting, ksft_pr, ksft_run, \
-        ksft_setup, \
+        ksft_metric_aggregate, ksft_metric_reporting, \
+        ksft_metric_reporting_enabled, ksft_pr, ksft_run, ksft_setup, \
         ksft_variants, KsftNamedVariant
     from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \
         ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none
@@ -44,8 +44,8 @@ KSFT_DIR = (Path(__file__).parent / "../../../..").resolve()
                "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids",
                "KsftSkipEx", "KsftFailEx", "KsftXfailEx",
                "ksft_disruptive", "ksft_exit", "ksft_metric",
-               "ksft_metric_aggregate", "ksft_metric_reporting", "ksft_pr",
-               "ksft_run",
+               "ksft_metric_aggregate", "ksft_metric_reporting",
+               "ksft_metric_reporting_enabled", "ksft_pr", "ksft_run",
                "ksft_setup", "ksft_variants",
                "KsftNamedVariant",
                "ksft_eq", "ksft_ge", "ksft_in", "ksft_is", "ksft_lt",
diff --git a/tools/testing/selftests/drivers/net/lib/py/load.py b/tools/testing/selftests/drivers/net/lib/py/load.py
index e24660e5c27f..f5f5cf780fbe 100644
--- a/tools/testing/selftests/drivers/net/lib/py/load.py
+++ b/tools/testing/selftests/drivers/net/lib/py/load.py
@@ -4,7 +4,8 @@ import re
 import time
 import json
 
-from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen
+from lib.py import cmd, ip, ksft_metric, ksft_metric_aggregate, \
+    ksft_metric_reporting_enabled, ksft_pr, rand_port, wait_port_listen
 
 
 class Iperf3Runner:
@@ -57,8 +58,17 @@ from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen
         """
         Runs an iperf3 measurement and returns the average bandwidth (Gbps).
         Discards the first and last few reporting intervals and uses only the
-        middle part of the run where throughput is typically stable.
+        middle part of the run where throughput is typically stable. When a
+        SystemMonitor is active, also emit throughput and retransmit metrics.
         """
+        report_metrics = ksft_metric_reporting_enabled()
+        if report_metrics:
+            ksft_metric_aggregate(
+                "throughput", summarize="distribution",
+                regression={"compare": "p50", "better": "higher",
+                            "relative_tolerance": 0.05})
+            ksft_metric_aggregate("tcp.retransmits", summarize="max")
+
         self.start_server()
         result = self.start_client(duration=10, reverse=reverse)
 
@@ -77,6 +87,17 @@ from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen
         stable = samples[3:-3]
 
         avg = sum(stable) / len(stable)
+        retransmits = out.get("end", {}).get("sum_sent", {}).get(
+            "retransmits")
+        if report_metrics:
+            if not isinstance(retransmits, int) or \
+                    isinstance(retransmits, bool):
+                raise ValueError("iperf3 did not report TCP retransmits")
+            receiver = "remote" if reverse else "local"
+            sender = "local" if reverse else "remote"
+            ksft_metric("throughput", avg, shape="scalar", host=receiver)
+            ksft_metric("tcp.retransmits", retransmits, shape="scalar",
+                        host=sender)
 
         return avg
 
diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py
index 9795a83d662a..5b91de1650c5 100644
--- a/tools/testing/selftests/net/lib/py/__init__.py
+++ b/tools/testing/selftests/net/lib/py/__init__.py
@@ -9,7 +9,7 @@ from .ksft import KsftFailEx, KsftSkipEx, KsftXfailEx, ksft_pr, ksft_eq, \
     ksft_ne, ksft_true, ksft_not_none, ksft_in, ksft_not_in, ksft_is, \
     ksft_ge, ksft_gt, ksft_lt, ksft_raises, ksft_busy_wait, \
     ktap_result, ksft_disruptive, ksft_metric, ksft_metric_aggregate, \
-    ksft_metric_reporting, ksft_setup, ksft_run, \
+    ksft_metric_reporting, ksft_metric_reporting_enabled, ksft_setup, ksft_run, \
     ksft_exit, ksft_variants, KsftNamedVariant
 from .netns import NetNS, NetNSEnter, UserNetNS
 from .nsim import NetdevSim, NetdevSimDev
@@ -26,8 +26,8 @@ __all__ = ["KSRC",
            "ksft_ne", "ksft_true", "ksft_not_none", "ksft_in", "ksft_not_in",
            "ksft_is", "ksft_ge", "ksft_gt", "ksft_lt", "ksft_raises",
            "ksft_busy_wait", "ktap_result", "ksft_disruptive", "ksft_metric",
-           "ksft_metric_aggregate", "ksft_metric_reporting", "ksft_setup",
-           "ksft_run",
+           "ksft_metric_aggregate", "ksft_metric_reporting",
+           "ksft_metric_reporting_enabled", "ksft_setup", "ksft_run",
            "ksft_exit", "ksft_variants",
            "KsftNamedVariant",
            "NetNS", "NetNSEnter", "UserNetNS",
diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py
index e44029213e4f..566ec52cd9c9 100644
--- a/tools/testing/selftests/net/lib/py/ksft.py
+++ b/tools/testing/selftests/net/lib/py/ksft.py
@@ -108,6 +108,12 @@ KSFT_METRICS_LOCK = threading.Lock()
         KSFT_METRIC_REPORTING += 1 if enable else -1
 
 
+def ksft_metric_reporting_enabled():
+    """Return whether benchmark metric producers should emit metrics."""
+    with KSFT_METRICS_LOCK:
+        return KSFT_METRIC_REPORTING > 0
+
+
 def ksft_metric_aggregate(name, summarize="total", *, aggregation=None,
                           transform=None, regression=None,
                           display_range=None, display_scale=None,
-- 
2.53.0-Meta
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help