From: Bobby Eshleman <bobbyeshleman@gmail.com>
To: Daniel Borkmann <daniel@iogearbox.net>
Cc: kuba@kernel.org, razor@blackwall.org, bobbyeshleman@meta.com,
dw@davidwei.uk, netdev@vger.kernel.org
Subject: Re: [PATCH net-next 1/4] selftests/net: Move netkit lease hw setup into per-test fixtures
Date: Wed, 10 Jun 2026 16:50:19 -0700 [thread overview]
Message-ID: <ain4OzpmFM17XWqD@devvm29614.prn0.facebook.com> (raw)
In-Reply-To: <20260610080344.701380-2-daniel@iogearbox.net>
On Wed, Jun 10, 2026 at 10:03:41AM +0200, Daniel Borkmann wrote:
> 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
>
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
next prev parent reply other threads:[~2026-06-10 23:50 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 ` [PATCH net-next 1/4] selftests/net: Move netkit lease hw setup into per-test fixtures Daniel Borkmann
2026-06-10 23:50 ` Bobby Eshleman [this message]
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=ain4OzpmFM17XWqD@devvm29614.prn0.facebook.com \
--to=bobbyeshleman@gmail.com \
--cc=bobbyeshleman@meta.com \
--cc=daniel@iogearbox.net \
--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