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
10 def get_shapers(cfg
, nl_shaper
) -> None:
12 shapers
= nl_shaper
.get({'ifindex': cfg
.ifindex
}, dump
=True)
15 raise KsftSkipEx("shapers not supported by the device")
18 # Default configuration: no shapers configured.
19 ksft_eq(len(shapers
), 0)
21 def get_caps(cfg
, nl_shaper
) -> None:
23 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
}, dump
=True)
26 raise KsftSkipEx("shapers not supported by the device")
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:
35 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
39 raise KsftSkipEx("shapers not supported by the device")
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")
45 netnl
= EthtoolFamily()
46 channels
= netnl
.channels_get({'header': {'dev-index': cfg
.ifindex
}})
47 if channels
['combined-count'] == 0:
49 cfg
.nr_queues
= channels
['rx-count']
51 cfg
.rx_type
= 'combined'
52 cfg
.nr_queues
= channels
['combined-count']
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},
60 nl_shaper
.set({'ifindex': cfg
.ifindex
,
61 'handle': {'scope': 'queue', 'id': 2},
65 # Querying a specific shaper not yet configured must fail.
68 shaper_q0
= nl_shaper
.get({'ifindex': cfg
.ifindex
,
69 'handle': {'scope': 'queue', 'id': 0}})
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},
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},
88 {'ifindex': cfg
.ifindex
,
89 'parent': {'scope': 'netdev'},
90 'handle': {'scope': 'queue', 'id': 2},
94 def del_qshapers(cfg
, nl_shaper
) -> None:
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.
108 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
112 raise KsftSkipEx("shapers not supported by the device")
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")
118 nl_shaper
.set({'ifindex': cfg
.ifindex
,
119 'handle': {'scope': 'netdev', 'id': 0},
122 shapers
= nl_shaper
.get({'ifindex': cfg
.ifindex
}, dump
=True)
123 ksft_eq(shapers
, [{'ifindex': cfg
.ifindex
,
124 'handle': {'scope': 'netdev'},
128 def del_nshapers(cfg
, nl_shaper
) -> None:
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:
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}")
144 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
148 raise KsftSkipEx("shapers not supported by the device")
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},
157 {'handle': {'scope': 'queue', 'id': 2},
159 'handle': {'scope':'netdev'},
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},
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}")
189 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
193 raise KsftSkipEx("shapers not supported by the device")
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")
198 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
202 raise KsftSkipEx("shapers not supported by the device")
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")
208 node_handle
= nl_shaper
.group({
209 'ifindex': cfg
.ifindex
,
210 'leaves':[{'handle': {'scope': 'queue', 'id': 1},
212 {'handle': {'scope': 'queue', 'id': 2},
214 'handle': {'scope':'node'},
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},
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'},
233 # Grouping to a specified, not existing node scope shaper must fail
237 'ifindex': cfg
.ifindex
,
238 'leaves':[{'handle': {'scope': 'queue', 'id': 3},
240 'handle': {'scope':'node', 'id': node_id
+ 1},
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},
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},
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},
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:
286 raise KsftSkipEx("device does not support node scope")
288 caps
= nl_shaper
.cap_get({'ifindex': cfg
.ifindex
,
292 raise KsftSkipEx("node scope shapers not supported by the device")
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},
301 {'handle': {'scope': 'queue', 'id': 2},
303 {'handle': {'scope': 'queue', 'id': 3},
305 'handle': {'scope':'node'},
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},
315 {'handle': {'scope': 'queue', 'id': 2},
317 'handle': {'scope':'node'},
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},
327 {'ifindex': cfg
.ifindex
,
328 'parent': {'scope': 'node', 'id': nested_node_id
},
329 'handle': {'scope': 'queue', 'id': 2},
331 {'ifindex': cfg
.ifindex
,
332 'parent': {'scope': 'node', 'id': node_id
},
333 'handle': {'scope': 'queue', 'id': 3},
335 {'ifindex': cfg
.ifindex
,
336 'parent': {'scope': 'netdev'},
337 'handle': {'scope': 'node', 'id': node_id
},
340 {'ifindex': cfg
.ifindex
,
341 'parent': {'scope': 'node', 'id': node_id
},
342 'handle': {'scope': 'node', 'id': nested_node_id
},
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},
354 {'ifindex': cfg
.ifindex
,
355 'parent': {'scope': 'node', 'id': node_id
},
356 'handle': {'scope': 'queue', 'id': 2},
358 {'ifindex': cfg
.ifindex
,
359 'parent': {'scope': 'node', 'id': node_id
},
360 'handle': {'scope': 'queue', 'id': 3},
362 {'ifindex': cfg
.ifindex
,
363 'parent': {'scope': 'netdev'},
364 'handle': {'scope': 'node', 'id': node_id
},
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}")
379 raise KsftSkipEx("device does not support queue scope")
382 nl_shaper
.set({'ifindex': cfg
.ifindex
,
383 'handle': {'scope': 'queue', 'id': i
},
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},
395 {'ifindex': cfg
.ifindex
,
396 'parent': {'scope': 'netdev'},
397 'handle': {'scope': 'queue', 'id': 1},
400 {'ifindex': cfg
.ifindex
,
401 'parent': {'scope': 'netdev'},
402 'handle': {'scope': 'queue', 'id': 2},
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},
416 {'ifindex': cfg
.ifindex
,
417 'parent': {'scope': 'netdev'},
418 'handle': {'scope': 'queue', 'id': 1},
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},
430 {'ifindex': cfg
.ifindex
,
431 'parent': {'scope': 'netdev'},
432 'handle': {'scope': 'queue', 'id': 1},
437 for i
in range(0, 2):
438 nl_shaper
.delete({'ifindex': cfg
.ifindex
,
439 'handle': {'scope': 'queue', 'id': i
}})
442 with
NetDrvEnv(__file__
, queue_count
=4) as cfg
:
447 ksft_run([get_shapers
,
456 queue_update
], args
=(cfg
, NetshaperFamily()))
460 if __name__
== "__main__":