1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5API level tests for RSS (mostly Netlink vs IOCTL). 6""" 7 8import glob 9from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne 10from lib.py import KsftSkipEx, KsftFailEx 11from lib.py import defer, ethtool 12from lib.py import EthtoolFamily 13from lib.py import NetDrvEnv 14 15 16def _require_2qs(cfg): 17 qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 18 if qcnt < 2: 19 raise KsftSkipEx(f"Local has only {qcnt} queues") 20 return qcnt 21 22 23def _ethtool_create(cfg, act, opts): 24 output = ethtool(f"{act} {cfg.ifname} {opts}").stdout 25 # Output will be something like: "New RSS context is 1" or 26 # "Added rule with ID 7", we want the integer from the end 27 return int(output.split()[-1]) 28 29 30def _ethtool_get_cfg(cfg, fl_type, to_nl=False): 31 descr = ethtool(f"-n {cfg.ifname} rx-flow-hash {fl_type}").stdout 32 33 if to_nl: 34 converter = { 35 "IP SA": "ip-src", 36 "IP DA": "ip-dst", 37 "L4 bytes 0 & 1 [TCP/UDP src port]": "l4-b-0-1", 38 "L4 bytes 2 & 3 [TCP/UDP dst port]": "l4-b-2-3", 39 } 40 41 ret = set() 42 else: 43 converter = { 44 "IP SA": "s", 45 "IP DA": "d", 46 "L3 proto": "t", 47 "L4 bytes 0 & 1 [TCP/UDP src port]": "f", 48 "L4 bytes 2 & 3 [TCP/UDP dst port]": "n", 49 } 50 51 ret = "" 52 53 for line in descr.split("\n")[1:-2]: 54 # if this raises we probably need to add more keys to converter above 55 if to_nl: 56 ret.add(converter[line]) 57 else: 58 ret += converter[line] 59 return ret 60 61 62def test_rxfh_indir_ntf(cfg): 63 """ 64 Check that Netlink notifications are generated when RSS indirection 65 table was modified. 66 """ 67 _require_2qs(cfg) 68 69 ethnl = EthtoolFamily() 70 ethnl.ntf_subscribe("monitor") 71 72 ethtool(f"--disable-netlink -X {cfg.ifname} weight 0 1") 73 reset = defer(ethtool, f"-X {cfg.ifname} default") 74 75 ntf = next(ethnl.poll_ntf(duration=0.2), None) 76 if ntf is None: 77 raise KsftFailEx("No notification received") 78 ksft_eq(ntf["name"], "rss-ntf") 79 ksft_eq(set(ntf["msg"]["indir"]), {1}) 80 81 reset.exec() 82 ntf = next(ethnl.poll_ntf(duration=0.2), None) 83 if ntf is None: 84 raise KsftFailEx("No notification received after reset") 85 ksft_eq(ntf["name"], "rss-ntf") 86 ksft_is(ntf["msg"].get("context"), None) 87 ksft_ne(set(ntf["msg"]["indir"]), {1}) 88 89 90def test_rxfh_indir_ctx_ntf(cfg): 91 """ 92 Check that Netlink notifications are generated when RSS indirection 93 table was modified on an additional RSS context. 94 """ 95 _require_2qs(cfg) 96 97 ctx_id = _ethtool_create(cfg, "-X", "context new") 98 defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") 99 100 ethnl = EthtoolFamily() 101 ethnl.ntf_subscribe("monitor") 102 103 ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} weight 0 1") 104 105 ntf = next(ethnl.poll_ntf(duration=0.2), None) 106 if ntf is None: 107 raise KsftFailEx("No notification received") 108 ksft_eq(ntf["name"], "rss-ntf") 109 ksft_eq(ntf["msg"].get("context"), ctx_id) 110 ksft_eq(set(ntf["msg"]["indir"]), {1}) 111 112 113def test_rxfh_fields(cfg): 114 """ 115 Test reading Rx Flow Hash over Netlink. 116 """ 117 118 flow_types = ["tcp4", "tcp6", "udp4", "udp6"] 119 ethnl = EthtoolFamily() 120 121 cfg_nl = ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 122 for fl_type in flow_types: 123 one = _ethtool_get_cfg(cfg, fl_type, to_nl=True) 124 ksft_eq(one, cfg_nl["flow-hash"][fl_type], 125 comment="Config for " + fl_type) 126 127 128def main() -> None: 129 """ Ksft boiler plate main """ 130 131 with NetDrvEnv(__file__, nsim_test=False) as cfg: 132 ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) 133 ksft_exit() 134 135 136if __name__ == "__main__": 137 main() 138