Thread (18 messages) flat view 18 messages, 4 authors, 24d ago

[RFC net-next 6/6] selftests: drv-net: psp_steer: cover corner cases and races

From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-08-22 22:55:34
Subsystem: kernel selftest framework, networking drivers, the rest · Maintainers: Shuah Khan, Shuah Khan, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Test a PSP cookie naming a queue which went away.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 .../selftests/drivers/net/psp_steer.py        | 80 ++++++++++++++++---
 1 file changed, 70 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/psp_steer.py b/tools/testing/selftests/drivers/net/psp_steer.py
index fac2d7532d61..f61479befcc0 100644
--- a/tools/testing/selftests/drivers/net/psp_steer.py
+++ b/tools/testing/selftests/drivers/net/psp_steer.py
@@ -6,17 +6,18 @@
 import errno
 import os
 import socket
+import time
 
 from lib.py import defer
 from lib.py import ksft_run, ksft_exit
-from lib.py import ksft_eq, ksft_ge, ksft_in, ksft_ne, ksft_raises
+from lib.py import ksft_eq, ksft_ge, ksft_in, ksft_lt, ksft_ne, ksft_raises
 from lib.py import CmdExitFailure, KsftSkipEx
 from lib.py import NetDrvEpEnv
 from lib.py import NetdevFamily, NlError, PSPFamily
 from lib.py import ethtool
 
 from psp_lib import close_conn, init_psp_dev, make_clr_conn, make_psp_conn, \
-    psp_txrx, remote_conn_steer, remote_dev_steer, spi_xchg
+    psp_txrx, remote_dev_steer, req_echo, spi_xchg
 from psp_lib import responder as psp_responder
 
 # Not exposed by the socket module
@@ -43,6 +44,8 @@ _VC_TX = 1 << 0
 _VC_RX = 1 << 1
 _VC_BOTH = _VC_TX | _VC_RX
 _VC_SIZE = 8
+_IDLE_TIME = 0.5
+_FALLBACK_QUEUE = 3
 
 
 def _require_steer(cfg):
@@ -138,18 +141,19 @@ _VC_SIZE = 8
     ethtool(f"-X {cfg.ifname} {ctx}weight {weights}")
 
 
-def _require_rss_steering(cfg):
-    """Skip unless the Rx queue actually follows the RSS table
+def _rss_steers(cfg):
+    """Does the Rx queue actually follow the RSS table?
 
-    Steering can only be shown to outrank RSS on a device where RSS has
-    a say in the first place - netdevsim, for one, ignores the table.
+    Steering can only be compared against RSS on a device where RSS has a
+    say in the first place - netdevsim, for one, ignores the table. Leaves
+    the table as it found it.
     """
     probe = cfg.rx_queue_cnt - 1
 
     try:
         _rss_pin(cfg, probe)
-    except CmdExitFailure as exc:
-        raise KsftSkipEx("Device does not support RSS table updates") from exc
+    except CmdExitFailure:
+        return False
     defer(ethtool, f"-X {cfg.ifname} default")
 
     with make_clr_conn(cfg) as s:
@@ -157,10 +161,22 @@ _VC_SIZE = 8
         landed = _rx_queue(cfg, s)
         close_conn(cfg, s)
 
-    if landed != probe:
+    ethtool(f"-X {cfg.ifname} default")
+    return landed == probe
+
+
+def _require_rss_steering(cfg):
+    """Skip unless the Rx queue follows the RSS table"""
+    if not _rss_steers(cfg):
         raise KsftSkipEx("Rx queue does not follow the RSS table")
 
 
+def _set_queue_cnt(cfg, cnt):
+    """Reconfigure the device, and re-read the NAPI ids it hands out"""
+    ethtool(f"-L {cfg.ifname} combined {cnt}")
+    _queue_info(cfg)
+
+
 def _ntuple_l3_rule(cfg, target):
     """Steer this host's traffic with an L3 only rule, and clean it up
 
@@ -378,6 +394,50 @@ _VC_SIZE = 8
         close_conn(cfg, s)
 
 
+def data_steer_stale_queue(cfg):
+    """ A cookie naming a queue which went away has to fall back to RSS """
+    _require_steer(cfg)
+    _require_queues(cfg, 8)
+    _enable_steer(cfg)
+
+    nq = cfg.rx_queue_cnt
+    rss = _rss_steers(cfg)
+    if rss:
+        # Point RSS at a queue of our choosing so that "it fell back to
+        # RSS" is a statement we can actually check. Deliberately not
+        # queue 0: plenty of devices use that as a default or error queue,
+        # and landing there would prove nothing - it would also be a
+        # thundering herd waiting to happen if every stale flow went there.
+        _rss_pin(cfg, _FALLBACK_QUEUE)
+
+    defer(_set_queue_cnt, cfg, nq)
+    defer(_force_tx_queue, cfg, -1)
+    _force_tx_queue(cfg, nq - 1)
+
+    with _psp_conn(cfg) as s:
+        qid, _ = _settled_rx_queue(cfg, s, 0)
+        ksft_eq(qid, nq - 1)
+
+        # Go properly idle first. A delayed ACK landing after the
+        # reconfiguration would carry a fresh request and teach the peer a
+        # live queue, and we would end up measuring nothing.
+        time.sleep(_IDLE_TIME)
+
+        # Take the queue away without telling the peer, which goes on
+        # asking for it in every cookie it sends.
+        _set_queue_cnt(cfg, nq - 1)
+
+        # One packet, so that our ACK cannot teach the peer a new queue
+        # before we get to look at where this one landed.
+        req_echo(cfg, s)
+        qid = _rx_queue(cfg, s)
+        close_conn(cfg, s)
+
+    ksft_lt(qid, nq - 1, comment="delivered to a queue which no longer exists")
+    if rss:
+        ksft_eq(qid, _FALLBACK_QUEUE, comment="stale request did not fall back to RSS")
+
+
 def _queue_info(cfg):
     """Map NAPI ids to Rx queue ids, and count the queues"""
     netnl = NetdevFamily()
@@ -398,7 +458,7 @@ _VC_SIZE = 8
 def main() -> None:
     """ Ksft boiler plate main """
 
-    with NetDrvEpEnv(__file__, queue_count=4) as cfg:
+    with NetDrvEpEnv(__file__, queue_count=8) as cfg:
         cfg.pspnl = PSPFamily()
         _queue_info(cfg)
 
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help