1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5Test channel and ring size configuration via ethtool (-L / -G). 6""" 7 8from lib.py import ksft_run, ksft_exit, ksft_pr 9from lib.py import ksft_eq 10from lib.py import NetDrvEpEnv, EthtoolFamily, GenerateTraffic 11from lib.py import defer, NlError 12 13 14def channels(cfg) -> None: 15 """ 16 Twiddle channel counts in various combinations of parameters. 17 We're only looking for driver adhering to the requested config 18 if the config is accepted and crashes. 19 """ 20 ehdr = {'header':{'dev-index': cfg.ifindex}} 21 chans = cfg.eth.channels_get(ehdr) 22 23 all_keys = ["rx", "tx", "combined"] 24 mixes = [{"combined"}, {"rx", "tx"}, {"rx", "combined"}, {"tx", "combined"}, 25 {"rx", "tx", "combined"},] 26 27 # Get the set of keys that device actually supports 28 restore = {} 29 supported = set() 30 for key in all_keys: 31 if key + "-max" in chans: 32 supported.add(key) 33 restore |= {key + "-count": chans[key + "-count"]} 34 35 defer(cfg.eth.channels_set, ehdr | restore) 36 37 def test_config(config): 38 try: 39 cfg.eth.channels_set(ehdr | config) 40 get = cfg.eth.channels_get(ehdr) 41 for k, v in config.items(): 42 ksft_eq(get.get(k, 0), v) 43 except NlError as e: 44 failed.append(mix) 45 ksft_pr("Can't set", config, e) 46 else: 47 ksft_pr("Okay", config) 48 49 failed = [] 50 for mix in mixes: 51 if not mix.issubset(supported): 52 continue 53 54 # Set all the values in the mix to 1, other supported to 0 55 config = {} 56 for key in all_keys: 57 config[key + "-count"] = 1 if key in mix else 0 58 test_config(config) 59 60 for mix in mixes: 61 if not mix.issubset(supported): 62 continue 63 if mix in failed: 64 continue 65 66 # Set all the values in the mix to max, other supported to 0 67 config = {} 68 for key in all_keys: 69 config[key + "-count"] = chans[key + '-max'] if key in mix else 0 70 test_config(config) 71 72 73def _configure_min_ring_cnt(cfg) -> None: 74 """ Try to configure a single Rx/Tx ring. """ 75 ehdr = {'header':{'dev-index': cfg.ifindex}} 76 chans = cfg.eth.channels_get(ehdr) 77 78 all_keys = ["rx-count", "tx-count", "combined-count"] 79 restore = {} 80 config = {} 81 for key in all_keys: 82 if key in chans: 83 restore[key] = chans[key] 84 config[key] = 0 85 86 if chans.get('combined-count', 0) > 1: 87 config['combined-count'] = 1 88 elif chans.get('rx-count', 0) > 1 and chans.get('tx-count', 0) > 1: 89 config['tx-count'] = 1 90 config['rx-count'] = 1 91 else: 92 # looks like we're already on 1 channel 93 return 94 95 cfg.eth.channels_set(ehdr | config) 96 defer(cfg.eth.channels_set, ehdr | restore) 97 98 99def ringparam(cfg) -> None: 100 """ 101 Tweak the ringparam configuration. Try to run some traffic over min 102 ring size to make sure it actually functions. 103 """ 104 ehdr = {'header':{'dev-index': cfg.ifindex}} 105 rings = cfg.eth.rings_get(ehdr) 106 107 restore = {} 108 maxes = {} 109 params = set() 110 for key in rings.keys(): 111 if 'max' in key: 112 param = key[:-4] 113 maxes[param] = rings[key] 114 params.add(param) 115 restore[param] = rings[param] 116 117 defer(cfg.eth.rings_set, ehdr | restore) 118 119 # Speed up the reconfig by configuring just one ring 120 _configure_min_ring_cnt(cfg) 121 122 # Try to reach min on all settings 123 for param in params: 124 val = rings[param] 125 while True: 126 try: 127 cfg.eth.rings_set({'header':{'dev-index': cfg.ifindex}, 128 param: val // 2}) 129 if val == 0: 130 break 131 val //= 2 132 except NlError: 133 break 134 135 get = cfg.eth.rings_get(ehdr) 136 ksft_eq(get[param], val) 137 138 ksft_pr(f"Reached min for '{param}' at {val} (max {rings[param]})") 139 140 GenerateTraffic(cfg).wait_pkts_and_stop(10000) 141 142 # Try max across all params, if the driver supports large rings 143 # this may OOM so we ignore errors 144 try: 145 ksft_pr("Applying max settings") 146 config = {p: maxes[p] for p in params} 147 cfg.eth.rings_set(ehdr | config) 148 except NlError as e: 149 ksft_pr("Can't set max params", config, e) 150 else: 151 GenerateTraffic(cfg).wait_pkts_and_stop(10000) 152 153 154def main() -> None: 155 """ Ksft boiler plate main """ 156 157 with NetDrvEpEnv(__file__) as cfg: 158 cfg.eth = EthtoolFamily() 159 160 ksft_run([channels, 161 ringparam], 162 args=(cfg, )) 163 ksft_exit() 164 165 166if __name__ == "__main__": 167 main() 168