Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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()