Netdev List
 help / color / mirror / Atom feed
From: Jakub Kicinski <kuba@kernel.org>
To: daniel.zahka@gmail.com, willemdebruijn.kernel@gmail.com
Cc: edumazet@google.com, cratiu@nvidia.com, borisp@nvidia.com,
	kuniyu@google.com, netdev@vger.kernel.org,
	Jakub Kicinski <kuba@kernel.org>
Subject: [RFC net-next 5/6] selftests: drv-net: psp_steer: test where PSP steering sits in the Rx pipeline
Date: Sat, 22 Aug 2026 15:55:23 -0700	[thread overview]
Message-ID: <20260822225524.2328465-6-kuba@kernel.org> (raw)
In-Reply-To: <20260822225524.2328465-1-kuba@kernel.org>

Cover the steering priority / Rx pipeline ordering.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 .../selftests/drivers/net/psp_steer.py        | 171 +++++++++++++++++-
 1 file changed, 164 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/drivers/net/psp_steer.py b/tools/testing/selftests/drivers/net/psp_steer.py
index 0401a7c359e7..fac2d7532d61 100644
--- a/tools/testing/selftests/drivers/net/psp_steer.py
+++ b/tools/testing/selftests/drivers/net/psp_steer.py
@@ -10,17 +10,35 @@ import socket
 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 KsftSkipEx
+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_psp_conn, psp_txrx, \
-    remote_conn_steer, remote_dev_steer, spi_xchg
+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
 from psp_lib import responder as psp_responder
 
 # Not exposed by the socket module
 _SO_INCOMING_NAPI_ID = 56
 
+
+# Mirrors the helpers of the same name in hw/rss_ctx.py, which lives in a
+# directory this test cannot import from.
+def ethtool_create(cfg, act, opts):
+    output = ethtool(f"{act} {cfg.ifname} {opts}").stdout
+    # "New RSS context is 1" / "Added rule with ID 7", we want the integer
+    return int(output.split()[-1])
+
+
+def require_ntuple(cfg):
+    features = ethtool(f"-k {cfg.ifname}", json=True)[0]
+    if not features["ntuple-filters"]["active"]:
+        if features["ntuple-filters"]["fixed"]:
+            raise KsftSkipEx("Device does not support ntuple-filters")
+        ethtool(f"-K {cfg.ifname} ntuple-filters on")
+        defer(ethtool, f"-K {cfg.ifname} ntuple-filters off")
+
 _VC_TX = 1 << 0
 _VC_RX = 1 << 1
 _VC_BOTH = _VC_TX | _VC_RX
@@ -92,6 +110,14 @@ _VC_SIZE = 8
     return s
 
 
+def _rx_queue(cfg, s):
+    """Rx queue the socket's last packet arrived on"""
+    napi_id = s.getsockopt(socket.SOL_SOCKET, _SO_INCOMING_NAPI_ID)
+    ksft_ne(napi_id, 0, comment="socket saw no traffic?")
+    ksft_in(napi_id, cfg.napi2queue, comment="unknown NAPI id")
+    return cfg.napi2queue[napi_id]
+
+
 def _settled_rx_queue(cfg, s, sent):
     """Run traffic until the peer picked our request up, report the queue
 
@@ -101,10 +127,52 @@ _VC_SIZE = 8
     sent = psp_txrx(cfg, s, 1, sent)
     sent = psp_txrx(cfg, s, 1, sent)
 
-    napi_id = s.getsockopt(socket.SOL_SOCKET, _SO_INCOMING_NAPI_ID)
-    ksft_ne(napi_id, 0, comment="socket saw no traffic?")
-    ksft_in(napi_id, cfg.napi2queue, comment="unknown NAPI id")
-    return cfg.napi2queue[napi_id], sent
+    return _rx_queue(cfg, s), sent
+
+
+def _rss_pin(cfg, qid, context=None):
+    """Point an RSS indirection table at a single queue"""
+    ctx = f"context {context} " if context is not None else ""
+    weights = " ".join("1" if i == qid else "0"
+                       for i in range(cfg.rx_queue_cnt))
+    ethtool(f"-X {cfg.ifname} {ctx}weight {weights}")
+
+
+def _require_rss_steering(cfg):
+    """Skip unless the Rx queue actually follows 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.
+    """
+    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
+    defer(ethtool, f"-X {cfg.ifname} default")
+
+    with make_clr_conn(cfg) as s:
+        psp_txrx(cfg, s, 1)
+        landed = _rx_queue(cfg, s)
+        close_conn(cfg, s)
+
+    if landed != probe:
+        raise KsftSkipEx("Rx queue does not follow the RSS table")
+
+
+def _ntuple_l3_rule(cfg, target):
+    """Steer this host's traffic with an L3 only rule, and clean it up
+
+    L3 only on purpose: whether the classifier sees the inner TCP ports
+    of a PSP packet or just the outer UDP encapsulation is up to the
+    device, the addresses are there either way.
+    """
+    flow = (f"flow-type ip{cfg.addr_ipver} "
+            f"src-ip {cfg.remote_addr} dst-ip {cfg.addr} {target}")
+    rule = ethtool_create(cfg, "-N", flow)
+    defer(ethtool, f"-N {cfg.ifname} delete {rule}")
+    return rule
 
 
 #
@@ -221,6 +289,95 @@ _VC_SIZE = 8
         close_conn(cfg, s)
 
 
+def data_steer_no_grant(cfg):
+    """ We ask and nobody grants: nothing is steered """
+    _require_steer(cfg)
+    _require_queues(cfg, 3)
+    _require_rss_steering(cfg)
+    _enable_steer(cfg, local=_VC_RX, remote=0)
+
+    # RSS says 2, we would be asking for 1 if anyone were listening
+    _rss_pin(cfg, 2)
+    defer(_force_tx_queue, cfg, -1)
+    _force_tx_queue(cfg, 1)
+
+    with _psp_conn(cfg) as s:
+        qid, _ = _settled_rx_queue(cfg, s, 0)
+        ksft_eq(qid, 2, comment="steered without the peer granting anything")
+        close_conn(cfg, s)
+
+
+def data_steer_beats_rss(cfg):
+    """ Steering has to win over the RSS table """
+    _require_steer(cfg)
+    _require_queues(cfg, 3)
+    _enable_steer(cfg)
+    _require_rss_steering(cfg)
+
+    # RSS says 2, steering is going to ask for 1
+    _rss_pin(cfg, 2)
+    defer(_force_tx_queue, cfg, -1)
+    _force_tx_queue(cfg, 1)
+
+    with _psp_conn(cfg) as s:
+        qid, _ = _settled_rx_queue(cfg, s, 0)
+        ksft_eq(qid, 1)
+        close_conn(cfg, s)
+
+
+def data_steer_beats_rss_ctx(cfg):
+    """ Steering has to win over an additional RSS context as well """
+    _require_steer(cfg)
+    _require_queues(cfg, 3)
+    _enable_steer(cfg)
+    _require_rss_steering(cfg)
+    require_ntuple(cfg)
+
+    # Three distinct answers: default RSS says 0, the context says 2,
+    # and steering is going to ask for 1.
+    _rss_pin(cfg, 0)
+    ctx = ethtool_create(cfg, "-X", "context new")
+    defer(ethtool, f"-X {cfg.ifname} context {ctx} delete")
+    _rss_pin(cfg, 2, context=ctx)
+    _ntuple_l3_rule(cfg, f"context {ctx}")
+
+    # Without a cookie the context has to be the one deciding, otherwise
+    # the check below would pass without steering doing anything.
+    with make_clr_conn(cfg) as s:
+        psp_txrx(cfg, s, 1)
+        ksft_eq(_rx_queue(cfg, s), 2, comment="RSS context not in use")
+        close_conn(cfg, s)
+
+    defer(_force_tx_queue, cfg, -1)
+    _force_tx_queue(cfg, 1)
+
+    with _psp_conn(cfg) as s:
+        qid, _ = _settled_rx_queue(cfg, s, 0)
+        ksft_eq(qid, 1, comment="steering did not escape the RSS context")
+        close_conn(cfg, s)
+
+
+def data_ntuple_beats_steer(cfg):
+    """ An ntuple rule naming a queue outranks steering """
+    _require_steer(cfg)
+    _require_queues(cfg, 3)
+    _enable_steer(cfg)
+    _require_rss_steering(cfg)
+    require_ntuple(cfg)
+
+    # RSS says 0, the rule says 2, steering is going to ask for 1
+    _rss_pin(cfg, 0)
+    _ntuple_l3_rule(cfg, "action 2")
+
+    defer(_force_tx_queue, cfg, -1)
+    _force_tx_queue(cfg, 1)
+
+    with _psp_conn(cfg) as s:
+        qid, _ = _settled_rx_queue(cfg, s, 0)
+        ksft_eq(qid, 2, comment="steering overrode an explicit ntuple rule")
+        close_conn(cfg, s)
+
+
 def _queue_info(cfg):
     """Map NAPI ids to Rx queue ids, and count the queues"""
     netnl = NetdevFamily()
-- 
2.55.0


  parent reply	other threads:[~2026-08-22 22:55 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-22 22:55 [RFC net-next 0/6] psp: use virt cookie as Rx steering hint Jakub Kicinski
2026-08-22 22:55 ` [RFC net-next 1/6] psp: steer Rx queues with the virtualization cookie Jakub Kicinski
2026-08-23 15:31   ` Daniel Zahka
2026-08-24 15:01     ` Jakub Kicinski
2026-08-24 15:09       ` Cosmin Ratiu
2026-08-24 15:19         ` Jakub Kicinski
2026-08-23 18:18   ` Willem de Bruijn
2026-08-22 22:55 ` [RFC net-next 2/6] netdevsim: support PSP VC based queue steering Jakub Kicinski
2026-08-22 22:55 ` [RFC net-next 3/6] selftests: drv-net: psp: move the PSP test plumbing into psp_lib.py Jakub Kicinski
2026-08-22 22:55 ` [RFC net-next 4/6] selftests: drv-net: psp_steer: test PSP VC based queue steering Jakub Kicinski
2026-08-22 22:55 ` Jakub Kicinski [this message]
2026-08-22 22:55 ` [RFC net-next 6/6] selftests: drv-net: psp_steer: cover corner cases and races Jakub Kicinski
2026-08-23 17:48 ` [RFC net-next 0/6] psp: use virt cookie as Rx steering hint Willem de Bruijn
2026-08-24 15:05   ` Cosmin Ratiu
2026-08-25  9:52     ` Cosmin Ratiu
2026-08-25 18:55       ` Jakub Kicinski
2026-08-24 15:11   ` Jakub Kicinski
2026-08-24 18:04     ` Willem de Bruijn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260822225524.2328465-6-kuba@kernel.org \
    --to=kuba@kernel.org \
    --cc=borisp@nvidia.com \
    --cc=cratiu@nvidia.com \
    --cc=daniel.zahka@gmail.com \
    --cc=edumazet@google.com \
    --cc=kuniyu@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=willemdebruijn.kernel@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox