JustOS/linux-6.13/tools/testing/selftests/drivers/net/shaper.py
justuser 02e73b8cd9 up
2025-01-24 17:00:19 +03:00

462 lines
19 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_true, KsftSkipEx
from lib.py import EthtoolFamily, NetshaperFamily
from lib.py import NetDrvEnv
from lib.py import NlError
from lib.py import cmd
def get_shapers(cfg, nl_shaper) -> None:
try:
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
# Default configuration: no shapers configured.
ksft_eq(len(shapers), 0)
def get_caps(cfg, nl_shaper) -> None:
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex}, dump=True)
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
# Each device implementing shaper support must support some
# features in at least a scope.
ksft_true(len(caps)> 0)
def set_qshapers(cfg, nl_shaper) -> None:
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'queue'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
raise KsftSkipEx("device does not support queue scope shapers with bw_max and metric bps")
cfg.queues = True;
netnl = EthtoolFamily()
channels = netnl.channels_get({'header': {'dev-index': cfg.ifindex}})
if channels['combined-count'] == 0:
cfg.rx_type = 'rx'
cfg.nr_queues = channels['rx-count']
else:
cfg.rx_type = 'combined'
cfg.nr_queues = channels['combined-count']
if cfg.nr_queues < 3:
raise KsftSkipEx(f"device does not support enough queues min 3 found {cfg.nr_queues}")
nl_shaper.set({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 10000})
nl_shaper.set({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 2},
'metric': 'bps',
'bw-max': 20000})
# Querying a specific shaper not yet configured must fail.
raised = False
try:
shaper_q0 = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 0}})
except (NlError):
raised = True
ksft_eq(raised, True)
shaper_q1 = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
ksft_eq(shaper_q1, {'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 10000})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 10000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 2},
'metric': 'bps',
'bw-max': 20000}])
def del_qshapers(cfg, nl_shaper) -> None:
if not cfg.queues:
raise KsftSkipEx("queue shapers not supported by device, skipping delete")
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 2}})
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(len(shapers), 0)
def set_nshapers(cfg, nl_shaper) -> None:
# Check required features.
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'netdev'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
raise KsftSkipEx("device does not support nested netdev scope shapers with weight")
cfg.netdev = True;
nl_shaper.set({'ifindex': cfg.ifindex,
'handle': {'scope': 'netdev', 'id': 0},
'bw-max': 100000})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'handle': {'scope': 'netdev'},
'metric': 'bps',
'bw-max': 100000}])
def del_nshapers(cfg, nl_shaper) -> None:
if not cfg.netdev:
raise KsftSkipEx("netdev shaper not supported by device, skipping delete")
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'netdev'}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(len(shapers), 0)
def basic_groups(cfg, nl_shaper) -> None:
if not cfg.netdev:
raise KsftSkipEx("netdev shaper not supported by the device")
if cfg.nr_queues < 3:
raise KsftSkipEx(f"netdev does not have enough queues min 3 reported {cfg.nr_queues}")
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'queue'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
if not 'support-weight' in caps:
raise KsftSkipEx("device does not support queue scope shapers with weight")
node_handle = nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 1},
'weight': 1},
{'handle': {'scope': 'queue', 'id': 2},
'weight': 2}],
'handle': {'scope':'netdev'},
'metric': 'bps',
'bw-max': 10000})
ksft_eq(node_handle, {'ifindex': cfg.ifindex,
'handle': {'scope': 'netdev'}})
shaper = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
ksft_eq(shaper, {'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'weight': 1 })
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 2}})
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
# Deleting all the leaves shaper does not affect the node one
# when the latter has 'netdev' scope.
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(len(shapers), 1)
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'netdev'}})
def qgroups(cfg, nl_shaper) -> None:
if cfg.nr_queues < 4:
raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'node'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:
raise KsftSkipEx("device does not support node scope shapers with bw_max and metric bps")
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'queue'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("shapers not supported by the device")
raise
if not 'support-nesting' in caps or not 'support-weight' in caps or not 'support-metric-bps' in caps:
raise KsftSkipEx("device does not support nested queue scope shapers with weight")
cfg.groups = True;
node_handle = nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 1},
'weight': 3},
{'handle': {'scope': 'queue', 'id': 2},
'weight': 2}],
'handle': {'scope':'node'},
'metric': 'bps',
'bw-max': 10000})
node_id = node_handle['handle']['id']
shaper = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
ksft_eq(shaper, {'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 1},
'weight': 3})
shaper = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'node', 'id': node_id}})
ksft_eq(shaper, {'ifindex': cfg.ifindex,
'handle': {'scope': 'node', 'id': node_id},
'parent': {'scope': 'netdev'},
'metric': 'bps',
'bw-max': 10000})
# Grouping to a specified, not existing node scope shaper must fail
raised = False
try:
nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 3},
'weight': 3}],
'handle': {'scope':'node', 'id': node_id + 1},
'metric': 'bps',
'bw-max': 10000})
except (NlError):
raised = True
ksft_eq(raised, True)
# Add to an existing node
node_handle = nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 3},
'weight': 4}],
'handle': {'scope':'node', 'id': node_id}})
ksft_eq(node_handle, {'ifindex': cfg.ifindex,
'handle': {'scope': 'node', 'id': node_id}})
shaper = nl_shaper.get({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 3}})
ksft_eq(shaper, {'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 3},
'weight': 4})
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 2}})
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 1}})
# Deleting a non empty node will move the leaves downstream.
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'node', 'id': node_id}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 3},
'weight': 4}])
# Finish and verify the complete cleanup.
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': 3}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(len(shapers), 0)
def delegation(cfg, nl_shaper) -> None:
if not cfg.groups:
raise KsftSkipEx("device does not support node scope")
try:
caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,
'scope':'node'})
except NlError as e:
if e.error == 95:
raise KsftSkipEx("node scope shapers not supported by the device")
raise
if not 'support-nesting' in caps:
raise KsftSkipEx("device does not support node scope shapers nesting")
node_handle = nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 1},
'weight': 3},
{'handle': {'scope': 'queue', 'id': 2},
'weight': 2},
{'handle': {'scope': 'queue', 'id': 3},
'weight': 1}],
'handle': {'scope':'node'},
'metric': 'bps',
'bw-max': 10000})
node_id = node_handle['handle']['id']
# Create the nested node and validate the hierarchy
nested_node_handle = nl_shaper.group({
'ifindex': cfg.ifindex,
'leaves':[{'handle': {'scope': 'queue', 'id': 1},
'weight': 3},
{'handle': {'scope': 'queue', 'id': 2},
'weight': 2}],
'handle': {'scope':'node'},
'metric': 'bps',
'bw-max': 5000})
nested_node_id = nested_node_handle['handle']['id']
ksft_true(nested_node_id != node_id)
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': nested_node_id},
'handle': {'scope': 'queue', 'id': 1},
'weight': 3},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': nested_node_id},
'handle': {'scope': 'queue', 'id': 2},
'weight': 2},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 3},
'weight': 1},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'node', 'id': node_id},
'metric': 'bps',
'bw-max': 10000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'node', 'id': nested_node_id},
'metric': 'bps',
'bw-max': 5000}])
# Deleting a non empty node will move the leaves downstream.
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'node', 'id': nested_node_id}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 1},
'weight': 3},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 2},
'weight': 2},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'node', 'id': node_id},
'handle': {'scope': 'queue', 'id': 3},
'weight': 1},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'node', 'id': node_id},
'metric': 'bps',
'bw-max': 10000}])
# Final cleanup.
for i in range(1, 4):
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': i}})
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(len(shapers), 0)
def queue_update(cfg, nl_shaper) -> None:
if cfg.nr_queues < 4:
raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")
if not cfg.queues:
raise KsftSkipEx("device does not support queue scope")
for i in range(3):
nl_shaper.set({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': i},
'metric': 'bps',
'bw-max': (i + 1) * 1000})
# Delete a channel, with no shapers configured on top of the related
# queue: no changes expected
cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 3", timeout=10)
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 0},
'metric': 'bps',
'bw-max': 1000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 2000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 2},
'metric': 'bps',
'bw-max': 3000}])
# Delete a channel, with a shaper configured on top of the related
# queue: the shaper must be deleted, too
cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 2", timeout=10)
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 0},
'metric': 'bps',
'bw-max': 1000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 2000}])
# Restore the original channels number, no expected changes
cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} {cfg.nr_queues}", timeout=10)
shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)
ksft_eq(shapers, [{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 0},
'metric': 'bps',
'bw-max': 1000},
{'ifindex': cfg.ifindex,
'parent': {'scope': 'netdev'},
'handle': {'scope': 'queue', 'id': 1},
'metric': 'bps',
'bw-max': 2000}])
# Final cleanup.
for i in range(0, 2):
nl_shaper.delete({'ifindex': cfg.ifindex,
'handle': {'scope': 'queue', 'id': i}})
def main() -> None:
with NetDrvEnv(__file__, queue_count=4) as cfg:
cfg.queues = False
cfg.netdev = False
cfg.groups = False
cfg.nr_queues = 0
ksft_run([get_shapers,
get_caps,
set_qshapers,
del_qshapers,
set_nshapers,
del_nshapers,
basic_groups,
qgroups,
delegation,
queue_update], args=(cfg, NetshaperFamily()))
ksft_exit()
if __name__ == "__main__":
main()