From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
To: <netdev-bot+sashiko@kernel.org>
Cc: <netdev@vger.kernel.org>, <bpf@vger.kernel.org>,
<magnus.karlsson@intel.com>, <stfomichev@gmail.com>,
<kuba@kernel.org>, <pabeni@redhat.com>,
<tushar.vyavahare@intel.com>, <kerneljasonxing@gmail.com>,
<bjorn@kernel.org>
Subject: Re: [PATCH v3 net-next 14/15] selftests: drv-net: test AF_XDP zero-copy with an SKB peer
Date: Sat, 17 Oct 2026 20:50:39 +0200 [thread overview]
Message-ID: <atPDf2DpOfWLt9cT@boxer> (raw)
In-Reply-To: <179220637173.434549.12610717858136223745@kernel.org>
On Sat, Oct 17, 2026 at 03:06:11AM +0000, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 8 potential
> issue(s) to consider.
>
> Critical: 0 · High: 0 · Medium: 6 · Low: 2
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [Medium] In tools/testing/selftests/drivers/net/hw/xsk.py, main()
> changes state on both hosts and has no try/finally to undo it.
> - [Medium] In xsk.py, _run() starts the DUT zero-copy endpoint with
> `local = cmd(local_args, background=True, fail=False)` at line 142 and…
> - [Medium] main() in xsk.py calls `xsk_reserve_hugepages(saved_hugepages,
> cfg.remote)` (line 227), and xsk_reserve_hugepages()…
> - [Medium] If an endpoint is killed, the NIC's TX/RX descriptor ring
> sizes are never restored.
> - [Medium] Taking the zero-copy queue out of RSS (_reserve_queue(),
> xsk.py line 74) is the only thing keeping non-test traffic off it.
> - [Medium] _reserve_queue() in xsk.py assumes every reported channel can
> be addressed by RSS.
> - [Low] The commit message says 'A host without ntuple support skips' and
> 'A DUT with fewer than two such queues skips'.
> - [Low] In xsk.py, _steer_udp() runs `ethtool -K <if> ntuple-filters on`
> (line 86) when the feature is inactive and not fixed.
>
I believe here I am done with responding to comments from our beloved
bot. There is one issue that indeed needs a fix before getting this in
(see below at ATR comment). Let me know if any fixups are possible...
> --
>
> selftests: drv-net: test AF_XDP zero-copy with an SKB peer
>
> This adds drivers/net/hw/xsk.py. It runs the xskxceiver cases as pairs of
> processes: a zero-copy endpoint on the DUT and an SKB-mode endpoint on the
> remote. main() sets up the shared state once (an RSS-excluded queue, ntuple
> rules on both hosts, huge pages) and undoes it after ksft_run().
>
> > diff --git a/tools/testing/selftests/drivers/net/hw/xsk.py b/tools/testing/selftests/drivers/net/hw/xsk.py
> > new file mode 100755
> > index 000000000000..fd708bb3420d
> > --- /dev/null
> > +++ b/tools/testing/selftests/drivers/net/hw/xsk.py
> > @@ -0,0 +1,263 @@
>
> [ ... ]
>
> > +def _reserve_queue(cfg):
> > + """Take the last queue with both an RX and a TX ring out of RSS. A TX
> > + endpoint fills no RX buffers, so in zero-copy mode its queue drops what
> > + it receives, the control connection included."""
> > + ch = cfg.ethnl.channels_get({"header": {"dev-index": cfg.ifindex}})
> > + count = ch.get("combined-count", 0) + min(ch.get("rx-count", 0),
> > + ch.get("tx-count", 0))
>
> [Severity: Medium]
> Does this assume that the RSS indirection table can address every reported
> channel?
>
> On pre-X550 ixgbe (82599/X540) with ATR enabled, which is the default,
> ixgbe_get_channels() reports the Flow Director queue count:
>
> drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c:ixgbe_get_channels() {
> ...
> /* report flow director queues as maximum channels */
> ch->combined_count = adapter->ring_feature[RING_F_FDIR].indices;
> ...
> }
>
> That count can be as high as 64. The RSS table on those parts can only
> address 16 queues:
>
> ixgbe_rss_indir_tbl_max() {
> if (adapter->hw.mac.type < ixgbe_mac_X550)
> return 16;
> ...
> }
>
> ixgbe_set_rxfh() rejects entries beyond that limit:
>
> for (i = 0; i < reta_entries; i++)
> if (rxfh->indir[i] >= max_queues)
> return -EINVAL;
>
> On a host with more than 17 CPUs, would "ethtool -X <netif> equal 63" fail
> and raise CmdExitFailure during setup? That would abort the suite on a NIC
> that advertises AF_XDP zero-copy. In this configuration the chosen queue is
> already outside RSS, so the table doesn't need to change.
>
> > + if count < 2:
> > + return None
> > + queue = count - 1
> > + ethtool(f"-X {cfg.ifname} equal {queue}")
in v4 rejected ethtool -X skips the run instead of raising exitfailure and
any previous setup is undone.
Current get_channels() output interpretation is targeted towards modern
NICs, but later on if there is really such need we could check if chosen
queue is already out of RSS...?
>
> [Severity: Medium]
> On NICs that steer flows based on TX, is taking the queue out of RSS
> enough to keep the TCP control connection off it?
>
> Flow Director ATR is on by default on i40e. i40e_atr() samples TCP TX
> packets, including the SYN. It then programs an FD filter that steers the
> flow's RX to the queue of the TX ring:
>
> drivers/net/ethernet/intel/i40e/i40e_txrx.c:i40e_atr() {
> ...
> flex_ptype = FIELD_PREP(I40E_TXD_FLTR_QW0_QINDEX_MASK,
> tx_ring->queue_index);
> ...
> }
SO CLOSE! This one has to be addressed as atr is turned on by default and
this could cause i40e runs being stochastic. I must admit I did not have
i40e setup on my side, but it has full AF_XDP feature support (including
mbuf zc) so its coverage matters.
Fix would be:
diff --git a/tools/testing/selftests/drivers/net/hw/xsk.py b/tools/testing/selftests/drivers/net/hw/xsk.py
--- a/tools/testing/selftests/drivers/net/hw/xsk.py
+++ b/tools/testing/selftests/drivers/net/hw/xsk.py
@@ -115,6 +115,16 @@ def _reserve_queue(cfg, undo):
return queue
+def _disable_atr(cfg, undo):
+ """i40e ATR steers what a TCP connection receives to the queue that it
+ sends from, which can be the zero-copy one, so turn it off until undo."""
+ flags = cmd(f"ethtool --show-priv-flags {cfg.ifname}", fail=False).stdout
+ if re.search(r"^flow-director-atr\s*: on$", flags, re.MULTILINE):
+ priv = f"--set-priv-flags {cfg.ifname} flow-director-atr"
+ ethtool(f"{priv} off")
+ undo.callback(ethtool, f"{priv} on")
+
+
def _steer_udp(cfg, undo, ifname, queue, host=None):
"""Steer the test flow to queue until undo, return False if ifname
cannot."""
@@ -313,6 +323,7 @@ def main():
cfg.queue = _reserve_queue(cfg, undo)
if cfg.queue is None:
sys.exit(SKIP_CODE)
+ _disable_atr(cfg, undo)
cfg.udp_port = rand_port()
steered = (_steer_udp(cfg, undo, cfg.ifname, cfg.queue) and
>
> FD filters take priority over RSS. Suppose XPS picks the last TX queue for
> the CPU that the DUT xskxceiver runs on. The remote's replies on the
> control connection would then land on the zero-copy queue.
>
> In TX cases that queue has no fill buffers. Wouldn't the control
> connection stall, so the case fails or times out depending on CPU
> placement?
>
> Only a udp4 ntuple rule is installed. Unlike a TCP4 sideband rule, it
> doesn't disable ATR automatically, and the flow-director-atr private flag
> is not cleared.
>
> Some drivers gate aRFS on NETIF_F_NTUPLE, which _steer_udp() may turn on.
> When RFS is configured, aRFS could steer flows the same way.
>
> > + return queue
> > +
> > +
> > +def _steer_udp(cfg, ifname, port, queue, host=None):
> > + """Steer the test flow to queue, return False if ifname cannot."""
> > + ntuple = ethtool(f"-k {ifname}", json=True, host=host)[0]["ntuple-filters"]
> > + if not ntuple["active"]:
> > + if ntuple["fixed"]:
> > + print(f"1..0 # SKIP {ifname} does not support ntuple-filters")
> > + return False
> > +
> > + ethtool(f"-K {ifname} ntuple-filters on", host=host)
> > +
> > + out = ethtool(f"-N {ifname} flow-type udp4 dst-port {port} action {queue}",
> > + host=host).stdout
> > + rule = re.search(r"ID (\d+)", out).group(1)
>
> [Severity: Low]
> The commit message says:
>
> A host without ntuple support skips.
>
> and:
>
> A DUT with fewer than two such queues skips
>
> Here a skip happens only when ntuple-filters is fixed off. There are two
> other failure cases:
>
> - The feature can be toggled but the NIC rejects the udp4 dst-port rule.
> ethtool -N then raises CmdExitFailure.
> - The output has no "ID N". The re.search(...).group(1) then raises
> AttributeError.
>
> Would either of these bypass the skip branch in main()? The run would then
> error out instead of skipping, and the _unsteer_udp() and "ethtool -X
> default" calls in that branch would never run. If only the remote rule
> fails, the DUT rule that was already added would also be left behind.
>
> Similarly, _reserve_queue() runs "ethtool -X equal" with fail=True. A DUT
> that rejects the RSS table update, such as the pre-X550 ixgbe case above,
> would crash during setup instead of skipping.
>
> > + cfg.rules.append((host, ifname, rule, ntuple["active"]))
>
> [Severity: Low]
> When ntuple-filters was inactive, "ethtool -K ... ntuple-filters on" turns
> it on before the rule is added. The toggle is recorded only here, after
> both ethtool -N and the ID parse succeed.
>
> If either of those raises, the toggle is never recorded. _unsteer_udp()
> turns the feature off only for entries in cfg.rules.
>
> Could ntuple-filters be left enabled on that host after a failed run?
> Recording the undo right after the toggle would avoid that.
>
> > + return True
>
> [ ... ]
>
> > +def _setup(cfg, busy_poll):
>
> [ ... ]
>
> > + fallbacks = [
> > + defer(ip, f"link set dev {cfg.ifname} mtu {cfg.dev['mtu']}"),
> > + defer(ip, f"link set dev {cfg.remote_ifname} "
> > + f"mtu {cfg.remote_dev['mtu']}", host=cfg.remote),
> > + defer(ip, f"link set dev {cfg.ifname} xdpdrv off"),
> > + defer(ip, f"link set dev {cfg.remote_ifname} xdpgeneric off",
> > + host=cfg.remote),
> > + ]
>
> [Severity: Medium]
> What restores the NIC ring sizes when an endpoint is killed?
>
> xskxceiver changes ring sizes with set_hw_ring_size(). The only restore
> happens in cleanup_iface(), on normal exit:
Addressed in v4 by moving ring changes into xsk.py . The runner saves the
sizes and registers their restoration before starting the endpoints, so
restoration does not depend on endpoint cleanup running.
>
> tools/testing/selftests/net/lib/xsk/xskxceiver.c:cleanup_iface() {
> ...
> if (ifobj_has_cap(ifobj, XSK_CAP_HW_RING))
> hw_ring_size_reset(ifobj);
> ...
> }
>
> xskxceiver has no signal handling, so this restore is skipped in two
> cases:
>
> - xsk_wait_endpoint_exit() sends SIGKILL to the DUT endpoint after
> CASE_TIMEOUT.
> - timeout(1) sends SIGTERM to the remote endpoint.
>
> These fallbacks cover only the MTU and the XDP detach.
>
> The next endpoint's capability probe in test_xsk.c then adopts the
> leftover size as its default:
>
> if (!get_hw_ring_size(ifobj->ifname, &ifobj->ring)) {
> ifobj_set_cap(ifobj, XSK_CAP_HW_RING);
> ...
> ifobj->set_ring.default_tx = ifobj->ring.tx_pending;
> ifobj->set_ring.default_rx = ifobj->ring.rx_pending;
>
> Would a hung ring size case then leave the NIC with a min or max
> descriptor ring size for the rest of the run, and after it?
>
> > + return fallbacks
>
> [ ... ]
>
> > +def _run(cfg, local_args, remote_args, ctrl_port):
>
> [ ... ]
>
> > + try:
> > + head, listening = xsk_wait_listen(remote, ctrl_port, LISTEN_TIMEOUT)
> > + if listening:
> > + local = cmd(local_args, background=True, fail=False)
> > + dut_ret = xsk_wait_endpoint_exit(local, "dut", CASE_TIMEOUT)
> > + finally:
> > + # Even if the DUT endpoint did not run, so that the remote one is
> > + # gone before the next case starts.
> > + remote_ret = xsk_wait_endpoint_exit(remote, "remote", REMOTE_TIMEOUT,
> > + head)
>
> [Severity: Medium]
> Can the DUT endpoint be orphaned here? The finally reaps only the remote
> endpoint, and xsk_wait_endpoint_exit() kills the process only on a
> timeout:
>
> tools/testing/selftests/net/lib/py/xsk.py:xsk_wait_endpoint_exit() {
> try:
> endpoint.process(terminate=False, fail=False, timeout=timeout)
> except subprocess.TimeoutExpired:
> endpoint.proc.kill()
> ...
> }
Addressed in v4 in the shared helper: interruption kills and reaps the
endpoint before propagating the exception.
>
> When the kselftest runner times out, it sends SIGTERM to the Python
> process only. _ksft_intr then raises KsftTerminate inside the wait on
> local, and the exception propagates while local.proc is still running.
>
> ksft_run() catches the exception and runs ksft_flush_defer(). That
> detaches the XDP program and restores the MTU while the AF_XDP socket is
> still bound. main() then deletes the ntuple rules and resets RSS.
>
> xskxceiver would keep running after Python exits. It would later run its
> own cleanup_iface() against state that the runner has already changed.
>
> Should local also be killed in the finally?
>
> > + return dut_ret, remote_ret
>
> [ ... ]
>
> > +def main():
> > + # ksft_run() exits on -l and -h before main() can undo the setup.
> > + if "-l" in sys.argv or "-h" in sys.argv:
> > + sys.exit(f"{sys.argv[0]}: -l and -h are not supported")
>
> [ ... ]
>
> > + saved_hugepages = []
> > + cfg.hugepages = (xsk_reserve_hugepages(saved_hugepages) and
> > + xsk_reserve_hugepages(saved_hugepages, cfg.remote))
>
> [Severity: Medium]
> What happens when the remote kernel is built without CONFIG_HUGETLBFS?
> xsk_reserve_hugepages() starts with:
addressed in v4 and described on veth runner comments
>
> have = int(cmd("sysctl -n vm.nr_hugepages", host=host).stdout)
>
> vm.nr_hugepages exists only with CONFIG_HUGETLBFS (mm/hugetlb_sysctl.c).
> Without it the sysctl fails, and cmd() raises CmdExitFailure instead of
> the helper returning False.
>
> The commit message says the UNALIGNED cases are "left out without them".
> Wouldn't the whole suite abort before ksft_run() instead, leaving the RSS
> change and ntuple rules in place?
>
> The drivers/net/hw config covers the DUT, but nothing constrains the
> remote kernel.
>
> > +
> > + cases = _get_cases(cfg)
>
> [ ... ]
>
> > + ksft_run([ksft_variants(cases)(test_xsk),
> > + ksft_variants(cases)(test_xsk_busy_poll)], args=(cfg,))
> > +
> > + xsk_release_hugepages(saved_hugepages)
> > + _unsteer_udp(cfg)
> > + ethtool(f"-X {cfg.ifname} default")
>
> [Severity: Medium]
> If main() leaves early, does anything undo the RSS change, the ntuple
> rules and the huge page reservation? These three calls run only when
> ksft_run() returns normally, and the setup has no try/finally around it.
> NetDrvEpEnv.__exit__ doesn't know about cfg.rules or saved_hugepages, and
> defer() only works inside a case.
>
> Any exception after _reserve_queue() skips all three calls. Some examples:
addressed in v4 via ExitStack introduction, suggested by Bjorn
>
> - a CmdExitFailure from ethtool -N on either host
> - the AttributeError from the rule ID parse
> - a failing remote sysctl
> - a failing "xskxceiver -L" in _get_cases()
>
> The "-l"/"-h" guard at the top of main() matches only those exact tokens.
> ksft_run() parses arguments with:
>
> opts, _ = getopt.getopt(sys.argv[1:], 'hlt:T:')
>
> This accepts clustered options like "-lt NAME" or "-hl". It also calls
> sys.exit(1) on a GetoptError, such as an unknown option, --help, or -t
> with no value. All of these exit from inside ksft_run(), after the setup
> has already run.
>
> The commit message gives the reason for the guard as:
>
> so xsk.py rejects -l and -h, on which ksft_run() exits without
> returning
>
> sys.exit() raises SystemExit. Would a try/finally around the setup and
> ksft_run() cover these paths and the -l/-h case as well?
>
> There are two smaller points about the teardown itself:
>
> - "ethtool -X default" installs the driver's default indirection table,
> not the one that was in place before the run. A custom table set by
> the admin is lost.
> - If xsk_release_hugepages() fails, for example over ssh, the rule
> deletes and the RSS reset after it are skipped.
>
> > + ksft_exit()
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261014184539.774708-1-maciej.fijalkowski%40intel.com
next prev parent reply other threads:[~2026-10-17 18:50 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 18:45 [PATCH v3 net-next 00/15] selftests: net: migrate AF_XDP test suite over to net Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 01/15] selftests: xsk: factor endpoint work out of pthread wrappers Maciej Fijalkowski
2026-10-15 8:14 ` Björn Töpel
2026-10-14 18:45 ` [PATCH v3 net-next 02/15] selftests: xsk: size the TX UMEM in HW_SW_MAX_RING_SIZE Maciej Fijalkowski
2026-10-15 8:15 ` Björn Töpel
2026-10-15 18:46 ` sashiko-bot
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 14:40 ` Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 03/15] selftests: xsk: drop the single-interface loopback mode Maciej Fijalkowski
2026-10-15 8:17 ` Björn Töpel
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 14:50 ` Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 04/15] selftests/bpf: drop the test_progs AF_XDP wrapper Maciej Fijalkowski
2026-10-15 8:18 ` Björn Töpel
2026-10-14 18:45 ` [PATCH v3 net-next 05/15] selftests: net: add a generic rule for BPF skeletons Maciej Fijalkowski
2026-10-15 8:20 ` Björn Töpel
2026-10-14 18:45 ` [PATCH v3 net-next 06/15] selftests: xsk: move the AF_XDP test suite to selftests/net Maciej Fijalkowski
2026-10-15 8:22 ` Björn Töpel
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 15:31 ` Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 07/15] selftests: xsk: collect interface capabilities in struct xsk_caps Maciej Fijalkowski
2026-10-15 8:27 ` Björn Töpel
2026-10-14 18:45 ` [PATCH v3 net-next 08/15] selftests: xsk: split xskxceiver main() into setup, run and cleanup Maciej Fijalkowski
2026-10-15 8:29 ` Björn Töpel
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-14 18:45 ` [PATCH v3 net-next 09/15] selftests: xsk: run one test case per xskxceiver invocation Maciej Fijalkowski
2026-10-15 8:34 ` Björn Töpel
2026-10-15 18:46 ` sashiko-bot
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 16:04 ` Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 10/15] selftests: xsk: run the RX and TX endpoints in separate processes Maciej Fijalkowski
2026-10-15 9:04 ` Björn Töpel
2026-10-14 18:45 ` [PATCH v3 net-next 11/15] selftests: xsk: add a hardware mode to xskxceiver Maciej Fijalkowski
2026-10-15 9:42 ` Björn Töpel
2026-10-15 10:55 ` Maciej Fijalkowski
2026-10-15 16:03 ` Björn Töpel
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 17:40 ` Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 12/15] selftests: xsk: pass non-test traffic to the stack in hardware mode Maciej Fijalkowski
2026-10-15 10:05 ` Björn Töpel
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-14 18:45 ` [PATCH v3 net-next 13/15] selftests: xsk: list the hardware test cases Maciej Fijalkowski
2026-10-14 18:45 ` [PATCH v3 net-next 14/15] selftests: drv-net: test AF_XDP zero-copy with an SKB peer Maciej Fijalkowski
2026-10-15 10:29 ` Björn Töpel
2026-10-15 12:52 ` Maciej Fijalkowski
2026-10-15 18:46 ` sashiko-bot
2026-10-17 3:06 ` netdev-bot+sashiko
2026-10-17 18:50 ` Maciej Fijalkowski [this message]
2026-10-14 18:45 ` [PATCH v3 net-next 15/15] selftests: xsk: document generic and hardware endpoint runs Maciej Fijalkowski
2026-10-17 3:06 ` netdev-bot+sashiko
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=atPDf2DpOfWLt9cT@boxer \
--to=maciej.fijalkowski@intel.com \
--cc=bjorn@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=kerneljasonxing@gmail.com \
--cc=kuba@kernel.org \
--cc=magnus.karlsson@intel.com \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stfomichev@gmail.com \
--cc=tushar.vyavahare@intel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.