Netdev List
 help / color / mirror / Atom feed
From: Daniel Borkmann <daniel@iogearbox.net>
To: kuba@kernel.org
Cc: razor@blackwall.org, bobbyeshleman@meta.com, dw@davidwei.uk,
	netdev@vger.kernel.org
Subject: [PATCH net-next 1/4] selftests/net: Move netkit lease hw setup into per-test fixtures
Date: Wed, 10 Jun 2026 10:03:41 +0200	[thread overview]
Message-ID: <20260610080344.701380-2-daniel@iogearbox.net> (raw)
In-Reply-To: <20260610080344.701380-1-daniel@iogearbox.net>

The HW counterpart of nk_qlease.py was carrying its lease setup in main()
and stashing src_queue / nk_queue / nk_*_ifname on cfg, which had drawbacks
called out during the review at [0].

This is the deferred half of the cleanup that landed in commit e254ffb9502c
("selftests/net: Split netdevsim tests from HW tests in nk_qlease") which
was the SW counterpart of nk_qlease.py.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/netdev/20260408162238.16709090@kernel.org/ [0]
---
 .../selftests/drivers/net/hw/nk_qlease.py     | 192 +++++++++++++-----
 1 file changed, 146 insertions(+), 46 deletions(-)

diff --git a/tools/testing/selftests/drivers/net/hw/nk_qlease.py b/tools/testing/selftests/drivers/net/hw/nk_qlease.py
index 139a91ebd229..81125856727f 100755
--- a/tools/testing/selftests/drivers/net/hw/nk_qlease.py
+++ b/tools/testing/selftests/drivers/net/hw/nk_qlease.py
@@ -18,8 +18,10 @@ from lib.py import (
     NetNSEnter,
     EthtoolFamily,
     NetdevFamily,
+    RtnlFamily,
 )
 from lib.py import (
+    Netlink,
     bkg,
     cmd,
     defer,
@@ -31,9 +33,117 @@ from lib.py import (
 from lib.py import KsftSkipEx, CmdExitFailure
 
 
-def set_flow_rule(cfg):
+def _create_netkit_pair(cfg, rxqueues=2):
+    if cfg._nk_host_ifname:
+        cmd(f"ip link del dev {cfg._nk_host_ifname}", fail=False)
+        cfg._nk_host_ifname = None
+        cfg.nk_guest_ifname = None
+    if getattr(cfg, "_tc_attached", False):
+        cmd(
+            f"tc filter del dev {cfg.ifname} ingress pref {cfg._bpf_prog_pref}",
+            fail=False,
+        )
+        cfg._tc_attached = False
+
+    all_links = ip("-d link show", json=True)
+    old_idxs = {
+        link["ifindex"]
+        for link in all_links
+        if link.get("linkinfo", {}).get("info_kind") == "netkit"
+    }
+
+    rtnl = RtnlFamily()
+    rtnl.newlink(
+        {
+            "linkinfo": {
+                "kind": "netkit",
+                "data": {
+                    "mode": "l2",
+                    "policy": "forward",
+                    "peer-policy": "forward",
+                },
+            },
+            "num-rx-queues": rxqueues,
+        },
+        flags=[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL],
+    )
+
+    all_links = ip("-d link show", json=True)
+    nk_links = [
+        link
+        for link in all_links
+        if link.get("linkinfo", {}).get("info_kind") == "netkit"
+        and link["ifindex"] not in old_idxs
+    ]
+    if len(nk_links) != 2:
+        raise KsftSkipEx("Failed to create netkit pair")
+
+    nk_links.sort(key=lambda x: x["ifindex"])
+    cfg._nk_host_ifname = nk_links[1]["ifname"]
+    cfg.nk_guest_ifname = nk_links[0]["ifname"]
+    cfg.nk_host_ifindex = nk_links[1]["ifindex"]
+    cfg.nk_guest_ifindex = nk_links[0]["ifindex"]
+
+    ip(f"link set dev {cfg.nk_guest_ifname} netns {cfg.netns.name}")
+    ip(f"link set dev {cfg._nk_host_ifname} up")
+    ip(f"-6 addr add fe80::1/64 dev {cfg._nk_host_ifname} nodad")
+    ip(
+        f"-6 route add {cfg.nk_guest_ipv6}/128 via fe80::2 "
+        f"dev {cfg._nk_host_ifname}"
+    )
+    ip(f"link set dev {cfg.nk_guest_ifname} up", ns=cfg.netns)
+    ip(f"-6 addr add fe80::2/64 dev {cfg.nk_guest_ifname}", ns=cfg.netns)
+    ip(
+        f"-6 addr add {cfg.nk_guest_ipv6}/64 dev {cfg.nk_guest_ifname} nodad",
+        ns=cfg.netns,
+    )
+    ip(
+        f"-6 route add default via fe80::1 dev {cfg.nk_guest_ifname}",
+        ns=cfg.netns,
+    )
+
+    cfg._attach_bpf()
+
+
+def _setup_lease(cfg, rxqueues=2):
+    _create_netkit_pair(cfg, rxqueues=rxqueues)
+
+    ethnl = EthtoolFamily()
+    channels = ethnl.channels_get({"header": {"dev-index": cfg.ifindex}})[
+        "combined-count"
+    ]
+    if channels < 2:
+        raise KsftSkipEx(
+            "Test requires NETIF with at least 2 combined channels"
+        )
+    src_queue = channels - 1
+
+    with NetNSEnter(str(cfg.netns)):
+        netdevnl = NetdevFamily()
+        bind_result = netdevnl.queue_create(
+            {
+                "ifindex": cfg.nk_guest_ifindex,
+                "type": "rx",
+                "lease": {
+                    "ifindex": cfg.ifindex,
+                    "queue": {"id": src_queue, "type": "rx"},
+                    "netns-id": 0,
+                },
+            }
+        )
+    return src_queue, bind_result["id"]
+
+
+def _teardown_netkit(cfg):
+    if cfg._nk_host_ifname:
+        cmd(f"ip link del dev {cfg._nk_host_ifname}", fail=False)
+        cfg._nk_host_ifname = None
+        cfg.nk_guest_ifname = None
+
+
+def set_flow_rule(cfg, src_queue):
     output = ethtool(
-        f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {cfg.src_queue}"
+        f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {src_queue}"
     ).stdout
     values = re.search(r"ID (\d+)", output).group(1)
     return int(values)
@@ -41,6 +151,8 @@ def set_flow_rule(cfg):
 
 def test_iou_zcrx(cfg) -> None:
     cfg.require_ipver("6")
+    src_queue, nk_queue = _setup_lease(cfg)
+    defer(_teardown_netkit, cfg)
     ethnl = EthtoolFamily()
 
     rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
@@ -65,13 +177,16 @@ def test_iou_zcrx(cfg) -> None:
         },
     )
 
-    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
+    ethtool(f"-X {cfg.ifname} equal {src_queue}")
     defer(ethtool, f"-X {cfg.ifname} default")
 
-    flow_rule_id = set_flow_rule(cfg)
+    flow_rule_id = set_flow_rule(cfg, src_queue)
     defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
 
-    rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
+    rx_cmd = (
+        f"ip netns exec {cfg.netns.name} {cfg.bin_local} "
+        f"-s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {nk_queue}"
+    )
     tx_cmd = f"{cfg.bin_remote} -c -h {cfg.nk_guest_ipv6} -p {cfg.port} -l 12840"
     with bkg(rx_cmd, exit_wait=True):
         wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
@@ -80,25 +195,29 @@ def test_iou_zcrx(cfg) -> None:
 
 def test_attrs(cfg) -> None:
     cfg.require_ipver("6")
+    src_queue, nk_queue = _setup_lease(cfg)
+    defer(_teardown_netkit, cfg)
     netdevnl = NetdevFamily()
     queue_info = netdevnl.queue_get(
-        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+        {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
     )
 
-    ksft_eq(queue_info["id"], cfg.src_queue)
+    ksft_eq(queue_info["id"], src_queue)
     ksft_eq(queue_info["type"], "rx")
     ksft_eq(queue_info["ifindex"], cfg.ifindex)
 
     ksft_in("lease", queue_info)
     lease = queue_info["lease"]
     ksft_eq(lease["ifindex"], cfg.nk_guest_ifindex)
-    ksft_eq(lease["queue"]["id"], cfg.nk_queue)
+    ksft_eq(lease["queue"]["id"], nk_queue)
     ksft_eq(lease["queue"]["type"], "rx")
     ksft_in("netns-id", lease)
 
 
 def test_attach_xdp_with_mp(cfg) -> None:
     cfg.require_ipver("6")
+    src_queue, nk_queue = _setup_lease(cfg)
+    defer(_teardown_netkit, cfg)
     ethnl = EthtoolFamily()
 
     rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
@@ -123,18 +242,21 @@ def test_attach_xdp_with_mp(cfg) -> None:
         },
     )
 
-    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
+    ethtool(f"-X {cfg.ifname} equal {src_queue}")
     defer(ethtool, f"-X {cfg.ifname} default")
 
     netdevnl = NetdevFamily()
 
-    rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
+    rx_cmd = (
+        f"ip netns exec {cfg.netns.name} {cfg.bin_local} "
+        f"-s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {nk_queue}"
+    )
     with bkg(rx_cmd):
         wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
 
         time.sleep(0.1)
         queue_info = netdevnl.queue_get(
-            {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+            {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
         )
         ksft_in("io-uring", queue_info)
 
@@ -144,13 +266,15 @@ def test_attach_xdp_with_mp(cfg) -> None:
 
     time.sleep(0.1)
     queue_info = netdevnl.queue_get(
-        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+        {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
     )
     ksft_not_in("io-uring", queue_info)
 
 
 def test_destroy(cfg) -> None:
     cfg.require_ipver("6")
+    src_queue, nk_queue = _setup_lease(cfg)
+    defer(_teardown_netkit, cfg)
     ethnl = EthtoolFamily()
 
     rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
@@ -175,16 +299,19 @@ def test_destroy(cfg) -> None:
         },
     )
 
-    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
+    ethtool(f"-X {cfg.ifname} equal {src_queue}")
     defer(ethtool, f"-X {cfg.ifname} default")
 
-    rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
+    rx_cmd = (
+        f"ip netns exec {cfg.netns.name} {cfg.bin_local} "
+        f"-s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {nk_queue}"
+    )
     rx_proc = cmd(rx_cmd, background=True)
     wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
 
     netdevnl = NetdevFamily()
     queue_info = netdevnl.queue_get(
-        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+        {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
     )
     ksft_in("io-uring", queue_info)
 
@@ -199,17 +326,14 @@ def test_destroy(cfg) -> None:
     cfg.nk_guest_ifname = None
 
     queue_info = netdevnl.queue_get(
-        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+        {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
     )
     ksft_not_in("io-uring", queue_info)
 
-    cmd(f"tc filter del dev {cfg.ifname} ingress pref {cfg._bpf_prog_pref}")
-    cfg._tc_attached = False
-
-    flow_rule_id = set_flow_rule(cfg)
+    flow_rule_id = set_flow_rule(cfg, src_queue)
     defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
 
-    rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.src_queue}"
+    rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {src_queue}"
     tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840"
     with bkg(rx_cmd, exit_wait=True):
         wait_port_listen(cfg.port, proto="tcp")
@@ -217,7 +341,7 @@ def test_destroy(cfg) -> None:
     # Short delay since iou cleanup is async and takes a bit of time.
     time.sleep(0.1)
     queue_info = netdevnl.queue_get(
-        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
+        {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"}
     )
     ksft_not_in("io-uring", queue_info)
 
@@ -230,30 +354,6 @@ def main() -> None:
         cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
         cfg.port = rand_port()
 
-        ethnl = EthtoolFamily()
-        channels = ethnl.channels_get({"header": {"dev-index": cfg.ifindex}})
-        channels = channels["combined-count"]
-        if channels < 2:
-            raise KsftSkipEx("Test requires NETIF with at least 2 combined channels")
-
-        cfg.src_queue = channels - 1
-
-        with NetNSEnter(str(cfg.netns)):
-            netdevnl = NetdevFamily()
-            bind_result = netdevnl.queue_create(
-                {
-                    "ifindex": cfg.nk_guest_ifindex,
-                    "type": "rx",
-                    "lease": {
-                        "ifindex": cfg.ifindex,
-                        "queue": {"id": cfg.src_queue, "type": "rx"},
-                        "netns-id": 0,
-                    },
-                }
-            )
-            cfg.nk_queue = bind_result["id"]
-
-        # test_destroy must be last because it destroys the netkit devices
         ksft_run(
             [test_iou_zcrx, test_attrs, test_attach_xdp_with_mp, test_destroy],
             args=(cfg,),
-- 
2.43.0


  reply	other threads:[~2026-06-10  8:03 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10  8:03 [PATCH net-next 0/4] Extend netkit io_uring ZC selftests Daniel Borkmann
2026-06-10  8:03 ` Daniel Borkmann [this message]
2026-06-10 23:50   ` [PATCH net-next 1/4] selftests/net: Move netkit lease hw setup into per-test fixtures Bobby Eshleman
2026-06-10  8:03 ` [PATCH net-next 2/4] selftests/net: Use public NetDrvContEnv API in nk_qlease fixtures Daniel Borkmann
2026-06-10 23:53   ` Bobby Eshleman
2026-06-10  8:03 ` [PATCH net-next 3/4] selftests/net: Add netkit io_uring ZC test for large rx_buf_len Daniel Borkmann
2026-06-10 23:28   ` Bobby Eshleman
2026-06-10  8:03 ` [PATCH net-next 4/4] selftests/net: Add hugepage kernel config dependency for zcrx Daniel Borkmann
2026-06-10 23:54   ` Bobby Eshleman

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=20260610080344.701380-2-daniel@iogearbox.net \
    --to=daniel@iogearbox.net \
    --cc=bobbyeshleman@meta.com \
    --cc=dw@davidwei.uk \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=razor@blackwall.org \
    /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