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