xref: /linux/tools/testing/selftests/drivers/net/hw/nk_qlease.py (revision 91a4855d6c03e770e42f17c798a36a3c46e63de2)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4import re
5import time
6import threading
7from os import path
8from lib.py import (
9    ksft_run,
10    ksft_exit,
11    ksft_eq,
12    ksft_in,
13    ksft_not_in,
14    ksft_raises,
15)
16from lib.py import (
17    NetDrvContEnv,
18    NetNSEnter,
19    EthtoolFamily,
20    NetdevFamily,
21)
22from lib.py import (
23    bkg,
24    cmd,
25    defer,
26    ethtool,
27    ip,
28    rand_port,
29    wait_port_listen,
30)
31from lib.py import KsftSkipEx, CmdExitFailure
32
33
34def set_flow_rule(cfg):
35    output = ethtool(
36        f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {cfg.src_queue}"
37    ).stdout
38    values = re.search(r"ID (\d+)", output).group(1)
39    return int(values)
40
41
42def test_iou_zcrx(cfg) -> None:
43    cfg.require_ipver("6")
44    ethnl = EthtoolFamily()
45
46    rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
47    rx_rings = rings["rx"]
48    hds_thresh = rings.get("hds-thresh", 0)
49
50    ethnl.rings_set(
51        {
52            "header": {"dev-index": cfg.ifindex},
53            "tcp-data-split": "enabled",
54            "hds-thresh": 0,
55            "rx": 64,
56        }
57    )
58    defer(
59        ethnl.rings_set,
60        {
61            "header": {"dev-index": cfg.ifindex},
62            "tcp-data-split": "unknown",
63            "hds-thresh": hds_thresh,
64            "rx": rx_rings,
65        },
66    )
67
68    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
69    defer(ethtool, f"-X {cfg.ifname} default")
70
71    flow_rule_id = set_flow_rule(cfg)
72    defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
73
74    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}"
75    tx_cmd = f"{cfg.bin_remote} -c -h {cfg.nk_guest_ipv6} -p {cfg.port} -l 12840"
76    with bkg(rx_cmd, exit_wait=True):
77        wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
78        cmd(tx_cmd, host=cfg.remote)
79
80
81def test_attrs(cfg) -> None:
82    cfg.require_ipver("6")
83    netdevnl = NetdevFamily()
84    queue_info = netdevnl.queue_get(
85        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
86    )
87
88    ksft_eq(queue_info["id"], cfg.src_queue)
89    ksft_eq(queue_info["type"], "rx")
90    ksft_eq(queue_info["ifindex"], cfg.ifindex)
91
92    ksft_in("lease", queue_info)
93    lease = queue_info["lease"]
94    ksft_eq(lease["ifindex"], cfg.nk_guest_ifindex)
95    ksft_eq(lease["queue"]["id"], cfg.nk_queue)
96    ksft_eq(lease["queue"]["type"], "rx")
97    ksft_in("netns-id", lease)
98
99
100def test_attach_xdp_with_mp(cfg) -> None:
101    cfg.require_ipver("6")
102    ethnl = EthtoolFamily()
103
104    rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
105    rx_rings = rings["rx"]
106    hds_thresh = rings.get("hds-thresh", 0)
107
108    ethnl.rings_set(
109        {
110            "header": {"dev-index": cfg.ifindex},
111            "tcp-data-split": "enabled",
112            "hds-thresh": 0,
113            "rx": 64,
114        }
115    )
116    defer(
117        ethnl.rings_set,
118        {
119            "header": {"dev-index": cfg.ifindex},
120            "tcp-data-split": "unknown",
121            "hds-thresh": hds_thresh,
122            "rx": rx_rings,
123        },
124    )
125
126    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
127    defer(ethtool, f"-X {cfg.ifname} default")
128
129    netdevnl = NetdevFamily()
130
131    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}"
132    with bkg(rx_cmd):
133        wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
134
135        time.sleep(0.1)
136        queue_info = netdevnl.queue_get(
137            {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
138        )
139        ksft_in("io-uring", queue_info)
140
141        prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
142        with ksft_raises(CmdExitFailure):
143            ip(f"link set dev {cfg.ifname} xdp obj {prog} sec xdp.frags")
144
145    time.sleep(0.1)
146    queue_info = netdevnl.queue_get(
147        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
148    )
149    ksft_not_in("io-uring", queue_info)
150
151
152def test_destroy(cfg) -> None:
153    cfg.require_ipver("6")
154    ethnl = EthtoolFamily()
155
156    rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}})
157    rx_rings = rings["rx"]
158    hds_thresh = rings.get("hds-thresh", 0)
159
160    ethnl.rings_set(
161        {
162            "header": {"dev-index": cfg.ifindex},
163            "tcp-data-split": "enabled",
164            "hds-thresh": 0,
165            "rx": 64,
166        }
167    )
168    defer(
169        ethnl.rings_set,
170        {
171            "header": {"dev-index": cfg.ifindex},
172            "tcp-data-split": "unknown",
173            "hds-thresh": hds_thresh,
174            "rx": rx_rings,
175        },
176    )
177
178    ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
179    defer(ethtool, f"-X {cfg.ifname} default")
180
181    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}"
182    rx_proc = cmd(rx_cmd, background=True)
183    wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
184
185    netdevnl = NetdevFamily()
186    queue_info = netdevnl.queue_get(
187        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
188    )
189    ksft_in("io-uring", queue_info)
190
191    # ip link del will wait for all refs to drop first, but iou-zcrx is holding
192    # onto a ref. Terminate iou-zcrx async via a thread after a delay.
193    kill_timer = threading.Timer(1, rx_proc.proc.terminate)
194    kill_timer.start()
195
196    ip(f"link del dev {cfg._nk_host_ifname}")
197    kill_timer.join()
198    cfg._nk_host_ifname = None
199    cfg._nk_guest_ifname = None
200
201    queue_info = netdevnl.queue_get(
202        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
203    )
204    ksft_not_in("io-uring", queue_info)
205
206    cmd(f"tc filter del dev {cfg.ifname} ingress pref {cfg._bpf_prog_pref}")
207    cfg._tc_attached = False
208
209    flow_rule_id = set_flow_rule(cfg)
210    defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
211
212    rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.src_queue}"
213    tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840"
214    with bkg(rx_cmd, exit_wait=True):
215        wait_port_listen(cfg.port, proto="tcp")
216        cmd(tx_cmd, host=cfg.remote)
217    # Short delay since iou cleanup is async and takes a bit of time.
218    time.sleep(0.1)
219    queue_info = netdevnl.queue_get(
220        {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
221    )
222    ksft_not_in("io-uring", queue_info)
223
224
225def main() -> None:
226    with NetDrvContEnv(__file__, rxqueues=2) as cfg:
227        cfg.bin_local = path.abspath(
228            path.dirname(__file__) + "/../../../drivers/net/hw/iou-zcrx"
229        )
230        cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
231        cfg.port = rand_port()
232
233        ethnl = EthtoolFamily()
234        channels = ethnl.channels_get({"header": {"dev-index": cfg.ifindex}})
235        channels = channels["combined-count"]
236        if channels < 2:
237            raise KsftSkipEx("Test requires NETIF with at least 2 combined channels")
238
239        cfg.src_queue = channels - 1
240
241        with NetNSEnter(str(cfg.netns)):
242            netdevnl = NetdevFamily()
243            bind_result = netdevnl.queue_create(
244                {
245                    "ifindex": cfg.nk_guest_ifindex,
246                    "type": "rx",
247                    "lease": {
248                        "ifindex": cfg.ifindex,
249                        "queue": {"id": cfg.src_queue, "type": "rx"},
250                        "netns-id": 0,
251                    },
252                }
253            )
254            cfg.nk_queue = bind_result["id"]
255
256        # test_destroy must be last because it destroys the netkit devices
257        ksft_run(
258            [test_iou_zcrx, test_attrs, test_attach_xdp_with_mp, test_destroy],
259            args=(cfg,),
260        )
261    ksft_exit()
262
263
264if __name__ == "__main__":
265    main()
266