Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / testing / selftests / drivers / net / shaper.py
blob11310f19bfa02884819733d7c6cad5e83662858e
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0
4 from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_true, KsftSkipEx
5 from lib.py import EthtoolFamily, NetshaperFamily
6 from lib.py import NetDrvEnv
7 from lib.py import NlError
8 from lib.py import cmd
10 def get_shapers(cfg, nl_shaper) -> None:
11 try:
12 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
13 except NlError as e:
14 if e.error == 95:
15 raise KsftSkipEx("shapers not supported by the device")
16 raise
18 # Default configuration: no shapers configured.
19 ksft_eq(len(shapers), 0)
21 def get_caps(cfg, nl_shaper) -> None:
22 try:
23 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex}, dump=True)
24 except NlError as e:
25 if e.error == 95:
26 raise KsftSkipEx("shapers not supported by the device")
27 raise
29 # Each device implementing shaper support must support some
30 # features in at least a scope.
31 ksft_true(len(caps)> 0)
33 def set_qshapers(cfg, nl_shaper) -> None:
34 try:
35 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
36 'scope':'queue'})
37 except NlError as e:
38 if e.error == 95:
39 raise KsftSkipEx("shapers not supported by the device")
40 raise
41 if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
42 raise KsftSkipEx("device does not support queue scope shapers with bw_max and metric bps")
44 cfg.queues = True;
45 netnl = EthtoolFamily()
46 channels = netnl.channels_get({'header': {'dev-index': cfg.ifindex}})
47 if channels['combined-count'] == 0:
48 cfg.rx_type = 'rx'
49 cfg.nr_queues = channels['rx-count']
50 else:
51 cfg.rx_type = 'combined'
52 cfg.nr_queues = channels['combined-count']
53 if cfg.nr_queues < 3:
54 raise KsftSkipEx(f"device does not support enough queues min 3 found {cfg.nr_queues}")
56 nl_shaper.set({'ifindex': cfg.ifindex,
57 'handle': {'scope': 'queue', 'id': 1},
58 'metric': 'bps',
59 'bw-max': 10000})
60 nl_shaper.set({'ifindex': cfg.ifindex,
61 'handle': {'scope': 'queue', 'id': 2},
62 'metric': 'bps',
63 'bw-max': 20000})
65 # Querying a specific shaper not yet configured must fail.
66 raised = False
67 try:
68 shaper_q0 = nl_shaper.get({'ifindex': cfg.ifindex,
69 'handle': {'scope': 'queue', 'id': 0}})
70 except (NlError):
71 raised = True
72 ksft_eq(raised, True)
74 shaper_q1 = nl_shaper.get({'ifindex': cfg.ifindex,
75 'handle': {'scope': 'queue', 'id': 1}})
76 ksft_eq(shaper_q1, {'ifindex': cfg.ifindex,
77 'parent': {'scope': 'netdev'},
78 'handle': {'scope': 'queue', 'id': 1},
79 'metric': 'bps',
80 'bw-max': 10000})
82 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
83 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
84 'parent': {'scope': 'netdev'},
85 'handle': {'scope': 'queue', 'id': 1},
86 'metric': 'bps',
87 'bw-max': 10000},
88 {'ifindex': cfg.ifindex,
89 'parent': {'scope': 'netdev'},
90 'handle': {'scope': 'queue', 'id': 2},
91 'metric': 'bps',
92 'bw-max': 20000}])
94 def del_qshapers(cfg, nl_shaper) -> None:
95 if not cfg.queues:
96 raise KsftSkipEx("queue shapers not supported by device, skipping delete")
98 nl_shaper.delete({'ifindex': cfg.ifindex,
99 'handle': {'scope': 'queue', 'id': 2}})
100 nl_shaper.delete({'ifindex': cfg.ifindex,
101 'handle': {'scope': 'queue', 'id': 1}})
102 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
103 ksft_eq(len(shapers), 0)
105 def set_nshapers(cfg, nl_shaper) -> None:
106 # Check required features.
107 try:
108 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
109 'scope':'netdev'})
110 except NlError as e:
111 if e.error == 95:
112 raise KsftSkipEx("shapers not supported by the device")
113 raise
114 if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
115 raise KsftSkipEx("device does not support nested netdev scope shapers with weight")
117 cfg.netdev = True;
118 nl_shaper.set({'ifindex': cfg.ifindex,
119 'handle': {'scope': 'netdev', 'id': 0},
120 'bw-max': 100000})
122 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
123 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
124 'handle': {'scope': 'netdev'},
125 'metric': 'bps',
126 'bw-max': 100000}])
128 def del_nshapers(cfg, nl_shaper) -> None:
129 if not cfg.netdev:
130 raise KsftSkipEx("netdev shaper not supported by device, skipping delete")
132 nl_shaper.delete({'ifindex': cfg.ifindex,
133 'handle': {'scope': 'netdev'}})
134 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
135 ksft_eq(len(shapers), 0)
137 def basic_groups(cfg, nl_shaper) -> None:
138 if not cfg.netdev:
139 raise KsftSkipEx("netdev shaper not supported by the device")
140 if cfg.nr_queues < 3:
141 raise KsftSkipEx(f"netdev does not have enough queues min 3 reported {cfg.nr_queues}")
143 try:
144 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
145 'scope':'queue'})
146 except NlError as e:
147 if e.error == 95:
148 raise KsftSkipEx("shapers not supported by the device")
149 raise
150 if not 'support-weight' in caps:
151 raise KsftSkipEx("device does not support queue scope shapers with weight")
153 node_handle = nl_shaper.group({
154 'ifindex': cfg.ifindex,
155 'leaves':[{'handle': {'scope': 'queue', 'id': 1},
156 'weight': 1},
157 {'handle': {'scope': 'queue', 'id': 2},
158 'weight': 2}],
159 'handle': {'scope':'netdev'},
160 'metric': 'bps',
161 'bw-max': 10000})
162 ksft_eq(node_handle, {'ifindex': cfg.ifindex,
163 'handle': {'scope': 'netdev'}})
165 shaper = nl_shaper.get({'ifindex': cfg.ifindex,
166 'handle': {'scope': 'queue', 'id': 1}})
167 ksft_eq(shaper, {'ifindex': cfg.ifindex,
168 'parent': {'scope': 'netdev'},
169 'handle': {'scope': 'queue', 'id': 1},
170 'weight': 1 })
172 nl_shaper.delete({'ifindex': cfg.ifindex,
173 'handle': {'scope': 'queue', 'id': 2}})
174 nl_shaper.delete({'ifindex': cfg.ifindex,
175 'handle': {'scope': 'queue', 'id': 1}})
177 # Deleting all the leaves shaper does not affect the node one
178 # when the latter has 'netdev' scope.
179 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
180 ksft_eq(len(shapers), 1)
182 nl_shaper.delete({'ifindex': cfg.ifindex,
183 'handle': {'scope': 'netdev'}})
185 def qgroups(cfg, nl_shaper) -> None:
186 if cfg.nr_queues < 4:
187 raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")
188 try:
189 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
190 'scope':'node'})
191 except NlError as e:
192 if e.error == 95:
193 raise KsftSkipEx("shapers not supported by the device")
194 raise
195 if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
196 raise KsftSkipEx("device does not support node scope shapers with bw_max and metric bps")
197 try:
198 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
199 'scope':'queue'})
200 except NlError as e:
201 if e.error == 95:
202 raise KsftSkipEx("shapers not supported by the device")
203 raise
204 if not 'support-nesting' in caps or not 'support-weight' in caps or not 'support-metric-bps' in caps:
205 raise KsftSkipEx("device does not support nested queue scope shapers with weight")
207 cfg.groups = True;
208 node_handle = nl_shaper.group({
209 'ifindex': cfg.ifindex,
210 'leaves':[{'handle': {'scope': 'queue', 'id': 1},
211 'weight': 3},
212 {'handle': {'scope': 'queue', 'id': 2},
213 'weight': 2}],
214 'handle': {'scope':'node'},
215 'metric': 'bps',
216 'bw-max': 10000})
217 node_id = node_handle['handle']['id']
219 shaper = nl_shaper.get({'ifindex': cfg.ifindex,
220 'handle': {'scope': 'queue', 'id': 1}})
221 ksft_eq(shaper, {'ifindex': cfg.ifindex,
222 'parent': {'scope': 'node', 'id': node_id},
223 'handle': {'scope': 'queue', 'id': 1},
224 'weight': 3})
225 shaper = nl_shaper.get({'ifindex': cfg.ifindex,
226 'handle': {'scope': 'node', 'id': node_id}})
227 ksft_eq(shaper, {'ifindex': cfg.ifindex,
228 'handle': {'scope': 'node', 'id': node_id},
229 'parent': {'scope': 'netdev'},
230 'metric': 'bps',
231 'bw-max': 10000})
233 # Grouping to a specified, not existing node scope shaper must fail
234 raised = False
235 try:
236 nl_shaper.group({
237 'ifindex': cfg.ifindex,
238 'leaves':[{'handle': {'scope': 'queue', 'id': 3},
239 'weight': 3}],
240 'handle': {'scope':'node', 'id': node_id + 1},
241 'metric': 'bps',
242 'bw-max': 10000})
244 except (NlError):
245 raised = True
246 ksft_eq(raised, True)
248 # Add to an existing node
249 node_handle = nl_shaper.group({
250 'ifindex': cfg.ifindex,
251 'leaves':[{'handle': {'scope': 'queue', 'id': 3},
252 'weight': 4}],
253 'handle': {'scope':'node', 'id': node_id}})
254 ksft_eq(node_handle, {'ifindex': cfg.ifindex,
255 'handle': {'scope': 'node', 'id': node_id}})
257 shaper = nl_shaper.get({'ifindex': cfg.ifindex,
258 'handle': {'scope': 'queue', 'id': 3}})
259 ksft_eq(shaper, {'ifindex': cfg.ifindex,
260 'parent': {'scope': 'node', 'id': node_id},
261 'handle': {'scope': 'queue', 'id': 3},
262 'weight': 4})
264 nl_shaper.delete({'ifindex': cfg.ifindex,
265 'handle': {'scope': 'queue', 'id': 2}})
266 nl_shaper.delete({'ifindex': cfg.ifindex,
267 'handle': {'scope': 'queue', 'id': 1}})
269 # Deleting a non empty node will move the leaves downstream.
270 nl_shaper.delete({'ifindex': cfg.ifindex,
271 'handle': {'scope': 'node', 'id': node_id}})
272 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
273 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
274 'parent': {'scope': 'netdev'},
275 'handle': {'scope': 'queue', 'id': 3},
276 'weight': 4}])
278 # Finish and verify the complete cleanup.
279 nl_shaper.delete({'ifindex': cfg.ifindex,
280 'handle': {'scope': 'queue', 'id': 3}})
281 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
282 ksft_eq(len(shapers), 0)
284 def delegation(cfg, nl_shaper) -> None:
285 if not cfg.groups:
286 raise KsftSkipEx("device does not support node scope")
287 try:
288 caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
289 'scope':'node'})
290 except NlError as e:
291 if e.error == 95:
292 raise KsftSkipEx("node scope shapers not supported by the device")
293 raise
294 if not 'support-nesting' in caps:
295 raise KsftSkipEx("device does not support node scope shapers nesting")
297 node_handle = nl_shaper.group({
298 'ifindex': cfg.ifindex,
299 'leaves':[{'handle': {'scope': 'queue', 'id': 1},
300 'weight': 3},
301 {'handle': {'scope': 'queue', 'id': 2},
302 'weight': 2},
303 {'handle': {'scope': 'queue', 'id': 3},
304 'weight': 1}],
305 'handle': {'scope':'node'},
306 'metric': 'bps',
307 'bw-max': 10000})
308 node_id = node_handle['handle']['id']
310 # Create the nested node and validate the hierarchy
311 nested_node_handle = nl_shaper.group({
312 'ifindex': cfg.ifindex,
313 'leaves':[{'handle': {'scope': 'queue', 'id': 1},
314 'weight': 3},
315 {'handle': {'scope': 'queue', 'id': 2},
316 'weight': 2}],
317 'handle': {'scope':'node'},
318 'metric': 'bps',
319 'bw-max': 5000})
320 nested_node_id = nested_node_handle['handle']['id']
321 ksft_true(nested_node_id != node_id)
322 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
323 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
324 'parent': {'scope': 'node', 'id': nested_node_id},
325 'handle': {'scope': 'queue', 'id': 1},
326 'weight': 3},
327 {'ifindex': cfg.ifindex,
328 'parent': {'scope': 'node', 'id': nested_node_id},
329 'handle': {'scope': 'queue', 'id': 2},
330 'weight': 2},
331 {'ifindex': cfg.ifindex,
332 'parent': {'scope': 'node', 'id': node_id},
333 'handle': {'scope': 'queue', 'id': 3},
334 'weight': 1},
335 {'ifindex': cfg.ifindex,
336 'parent': {'scope': 'netdev'},
337 'handle': {'scope': 'node', 'id': node_id},
338 'metric': 'bps',
339 'bw-max': 10000},
340 {'ifindex': cfg.ifindex,
341 'parent': {'scope': 'node', 'id': node_id},
342 'handle': {'scope': 'node', 'id': nested_node_id},
343 'metric': 'bps',
344 'bw-max': 5000}])
346 # Deleting a non empty node will move the leaves downstream.
347 nl_shaper.delete({'ifindex': cfg.ifindex,
348 'handle': {'scope': 'node', 'id': nested_node_id}})
349 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
350 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
351 'parent': {'scope': 'node', 'id': node_id},
352 'handle': {'scope': 'queue', 'id': 1},
353 'weight': 3},
354 {'ifindex': cfg.ifindex,
355 'parent': {'scope': 'node', 'id': node_id},
356 'handle': {'scope': 'queue', 'id': 2},
357 'weight': 2},
358 {'ifindex': cfg.ifindex,
359 'parent': {'scope': 'node', 'id': node_id},
360 'handle': {'scope': 'queue', 'id': 3},
361 'weight': 1},
362 {'ifindex': cfg.ifindex,
363 'parent': {'scope': 'netdev'},
364 'handle': {'scope': 'node', 'id': node_id},
365 'metric': 'bps',
366 'bw-max': 10000}])
368 # Final cleanup.
369 for i in range(1, 4):
370 nl_shaper.delete({'ifindex': cfg.ifindex,
371 'handle': {'scope': 'queue', 'id': i}})
372 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
373 ksft_eq(len(shapers), 0)
375 def queue_update(cfg, nl_shaper) -> None:
376 if cfg.nr_queues < 4:
377 raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")
378 if not cfg.queues:
379 raise KsftSkipEx("device does not support queue scope")
381 for i in range(3):
382 nl_shaper.set({'ifindex': cfg.ifindex,
383 'handle': {'scope': 'queue', 'id': i},
384 'metric': 'bps',
385 'bw-max': (i + 1) * 1000})
386 # Delete a channel, with no shapers configured on top of the related
387 # queue: no changes expected
388 cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 3", timeout=10)
389 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
390 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
391 'parent': {'scope': 'netdev'},
392 'handle': {'scope': 'queue', 'id': 0},
393 'metric': 'bps',
394 'bw-max': 1000},
395 {'ifindex': cfg.ifindex,
396 'parent': {'scope': 'netdev'},
397 'handle': {'scope': 'queue', 'id': 1},
398 'metric': 'bps',
399 'bw-max': 2000},
400 {'ifindex': cfg.ifindex,
401 'parent': {'scope': 'netdev'},
402 'handle': {'scope': 'queue', 'id': 2},
403 'metric': 'bps',
404 'bw-max': 3000}])
406 # Delete a channel, with a shaper configured on top of the related
407 # queue: the shaper must be deleted, too
408 cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 2", timeout=10)
410 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
411 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
412 'parent': {'scope': 'netdev'},
413 'handle': {'scope': 'queue', 'id': 0},
414 'metric': 'bps',
415 'bw-max': 1000},
416 {'ifindex': cfg.ifindex,
417 'parent': {'scope': 'netdev'},
418 'handle': {'scope': 'queue', 'id': 1},
419 'metric': 'bps',
420 'bw-max': 2000}])
422 # Restore the original channels number, no expected changes
423 cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} {cfg.nr_queues}", timeout=10)
424 shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
425 ksft_eq(shapers, [{'ifindex': cfg.ifindex,
426 'parent': {'scope': 'netdev'},
427 'handle': {'scope': 'queue', 'id': 0},
428 'metric': 'bps',
429 'bw-max': 1000},
430 {'ifindex': cfg.ifindex,
431 'parent': {'scope': 'netdev'},
432 'handle': {'scope': 'queue', 'id': 1},
433 'metric': 'bps',
434 'bw-max': 2000}])
436 # Final cleanup.
437 for i in range(0, 2):
438 nl_shaper.delete({'ifindex': cfg.ifindex,
439 'handle': {'scope': 'queue', 'id': i}})
441 def main() -> None:
442 with NetDrvEnv(__file__, queue_count=4) as cfg:
443 cfg.queues = False
444 cfg.netdev = False
445 cfg.groups = False
446 cfg.nr_queues = 0
447 ksft_run([get_shapers,
448 get_caps,
449 set_qshapers,
450 del_qshapers,
451 set_nshapers,
452 del_nshapers,
453 basic_groups,
454 qgroups,
455 delegation,
456 queue_update], args=(cfg, NetshaperFamily()))
457 ksft_exit()
460 if __name__ == "__main__":
461 main()