From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from www62.your-server.de (www62.your-server.de [213.133.104.62]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95417347FEE for ; Thu, 11 Jun 2026 08:25:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.133.104.62 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781166334; cv=none; b=ZzNUZ8YOGfby2yGS0nqIQyaYys+8skwRIeZHmP0r6Fr0Lva04eS+H5yPxYuBh+/HW4sULQ9phzEqw3SUFGsdcorBOXsLpqjzt6Q1DdFZHwKadpxuORJNg9MT9kUHIqLvwaaBwfo6SQSTQLoEWZ7sXkuITzCP9CYGn6gz/I+uu8Y= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781166334; c=relaxed/simple; bh=ivI7HDhMP3IELNfDfdXbbkSguyRJK8VPfid/vNTqOOA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oCRjT1fXRqs63TOJyTZUsKUbs/gjlPoQVh6ivS3JiaeBoq7neU6tsubcRde2JYnLIVWSTcUA9AQQTQgXXHHylAWC6cB7Nqel84ID0ZLVcbrm/ej6B6Qg2ofGx0GgxDZQbkc01DCy5C1QF2Um8RxYQgepgpjaaBLkxYLyDqqtJks= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=iogearbox.net; spf=pass smtp.mailfrom=iogearbox.net; dkim=pass (2048-bit key) header.d=iogearbox.net header.i=@iogearbox.net header.b=eyh49o3e; arc=none smtp.client-ip=213.133.104.62 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=iogearbox.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=iogearbox.net header.i=@iogearbox.net header.b="eyh49o3e" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=iogearbox.net; s=default2302; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID; bh=K1PXEjTsKYyYFML7jHnRhmgLb5kGBH4FlrIUr2KPspc=; b=eyh49o3eP8z553DDcW+NLG28ke h59hOBImXaUTowpGonwDWOMezADL27FV2mF6FAIIGe1p8OjC8K9WJQkSEMEQfUDwFVPny7gMHH1kV hb2tl9rjqU02gKpRUwA68NXU6JuhFiY1fbe8yOP9zYS1TNoKG/Cg4qIBBPinCqaJpcXJgyXUbTmcr 0rhL/z8MuI0DtA/bYvHDkOAP8g4KNCc/mSBHkroP9yI2mmD7ND/lo9LjaOxrgctiOj3HcUYQpu9hb 42XAULkn7GeHPc/tHGbo6nlk3FQ7x4+VDEAMirLAu70FKTd+mNQsUMyFpO9p2riWEbFvvkodya1WY WvI2thpA==; Received: from localhost ([127.0.0.1]) by www62.your-server.de with esmtpsa (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.96.2) (envelope-from ) id 1wXajB-0009rB-0U; Thu, 11 Jun 2026 10:25:29 +0200 From: Daniel Borkmann To: kuba@kernel.org Cc: razor@blackwall.org, bobbyeshleman@meta.com, dw@davidwei.uk, netdev@vger.kernel.org Subject: [PATCH net-next v2 1/4] selftests/net: Move netkit lease hw setup into per-test fixtures Date: Thu, 11 Jun 2026 10:25:24 +0200 Message-ID: <20260611082527.741674-2-daniel@iogearbox.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260611082527.741674-1-daniel@iogearbox.net> References: <20260611082527.741674-1-daniel@iogearbox.net> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Virus-Scanned: Clear (ClamAV 1.4.3/28028/Thu Jun 11 08:27:22 2026) 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. While at it, convert the open-coded "ip netns exec" prefixes in the test bodies over to the ns= argument of cmd() / bkg(). Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/netdev/20260408162238.16709090@kernel.org/ [0] Reviewed-by: Bobby Eshleman --- .../selftests/drivers/net/hw/nk_qlease.py | 198 +++++++++++++----- 1 file changed, 149 insertions(+), 49 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..c7cb039b8181 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,40 +177,47 @@ 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"{cfg.bin_local} -s -p {cfg.port} " + f"-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): + with bkg(rx_cmd, exit_wait=True, ns=cfg.netns): wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns) cmd(tx_cmd, host=cfg.remote) 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}" - with bkg(rx_cmd): + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue}" + ) + with bkg(rx_cmd, ns=cfg.netns): 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_proc = cmd(rx_cmd, background=True) + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue}" + ) + rx_proc = cmd(rx_cmd, background=True, ns=cfg.netns) 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