diff mbox series

[net-next,v3,5/5] selftests: drv-net-hw: Add a test for symmetric RSS hash

Message ID 20250216182453.226325-6-gal@nvidia.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series Symmetric OR-XOR RSS hash | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/build_tools success Errors and warnings before: 26 (+1) this patch: 26 (+1)
netdev/cc_maintainers warning 4 maintainers not CCed: petrm@nvidia.com willemb@google.com shuah@kernel.org linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 17 this patch: 17
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-02-17--21-00 (tests: 886)

Commit Message

Gal Pressman Feb. 16, 2025, 6:24 p.m. UTC
Add a selftest that verifies symmetric RSS hash is working as intended.
The test runs two iterations of iperf3 traffic, swapping the src/dst TCP
ports, and verifies that the same RX queue is receiving the traffic in
both cases.

Reviewed-by: Nimrod Oren <noren@nvidia.com>
Signed-off-by: Gal Pressman <gal@nvidia.com>
---
 .../testing/selftests/drivers/net/hw/Makefile |  1 +
 .../drivers/net/hw/rss_input_xfrm.py          | 77 +++++++++++++++++++
 .../selftests/drivers/net/lib/py/load.py      |  7 +-
 3 files changed, 83 insertions(+), 2 deletions(-)
 create mode 100755 tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py

Comments

Jakub Kicinski Feb. 18, 2025, 12:19 a.m. UTC | #1
On Sun, 16 Feb 2025 20:24:53 +0200 Gal Pressman wrote:
> +    # Check for symmetric xor/or-xor
> +    if input_xfrm and (input_xfrm == 1 or input_xfrm == 2):
> +        port1 = _get_rand_port(cfg.remote)
> +        port2 = _get_rand_port(cfg.remote)
> +        ksft_pr(f'Running traffic on ports: {port1 = }, {port2 = }')
> +
> +        cnts = _get_rx_cnts(cfg)
> +        GenerateTraffic(cfg, port=port1, parallel=1,
> +                        cport=port2).wait_pkts_and_stop(20000)
> +        cnts = _get_rx_cnts(cfg, prev=cnts)
> +        rxq1 = _get_active_rx_queue(cnts)
> +        ksft_pr(f'Received traffic on {rxq1 = }')
> +
> +        cnts = _get_rx_cnts(cfg)
> +        GenerateTraffic(cfg, port=port2, parallel=1,
> +                        cport=port1).wait_pkts_and_stop(20000)
> +        cnts = _get_rx_cnts(cfg, prev=cnts)
> +        rxq2 = _get_active_rx_queue(cnts)
> +        ksft_pr(f'Received traffic on {rxq2 = }')
> +
> +        ksft_eq(
> +            rxq1, rxq2, comment=f"Received traffic on different queues ({rxq1} != {rxq2}) while symmetric hash is configured")

Wouldn't it be both faster and less error prone to test this with UDP
sockets?

	sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
	sock.connect(cfg.remove_addr_v[ipver], remote_port)
	sock.recvmsg(100)

	tgt = f"{ipver}:{cfg.addr_v[ipver]}:{local_port},sourceport={remote_port}"
	cmd("echo a | socat - UDP" + tgt, host=cfg.remote)

	cpu = sock.getsockopt(socket.SOL_SOCKET, socket.SO_INCOMING_CPU)

Run this for 10 pairs for ports, make sure we hit at least 2 CPUs,
and that the CPUs match for each pair.

Would be good to test both IPv4 and IPv6. I'm going to merge:
  https://lore.kernel.org/all/20250217194200.3011136-4-kuba@kernel.org/
it should make writing the v4 + v6 test combos easier.
Gal Pressman Feb. 18, 2025, 8:26 p.m. UTC | #2
On 18/02/2025 2:19, Jakub Kicinski wrote:
> Wouldn't it be both faster and less error prone to test this with UDP
> sockets?
> 
> 	sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
> 	sock.connect(cfg.remove_addr_v[ipver], remote_port)
> 	sock.recvmsg(100)
> 
> 	tgt = f"{ipver}:{cfg.addr_v[ipver]}:{local_port},sourceport={remote_port}"
> 	cmd("echo a | socat - UDP" + tgt, host=cfg.remote)
> 
> 	cpu = sock.getsockopt(socket.SOL_SOCKET, socket.SO_INCOMING_CPU)
> 
> Run this for 10 pairs for ports, make sure we hit at least 2 CPUs,
> and that the CPUs match for each pair.

Thanks, I can do that.

> 
> Would be good to test both IPv4 and IPv6. I'm going to merge:
>   https://lore.kernel.org/all/20250217194200.3011136-4-kuba@kernel.org/
> it should make writing the v4 + v6 test combos easier.

Should I wait for it to get merged?
Jakub Kicinski Feb. 18, 2025, 9:24 p.m. UTC | #3
On Tue, 18 Feb 2025 22:26:25 +0200 Gal Pressman wrote:
> > Would be good to test both IPv4 and IPv6. I'm going to merge:
> >   https://lore.kernel.org/all/20250217194200.3011136-4-kuba@kernel.org/
> > it should make writing the v4 + v6 test combos easier.  
> 
> Should I wait for it to get merged?

Yes, please, I remove the members you'd need to use to test both v4
and v6 so you patch will stop working once mine is merged. Stan reported
a problem, I'll repost once again and hopefully the new version goes in
tomorrow.
diff mbox series

Patch

diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 21ba64ce1e34..23dca086f84f 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -15,6 +15,7 @@  TEST_PROGS = \
 	nic_performance.py \
 	pp_alloc_fail.py \
 	rss_ctx.py \
+	rss_input_xfrm.py \
 	#
 
 TEST_FILES := \
diff --git a/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
new file mode 100755
index 000000000000..e32df852f091
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/rss_input_xfrm.py
@@ -0,0 +1,77 @@ 
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_pr
+from lib.py import NetDrvEpEnv
+from lib.py import EthtoolFamily, NetdevFamily
+from lib.py import KsftSkipEx
+from lib.py import rand_port, check_port_available_remote
+from lib.py import GenerateTraffic
+from rss_ctx import _get_rx_cnts
+
+
+def _get_active_rx_queue(cnts):
+    return cnts.index(max(cnts))
+
+
+def _get_rand_port(remote):
+    for _ in range(1000):
+        port = rand_port()
+        try:
+            check_port_available_remote(port, remote)
+            return port
+        except:
+            continue
+
+    raise Exception("Can't find any free unprivileged port")
+
+
+def test_rss_input_xfrm(cfg):
+    """
+    Test symmetric input_xfrm.
+    If symmetric RSS hash is configured, send traffic twice, swapping the
+    src/dst TCP ports, and verify that the same queue is receiving the traffic
+    in both cases (IPs are constant).
+    """
+
+    input_xfrm = cfg.ethnl.rss_get(
+        {'header': {'dev-name': cfg.ifname}}).get('input_xfrm')
+
+    # Check for symmetric xor/or-xor
+    if input_xfrm and (input_xfrm == 1 or input_xfrm == 2):
+        port1 = _get_rand_port(cfg.remote)
+        port2 = _get_rand_port(cfg.remote)
+        ksft_pr(f'Running traffic on ports: {port1 = }, {port2 = }')
+
+        cnts = _get_rx_cnts(cfg)
+        GenerateTraffic(cfg, port=port1, parallel=1,
+                        cport=port2).wait_pkts_and_stop(20000)
+        cnts = _get_rx_cnts(cfg, prev=cnts)
+        rxq1 = _get_active_rx_queue(cnts)
+        ksft_pr(f'Received traffic on {rxq1 = }')
+
+        cnts = _get_rx_cnts(cfg)
+        GenerateTraffic(cfg, port=port2, parallel=1,
+                        cport=port1).wait_pkts_and_stop(20000)
+        cnts = _get_rx_cnts(cfg, prev=cnts)
+        rxq2 = _get_active_rx_queue(cnts)
+        ksft_pr(f'Received traffic on {rxq2 = }')
+
+        ksft_eq(
+            rxq1, rxq2, comment=f"Received traffic on different queues ({rxq1} != {rxq2}) while symmetric hash is configured")
+    else:
+        raise KsftSkipEx("Symmetric RSS hash not requested")
+
+
+def main() -> None:
+    with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+        cfg.ethnl = EthtoolFamily()
+        cfg.netdevnl = NetdevFamily()
+
+        ksft_run([test_rss_input_xfrm],
+                 args=(cfg, ))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/testing/selftests/drivers/net/lib/py/load.py b/tools/testing/selftests/drivers/net/lib/py/load.py
index da5af2c680fa..76aaa65c0367 100644
--- a/tools/testing/selftests/drivers/net/lib/py/load.py
+++ b/tools/testing/selftests/drivers/net/lib/py/load.py
@@ -5,7 +5,7 @@  import time
 from lib.py import ksft_pr, cmd, ip, rand_port, wait_port_listen, bkg
 
 class GenerateTraffic:
-    def __init__(self, env, port=None):
+    def __init__(self, env, port=None, cport=None, parallel=16):
         env.require_cmd("iperf3", remote=True)
 
         self.env = env
@@ -15,7 +15,10 @@  class GenerateTraffic:
         self._iperf_server = cmd(f"iperf3 -s -1 -p {port}", background=True)
         wait_port_listen(port)
         time.sleep(0.1)
-        self._iperf_client = cmd(f"iperf3 -c {env.addr} -P 16 -p {port} -t 86400",
+        client_cmd = f"iperf3 -c {env.addr} -P {parallel} -p {port} -t 86400"
+        if cport and parallel == 1:
+            client_cmd = f"{client_cmd} --cport {cport}"
+        self._iperf_client = cmd(client_cmd,
                                  background=True, host=env.remote)
 
         # Wait for traffic to ramp up