Path: blob/master/tools/testing/selftests/drivers/net/shaper.py
26288 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_true, KsftSkipEx4from lib.py import EthtoolFamily, NetshaperFamily5from lib.py import NetDrvEnv6from lib.py import NlError7from lib.py import cmd89def get_shapers(cfg, nl_shaper) -> None:10try:11shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)12except NlError as e:13if e.error == 95:14raise KsftSkipEx("shapers not supported by the device")15raise1617# Default configuration: no shapers configured.18ksft_eq(len(shapers), 0)1920def get_caps(cfg, nl_shaper) -> None:21try:22caps = nl_shaper.cap_get({'ifindex': cfg.ifindex}, dump=True)23except NlError as e:24if e.error == 95:25raise KsftSkipEx("shapers not supported by the device")26raise2728# Each device implementing shaper support must support some29# features in at least a scope.30ksft_true(len(caps)> 0)3132def set_qshapers(cfg, nl_shaper) -> None:33try:34caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,35'scope':'queue'})36except NlError as e:37if e.error == 95:38raise KsftSkipEx("shapers not supported by the device")39raise40if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:41raise KsftSkipEx("device does not support queue scope shapers with bw_max and metric bps")4243cfg.queues = True;44netnl = EthtoolFamily()45channels = netnl.channels_get({'header': {'dev-index': cfg.ifindex}})46if channels['combined-count'] == 0:47cfg.rx_type = 'rx'48cfg.nr_queues = channels['rx-count']49else:50cfg.rx_type = 'combined'51cfg.nr_queues = channels['combined-count']52if cfg.nr_queues < 3:53raise KsftSkipEx(f"device does not support enough queues min 3 found {cfg.nr_queues}")5455nl_shaper.set({'ifindex': cfg.ifindex,56'handle': {'scope': 'queue', 'id': 1},57'metric': 'bps',58'bw-max': 10000})59nl_shaper.set({'ifindex': cfg.ifindex,60'handle': {'scope': 'queue', 'id': 2},61'metric': 'bps',62'bw-max': 20000})6364# Querying a specific shaper not yet configured must fail.65raised = False66try:67shaper_q0 = nl_shaper.get({'ifindex': cfg.ifindex,68'handle': {'scope': 'queue', 'id': 0}})69except (NlError):70raised = True71ksft_eq(raised, True)7273shaper_q1 = nl_shaper.get({'ifindex': cfg.ifindex,74'handle': {'scope': 'queue', 'id': 1}})75ksft_eq(shaper_q1, {'ifindex': cfg.ifindex,76'parent': {'scope': 'netdev'},77'handle': {'scope': 'queue', 'id': 1},78'metric': 'bps',79'bw-max': 10000})8081shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)82ksft_eq(shapers, [{'ifindex': cfg.ifindex,83'parent': {'scope': 'netdev'},84'handle': {'scope': 'queue', 'id': 1},85'metric': 'bps',86'bw-max': 10000},87{'ifindex': cfg.ifindex,88'parent': {'scope': 'netdev'},89'handle': {'scope': 'queue', 'id': 2},90'metric': 'bps',91'bw-max': 20000}])9293def del_qshapers(cfg, nl_shaper) -> None:94if not cfg.queues:95raise KsftSkipEx("queue shapers not supported by device, skipping delete")9697nl_shaper.delete({'ifindex': cfg.ifindex,98'handle': {'scope': 'queue', 'id': 2}})99nl_shaper.delete({'ifindex': cfg.ifindex,100'handle': {'scope': 'queue', 'id': 1}})101shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)102ksft_eq(len(shapers), 0)103104def set_nshapers(cfg, nl_shaper) -> None:105# Check required features.106try:107caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,108'scope':'netdev'})109except NlError as e:110if e.error == 95:111raise KsftSkipEx("shapers not supported by the device")112raise113if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:114raise KsftSkipEx("device does not support nested netdev scope shapers with weight")115116cfg.netdev = True;117nl_shaper.set({'ifindex': cfg.ifindex,118'handle': {'scope': 'netdev', 'id': 0},119'bw-max': 100000})120121shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)122ksft_eq(shapers, [{'ifindex': cfg.ifindex,123'handle': {'scope': 'netdev'},124'metric': 'bps',125'bw-max': 100000}])126127def del_nshapers(cfg, nl_shaper) -> None:128if not cfg.netdev:129raise KsftSkipEx("netdev shaper not supported by device, skipping delete")130131nl_shaper.delete({'ifindex': cfg.ifindex,132'handle': {'scope': 'netdev'}})133shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)134ksft_eq(len(shapers), 0)135136def basic_groups(cfg, nl_shaper) -> None:137if not cfg.netdev:138raise KsftSkipEx("netdev shaper not supported by the device")139if cfg.nr_queues < 3:140raise KsftSkipEx(f"netdev does not have enough queues min 3 reported {cfg.nr_queues}")141142try:143caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,144'scope':'queue'})145except NlError as e:146if e.error == 95:147raise KsftSkipEx("shapers not supported by the device")148raise149if not 'support-weight' in caps:150raise KsftSkipEx("device does not support queue scope shapers with weight")151152node_handle = nl_shaper.group({153'ifindex': cfg.ifindex,154'leaves':[{'handle': {'scope': 'queue', 'id': 1},155'weight': 1},156{'handle': {'scope': 'queue', 'id': 2},157'weight': 2}],158'handle': {'scope':'netdev'},159'metric': 'bps',160'bw-max': 10000})161ksft_eq(node_handle, {'ifindex': cfg.ifindex,162'handle': {'scope': 'netdev'}})163164shaper = nl_shaper.get({'ifindex': cfg.ifindex,165'handle': {'scope': 'queue', 'id': 1}})166ksft_eq(shaper, {'ifindex': cfg.ifindex,167'parent': {'scope': 'netdev'},168'handle': {'scope': 'queue', 'id': 1},169'weight': 1 })170171nl_shaper.delete({'ifindex': cfg.ifindex,172'handle': {'scope': 'queue', 'id': 2}})173nl_shaper.delete({'ifindex': cfg.ifindex,174'handle': {'scope': 'queue', 'id': 1}})175176# Deleting all the leaves shaper does not affect the node one177# when the latter has 'netdev' scope.178shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)179ksft_eq(len(shapers), 1)180181nl_shaper.delete({'ifindex': cfg.ifindex,182'handle': {'scope': 'netdev'}})183184def qgroups(cfg, nl_shaper) -> None:185if cfg.nr_queues < 4:186raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")187try:188caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,189'scope':'node'})190except NlError as e:191if e.error == 95:192raise KsftSkipEx("shapers not supported by the device")193raise194if not 'support-bw-max' in caps or not 'support-metric-bps' in caps:195raise KsftSkipEx("device does not support node scope shapers with bw_max and metric bps")196try:197caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,198'scope':'queue'})199except NlError as e:200if e.error == 95:201raise KsftSkipEx("shapers not supported by the device")202raise203if not 'support-nesting' in caps or not 'support-weight' in caps or not 'support-metric-bps' in caps:204raise KsftSkipEx("device does not support nested queue scope shapers with weight")205206cfg.groups = True;207node_handle = nl_shaper.group({208'ifindex': cfg.ifindex,209'leaves':[{'handle': {'scope': 'queue', 'id': 1},210'weight': 3},211{'handle': {'scope': 'queue', 'id': 2},212'weight': 2}],213'handle': {'scope':'node'},214'metric': 'bps',215'bw-max': 10000})216node_id = node_handle['handle']['id']217218shaper = nl_shaper.get({'ifindex': cfg.ifindex,219'handle': {'scope': 'queue', 'id': 1}})220ksft_eq(shaper, {'ifindex': cfg.ifindex,221'parent': {'scope': 'node', 'id': node_id},222'handle': {'scope': 'queue', 'id': 1},223'weight': 3})224shaper = nl_shaper.get({'ifindex': cfg.ifindex,225'handle': {'scope': 'node', 'id': node_id}})226ksft_eq(shaper, {'ifindex': cfg.ifindex,227'handle': {'scope': 'node', 'id': node_id},228'parent': {'scope': 'netdev'},229'metric': 'bps',230'bw-max': 10000})231232# Grouping to a specified, not existing node scope shaper must fail233raised = False234try:235nl_shaper.group({236'ifindex': cfg.ifindex,237'leaves':[{'handle': {'scope': 'queue', 'id': 3},238'weight': 3}],239'handle': {'scope':'node', 'id': node_id + 1},240'metric': 'bps',241'bw-max': 10000})242243except (NlError):244raised = True245ksft_eq(raised, True)246247# Add to an existing node248node_handle = nl_shaper.group({249'ifindex': cfg.ifindex,250'leaves':[{'handle': {'scope': 'queue', 'id': 3},251'weight': 4}],252'handle': {'scope':'node', 'id': node_id}})253ksft_eq(node_handle, {'ifindex': cfg.ifindex,254'handle': {'scope': 'node', 'id': node_id}})255256shaper = nl_shaper.get({'ifindex': cfg.ifindex,257'handle': {'scope': 'queue', 'id': 3}})258ksft_eq(shaper, {'ifindex': cfg.ifindex,259'parent': {'scope': 'node', 'id': node_id},260'handle': {'scope': 'queue', 'id': 3},261'weight': 4})262263nl_shaper.delete({'ifindex': cfg.ifindex,264'handle': {'scope': 'queue', 'id': 2}})265nl_shaper.delete({'ifindex': cfg.ifindex,266'handle': {'scope': 'queue', 'id': 1}})267268# Deleting a non empty node will move the leaves downstream.269nl_shaper.delete({'ifindex': cfg.ifindex,270'handle': {'scope': 'node', 'id': node_id}})271shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)272ksft_eq(shapers, [{'ifindex': cfg.ifindex,273'parent': {'scope': 'netdev'},274'handle': {'scope': 'queue', 'id': 3},275'weight': 4}])276277# Finish and verify the complete cleanup.278nl_shaper.delete({'ifindex': cfg.ifindex,279'handle': {'scope': 'queue', 'id': 3}})280shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)281ksft_eq(len(shapers), 0)282283def delegation(cfg, nl_shaper) -> None:284if not cfg.groups:285raise KsftSkipEx("device does not support node scope")286try:287caps = nl_shaper.cap_get({'ifindex': cfg.ifindex,288'scope':'node'})289except NlError as e:290if e.error == 95:291raise KsftSkipEx("node scope shapers not supported by the device")292raise293if not 'support-nesting' in caps:294raise KsftSkipEx("device does not support node scope shapers nesting")295296node_handle = nl_shaper.group({297'ifindex': cfg.ifindex,298'leaves':[{'handle': {'scope': 'queue', 'id': 1},299'weight': 3},300{'handle': {'scope': 'queue', 'id': 2},301'weight': 2},302{'handle': {'scope': 'queue', 'id': 3},303'weight': 1}],304'handle': {'scope':'node'},305'metric': 'bps',306'bw-max': 10000})307node_id = node_handle['handle']['id']308309# Create the nested node and validate the hierarchy310nested_node_handle = nl_shaper.group({311'ifindex': cfg.ifindex,312'leaves':[{'handle': {'scope': 'queue', 'id': 1},313'weight': 3},314{'handle': {'scope': 'queue', 'id': 2},315'weight': 2}],316'handle': {'scope':'node'},317'metric': 'bps',318'bw-max': 5000})319nested_node_id = nested_node_handle['handle']['id']320ksft_true(nested_node_id != node_id)321shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)322ksft_eq(shapers, [{'ifindex': cfg.ifindex,323'parent': {'scope': 'node', 'id': nested_node_id},324'handle': {'scope': 'queue', 'id': 1},325'weight': 3},326{'ifindex': cfg.ifindex,327'parent': {'scope': 'node', 'id': nested_node_id},328'handle': {'scope': 'queue', 'id': 2},329'weight': 2},330{'ifindex': cfg.ifindex,331'parent': {'scope': 'node', 'id': node_id},332'handle': {'scope': 'queue', 'id': 3},333'weight': 1},334{'ifindex': cfg.ifindex,335'parent': {'scope': 'netdev'},336'handle': {'scope': 'node', 'id': node_id},337'metric': 'bps',338'bw-max': 10000},339{'ifindex': cfg.ifindex,340'parent': {'scope': 'node', 'id': node_id},341'handle': {'scope': 'node', 'id': nested_node_id},342'metric': 'bps',343'bw-max': 5000}])344345# Deleting a non empty node will move the leaves downstream.346nl_shaper.delete({'ifindex': cfg.ifindex,347'handle': {'scope': 'node', 'id': nested_node_id}})348shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)349ksft_eq(shapers, [{'ifindex': cfg.ifindex,350'parent': {'scope': 'node', 'id': node_id},351'handle': {'scope': 'queue', 'id': 1},352'weight': 3},353{'ifindex': cfg.ifindex,354'parent': {'scope': 'node', 'id': node_id},355'handle': {'scope': 'queue', 'id': 2},356'weight': 2},357{'ifindex': cfg.ifindex,358'parent': {'scope': 'node', 'id': node_id},359'handle': {'scope': 'queue', 'id': 3},360'weight': 1},361{'ifindex': cfg.ifindex,362'parent': {'scope': 'netdev'},363'handle': {'scope': 'node', 'id': node_id},364'metric': 'bps',365'bw-max': 10000}])366367# Final cleanup.368for i in range(1, 4):369nl_shaper.delete({'ifindex': cfg.ifindex,370'handle': {'scope': 'queue', 'id': i}})371shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)372ksft_eq(len(shapers), 0)373374def queue_update(cfg, nl_shaper) -> None:375if cfg.nr_queues < 4:376raise KsftSkipEx(f"netdev does not have enough queues min 4 reported {cfg.nr_queues}")377if not cfg.queues:378raise KsftSkipEx("device does not support queue scope")379380for i in range(3):381nl_shaper.set({'ifindex': cfg.ifindex,382'handle': {'scope': 'queue', 'id': i},383'metric': 'bps',384'bw-max': (i + 1) * 1000})385# Delete a channel, with no shapers configured on top of the related386# queue: no changes expected387cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 3", timeout=10)388shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)389ksft_eq(shapers, [{'ifindex': cfg.ifindex,390'parent': {'scope': 'netdev'},391'handle': {'scope': 'queue', 'id': 0},392'metric': 'bps',393'bw-max': 1000},394{'ifindex': cfg.ifindex,395'parent': {'scope': 'netdev'},396'handle': {'scope': 'queue', 'id': 1},397'metric': 'bps',398'bw-max': 2000},399{'ifindex': cfg.ifindex,400'parent': {'scope': 'netdev'},401'handle': {'scope': 'queue', 'id': 2},402'metric': 'bps',403'bw-max': 3000}])404405# Delete a channel, with a shaper configured on top of the related406# queue: the shaper must be deleted, too407cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} 2", timeout=10)408409shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)410ksft_eq(shapers, [{'ifindex': cfg.ifindex,411'parent': {'scope': 'netdev'},412'handle': {'scope': 'queue', 'id': 0},413'metric': 'bps',414'bw-max': 1000},415{'ifindex': cfg.ifindex,416'parent': {'scope': 'netdev'},417'handle': {'scope': 'queue', 'id': 1},418'metric': 'bps',419'bw-max': 2000}])420421# Restore the original channels number, no expected changes422cmd(f"ethtool -L {cfg.dev['ifname']} {cfg.rx_type} {cfg.nr_queues}", timeout=10)423shapers = nl_shaper.get({'ifindex': cfg.ifindex}, dump=True)424ksft_eq(shapers, [{'ifindex': cfg.ifindex,425'parent': {'scope': 'netdev'},426'handle': {'scope': 'queue', 'id': 0},427'metric': 'bps',428'bw-max': 1000},429{'ifindex': cfg.ifindex,430'parent': {'scope': 'netdev'},431'handle': {'scope': 'queue', 'id': 1},432'metric': 'bps',433'bw-max': 2000}])434435# Final cleanup.436for i in range(0, 2):437nl_shaper.delete({'ifindex': cfg.ifindex,438'handle': {'scope': 'queue', 'id': i}})439440def main() -> None:441with NetDrvEnv(__file__, queue_count=4) as cfg:442cfg.queues = False443cfg.netdev = False444cfg.groups = False445cfg.nr_queues = 0446ksft_run([get_shapers,447get_caps,448set_qshapers,449del_qshapers,450set_nshapers,451del_nshapers,452basic_groups,453qgroups,454delegation,455queue_update], args=(cfg, NetshaperFamily()))456ksft_exit()457458459if __name__ == "__main__":460main()461462463