Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/drivers/net/shaper.py
26288 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
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
9
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
17
18
# Default configuration: no shapers configured.
19
ksft_eq(len(shapers), 0)
20
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
28
29
# Each device implementing shaper support must support some
30
# features in at least a scope.
31
ksft_true(len(caps)> 0)
32
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")
43
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}")
55
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})
64
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)
73
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})
81
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}])
93
94
def del_qshapers(cfg, nl_shaper) -> None:
95
if not cfg.queues:
96
raise KsftSkipEx("queue shapers not supported by device, skipping delete")
97
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)
104
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")
116
117
cfg.netdev = True;
118
nl_shaper.set({'ifindex': cfg.ifindex,
119
'handle': {'scope': 'netdev', 'id': 0},
120
'bw-max': 100000})
121
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}])
127
128
def del_nshapers(cfg, nl_shaper) -> None:
129
if not cfg.netdev:
130
raise KsftSkipEx("netdev shaper not supported by device, skipping delete")
131
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)
136
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}")
142
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")
152
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'}})
164
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 })
171
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}})
176
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)
181
182
nl_shaper.delete({'ifindex': cfg.ifindex,
183
'handle': {'scope': 'netdev'}})
184
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")
206
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']
218
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})
232
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})
243
244
except (NlError):
245
raised = True
246
ksft_eq(raised, True)
247
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}})
256
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})
263
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}})
268
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}])
277
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)
283
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")
296
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']
309
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}])
345
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}])
367
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)
374
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")
380
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}])
405
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)
409
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}])
421
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}])
435
436
# Final cleanup.
437
for i in range(0, 2):
438
nl_shaper.delete({'ifindex': cfg.ifindex,
439
'handle': {'scope': 'queue', 'id': i}})
440
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()
458
459
460
if __name__ == "__main__":
461
main()
462
463