14d13c6c4SJakub Kicinski#!/usr/bin/env python3 24d13c6c4SJakub Kicinski# SPDX-License-Identifier: GPL-2.0 34d13c6c4SJakub Kicinski 44d13c6c4SJakub Kicinski""" 54d13c6c4SJakub KicinskiAPI level tests for RSS (mostly Netlink vs IOCTL). 64d13c6c4SJakub Kicinski""" 74d13c6c4SJakub Kicinski 8*4c86c9fdSJakub Kicinskiimport errno 94d13c6c4SJakub Kicinskiimport glob 10169b2620SJakub Kicinskiimport random 116e7eb93aSJakub Kicinskifrom lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises 124d13c6c4SJakub Kicinskifrom lib.py import KsftSkipEx, KsftFailEx 136e7eb93aSJakub Kicinskifrom lib.py import defer, ethtool, CmdExitFailure 146e7eb93aSJakub Kicinskifrom lib.py import EthtoolFamily, NlError 154d13c6c4SJakub Kicinskifrom lib.py import NetDrvEnv 164d13c6c4SJakub Kicinski 174d13c6c4SJakub Kicinski 181560af51SJakub Kicinskidef _require_2qs(cfg): 191560af51SJakub Kicinski qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 201560af51SJakub Kicinski if qcnt < 2: 211560af51SJakub Kicinski raise KsftSkipEx(f"Local has only {qcnt} queues") 221560af51SJakub Kicinski return qcnt 231560af51SJakub Kicinski 241560af51SJakub Kicinski 254d13c6c4SJakub Kicinskidef _ethtool_create(cfg, act, opts): 264d13c6c4SJakub Kicinski output = ethtool(f"{act} {cfg.ifname} {opts}").stdout 274d13c6c4SJakub Kicinski # Output will be something like: "New RSS context is 1" or 284d13c6c4SJakub Kicinski # "Added rule with ID 7", we want the integer from the end 294d13c6c4SJakub Kicinski return int(output.split()[-1]) 304d13c6c4SJakub Kicinski 314d13c6c4SJakub Kicinski 320c8754b7SJakub Kicinskidef _ethtool_get_cfg(cfg, fl_type, to_nl=False): 330c8754b7SJakub Kicinski descr = ethtool(f"-n {cfg.ifname} rx-flow-hash {fl_type}").stdout 340c8754b7SJakub Kicinski 350c8754b7SJakub Kicinski if to_nl: 360c8754b7SJakub Kicinski converter = { 370c8754b7SJakub Kicinski "IP SA": "ip-src", 380c8754b7SJakub Kicinski "IP DA": "ip-dst", 390c8754b7SJakub Kicinski "L4 bytes 0 & 1 [TCP/UDP src port]": "l4-b-0-1", 400c8754b7SJakub Kicinski "L4 bytes 2 & 3 [TCP/UDP dst port]": "l4-b-2-3", 410c8754b7SJakub Kicinski } 420c8754b7SJakub Kicinski 430c8754b7SJakub Kicinski ret = set() 440c8754b7SJakub Kicinski else: 450c8754b7SJakub Kicinski converter = { 460c8754b7SJakub Kicinski "IP SA": "s", 470c8754b7SJakub Kicinski "IP DA": "d", 480c8754b7SJakub Kicinski "L3 proto": "t", 490c8754b7SJakub Kicinski "L4 bytes 0 & 1 [TCP/UDP src port]": "f", 500c8754b7SJakub Kicinski "L4 bytes 2 & 3 [TCP/UDP dst port]": "n", 510c8754b7SJakub Kicinski } 520c8754b7SJakub Kicinski 530c8754b7SJakub Kicinski ret = "" 540c8754b7SJakub Kicinski 550c8754b7SJakub Kicinski for line in descr.split("\n")[1:-2]: 560c8754b7SJakub Kicinski # if this raises we probably need to add more keys to converter above 570c8754b7SJakub Kicinski if to_nl: 580c8754b7SJakub Kicinski ret.add(converter[line]) 590c8754b7SJakub Kicinski else: 600c8754b7SJakub Kicinski ret += converter[line] 610c8754b7SJakub Kicinski return ret 620c8754b7SJakub Kicinski 630c8754b7SJakub Kicinski 646e7eb93aSJakub Kicinskidef test_rxfh_nl_set_fail(cfg): 656e7eb93aSJakub Kicinski """ 666e7eb93aSJakub Kicinski Test error path of Netlink SET. 676e7eb93aSJakub Kicinski """ 686e7eb93aSJakub Kicinski _require_2qs(cfg) 696e7eb93aSJakub Kicinski 706e7eb93aSJakub Kicinski ethnl = EthtoolFamily() 716e7eb93aSJakub Kicinski ethnl.ntf_subscribe("monitor") 726e7eb93aSJakub Kicinski 736e7eb93aSJakub Kicinski with ksft_raises(NlError): 746e7eb93aSJakub Kicinski ethnl.rss_set({"header": {"dev-name": "lo"}, 756e7eb93aSJakub Kicinski "indir": None}) 766e7eb93aSJakub Kicinski 776e7eb93aSJakub Kicinski with ksft_raises(NlError): 786e7eb93aSJakub Kicinski ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 796e7eb93aSJakub Kicinski "indir": [100000]}) 806e7eb93aSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 816e7eb93aSJakub Kicinski ksft_is(ntf, None) 826e7eb93aSJakub Kicinski 836e7eb93aSJakub Kicinski 846e7eb93aSJakub Kicinskidef test_rxfh_nl_set_indir(cfg): 856e7eb93aSJakub Kicinski """ 866e7eb93aSJakub Kicinski Test setting indirection table via Netlink. 876e7eb93aSJakub Kicinski """ 886e7eb93aSJakub Kicinski qcnt = _require_2qs(cfg) 896e7eb93aSJakub Kicinski 906e7eb93aSJakub Kicinski # Test some SETs with a value 916e7eb93aSJakub Kicinski reset = defer(cfg.ethnl.rss_set, 926e7eb93aSJakub Kicinski {"header": {"dev-index": cfg.ifindex}, "indir": None}) 936e7eb93aSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 946e7eb93aSJakub Kicinski "indir": [1]}) 956e7eb93aSJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 966e7eb93aSJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), {1}) 976e7eb93aSJakub Kicinski 986e7eb93aSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 996e7eb93aSJakub Kicinski "indir": [0, 1]}) 1006e7eb93aSJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 1016e7eb93aSJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 1026e7eb93aSJakub Kicinski 1036e7eb93aSJakub Kicinski # Make sure we can't set the queue count below max queue used 1046e7eb93aSJakub Kicinski with ksft_raises(CmdExitFailure): 1056e7eb93aSJakub Kicinski ethtool(f"-L {cfg.ifname} combined 0 rx 1") 1066e7eb93aSJakub Kicinski with ksft_raises(CmdExitFailure): 1076e7eb93aSJakub Kicinski ethtool(f"-L {cfg.ifname} combined 1 rx 0") 1086e7eb93aSJakub Kicinski 1096e7eb93aSJakub Kicinski # Test reset back to default 1106e7eb93aSJakub Kicinski reset.exec() 1116e7eb93aSJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 1126e7eb93aSJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), set(range(qcnt))) 1136e7eb93aSJakub Kicinski 1146e7eb93aSJakub Kicinski 1156e7eb93aSJakub Kicinskidef test_rxfh_nl_set_indir_ctx(cfg): 1166e7eb93aSJakub Kicinski """ 1176e7eb93aSJakub Kicinski Test setting indirection table for a custom context via Netlink. 1186e7eb93aSJakub Kicinski """ 1196e7eb93aSJakub Kicinski _require_2qs(cfg) 1206e7eb93aSJakub Kicinski 1216e7eb93aSJakub Kicinski # Get setting for ctx 0, we'll make sure they don't get clobbered 1226e7eb93aSJakub Kicinski dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 1236e7eb93aSJakub Kicinski 1246e7eb93aSJakub Kicinski # Create context 1256e7eb93aSJakub Kicinski ctx_id = _ethtool_create(cfg, "-X", "context new") 1266e7eb93aSJakub Kicinski defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") 1276e7eb93aSJakub Kicinski 1286e7eb93aSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 1296e7eb93aSJakub Kicinski "context": ctx_id, "indir": [1]}) 1306e7eb93aSJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, 1316e7eb93aSJakub Kicinski "context": ctx_id}) 1326e7eb93aSJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), {1}) 1336e7eb93aSJakub Kicinski 1346e7eb93aSJakub Kicinski ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 1356e7eb93aSJakub Kicinski ksft_eq(ctx0, dflt) 1366e7eb93aSJakub Kicinski 1376e7eb93aSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 1386e7eb93aSJakub Kicinski "context": ctx_id, "indir": [0, 1]}) 1396e7eb93aSJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}, 1406e7eb93aSJakub Kicinski "context": ctx_id}) 1416e7eb93aSJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 1426e7eb93aSJakub Kicinski 1436e7eb93aSJakub Kicinski ctx0 = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 1446e7eb93aSJakub Kicinski ksft_eq(ctx0, dflt) 1456e7eb93aSJakub Kicinski 1466e7eb93aSJakub Kicinski # Make sure we can't set the queue count below max queue used 1476e7eb93aSJakub Kicinski with ksft_raises(CmdExitFailure): 1486e7eb93aSJakub Kicinski ethtool(f"-L {cfg.ifname} combined 0 rx 1") 1496e7eb93aSJakub Kicinski with ksft_raises(CmdExitFailure): 1506e7eb93aSJakub Kicinski ethtool(f"-L {cfg.ifname} combined 1 rx 0") 1516e7eb93aSJakub Kicinski 1526e7eb93aSJakub Kicinski 1534d13c6c4SJakub Kicinskidef test_rxfh_indir_ntf(cfg): 1544d13c6c4SJakub Kicinski """ 1554d13c6c4SJakub Kicinski Check that Netlink notifications are generated when RSS indirection 1564d13c6c4SJakub Kicinski table was modified. 1574d13c6c4SJakub Kicinski """ 1581560af51SJakub Kicinski _require_2qs(cfg) 1594d13c6c4SJakub Kicinski 1604d13c6c4SJakub Kicinski ethnl = EthtoolFamily() 1614d13c6c4SJakub Kicinski ethnl.ntf_subscribe("monitor") 1624d13c6c4SJakub Kicinski 1634d13c6c4SJakub Kicinski ethtool(f"--disable-netlink -X {cfg.ifname} weight 0 1") 1644d13c6c4SJakub Kicinski reset = defer(ethtool, f"-X {cfg.ifname} default") 1654d13c6c4SJakub Kicinski 1664d13c6c4SJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 1674d13c6c4SJakub Kicinski if ntf is None: 1684d13c6c4SJakub Kicinski raise KsftFailEx("No notification received") 1694d13c6c4SJakub Kicinski ksft_eq(ntf["name"], "rss-ntf") 1704d13c6c4SJakub Kicinski ksft_eq(set(ntf["msg"]["indir"]), {1}) 1714d13c6c4SJakub Kicinski 1724d13c6c4SJakub Kicinski reset.exec() 1734d13c6c4SJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 1744d13c6c4SJakub Kicinski if ntf is None: 1754d13c6c4SJakub Kicinski raise KsftFailEx("No notification received after reset") 1764d13c6c4SJakub Kicinski ksft_eq(ntf["name"], "rss-ntf") 1774d13c6c4SJakub Kicinski ksft_is(ntf["msg"].get("context"), None) 1784d13c6c4SJakub Kicinski ksft_ne(set(ntf["msg"]["indir"]), {1}) 1794d13c6c4SJakub Kicinski 1804d13c6c4SJakub Kicinski 1814d13c6c4SJakub Kicinskidef test_rxfh_indir_ctx_ntf(cfg): 1824d13c6c4SJakub Kicinski """ 1834d13c6c4SJakub Kicinski Check that Netlink notifications are generated when RSS indirection 1844d13c6c4SJakub Kicinski table was modified on an additional RSS context. 1854d13c6c4SJakub Kicinski """ 1861560af51SJakub Kicinski _require_2qs(cfg) 1874d13c6c4SJakub Kicinski 1884d13c6c4SJakub Kicinski ctx_id = _ethtool_create(cfg, "-X", "context new") 1894d13c6c4SJakub Kicinski defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") 1904d13c6c4SJakub Kicinski 1914d13c6c4SJakub Kicinski ethnl = EthtoolFamily() 1924d13c6c4SJakub Kicinski ethnl.ntf_subscribe("monitor") 1934d13c6c4SJakub Kicinski 1944d13c6c4SJakub Kicinski ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} weight 0 1") 1954d13c6c4SJakub Kicinski 1964d13c6c4SJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 1974d13c6c4SJakub Kicinski if ntf is None: 1984d13c6c4SJakub Kicinski raise KsftFailEx("No notification received") 1994d13c6c4SJakub Kicinski ksft_eq(ntf["name"], "rss-ntf") 2004d13c6c4SJakub Kicinski ksft_eq(ntf["msg"].get("context"), ctx_id) 2014d13c6c4SJakub Kicinski ksft_eq(set(ntf["msg"]["indir"]), {1}) 2024d13c6c4SJakub Kicinski 2034d13c6c4SJakub Kicinski 204169b2620SJakub Kicinskidef test_rxfh_nl_set_key(cfg): 205169b2620SJakub Kicinski """ 206169b2620SJakub Kicinski Test setting hashing key via Netlink. 207169b2620SJakub Kicinski """ 208169b2620SJakub Kicinski 209169b2620SJakub Kicinski dflt = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 210169b2620SJakub Kicinski defer(cfg.ethnl.rss_set, 211169b2620SJakub Kicinski {"header": {"dev-index": cfg.ifindex}, 212169b2620SJakub Kicinski "hkey": dflt["hkey"], "indir": None}) 213169b2620SJakub Kicinski 214169b2620SJakub Kicinski # Empty key should error out 215169b2620SJakub Kicinski with ksft_raises(NlError) as cm: 216169b2620SJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 217169b2620SJakub Kicinski "hkey": None}) 218169b2620SJakub Kicinski ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.hkey') 219169b2620SJakub Kicinski 220169b2620SJakub Kicinski # Set key to random 221169b2620SJakub Kicinski mod = random.randbytes(len(dflt["hkey"])) 222169b2620SJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 223169b2620SJakub Kicinski "hkey": mod}) 224169b2620SJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 225169b2620SJakub Kicinski ksft_eq(rss.get("hkey", [-1]), mod) 226169b2620SJakub Kicinski 227169b2620SJakub Kicinski # Set key to random and indir tbl to something at once 228169b2620SJakub Kicinski mod = random.randbytes(len(dflt["hkey"])) 229169b2620SJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 230169b2620SJakub Kicinski "indir": [0, 1], "hkey": mod}) 231169b2620SJakub Kicinski rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 232169b2620SJakub Kicinski ksft_eq(rss.get("hkey", [-1]), mod) 233169b2620SJakub Kicinski ksft_eq(set(rss.get("indir", [-1])), {0, 1}) 234169b2620SJakub Kicinski 235169b2620SJakub Kicinski 2360c8754b7SJakub Kicinskidef test_rxfh_fields(cfg): 2370c8754b7SJakub Kicinski """ 2380c8754b7SJakub Kicinski Test reading Rx Flow Hash over Netlink. 2390c8754b7SJakub Kicinski """ 2400c8754b7SJakub Kicinski 2410c8754b7SJakub Kicinski flow_types = ["tcp4", "tcp6", "udp4", "udp6"] 2420c8754b7SJakub Kicinski ethnl = EthtoolFamily() 2430c8754b7SJakub Kicinski 2440c8754b7SJakub Kicinski cfg_nl = ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 2450c8754b7SJakub Kicinski for fl_type in flow_types: 2460c8754b7SJakub Kicinski one = _ethtool_get_cfg(cfg, fl_type, to_nl=True) 2470c8754b7SJakub Kicinski ksft_eq(one, cfg_nl["flow-hash"][fl_type], 2480c8754b7SJakub Kicinski comment="Config for " + fl_type) 2490c8754b7SJakub Kicinski 2500c8754b7SJakub Kicinski 25100e6c61cSJakub Kicinskidef test_rxfh_fields_set(cfg): 25200e6c61cSJakub Kicinski """ Test configuring Rx Flow Hash over Netlink. """ 25300e6c61cSJakub Kicinski 25400e6c61cSJakub Kicinski flow_types = ["tcp4", "tcp6", "udp4", "udp6"] 25500e6c61cSJakub Kicinski 25600e6c61cSJakub Kicinski # Collect current settings 25700e6c61cSJakub Kicinski cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 25800e6c61cSJakub Kicinski # symmetric hashing is config-order-sensitive make sure we leave 25900e6c61cSJakub Kicinski # symmetric mode, or make the flow-hash sym-compatible first 26000e6c61cSJakub Kicinski changes = [{"flow-hash": cfg_old["flow-hash"],}, 26100e6c61cSJakub Kicinski {"input-xfrm": cfg_old.get("input-xfrm", {}),}] 26200e6c61cSJakub Kicinski if cfg_old.get("input-xfrm"): 26300e6c61cSJakub Kicinski changes = list(reversed(changes)) 26400e6c61cSJakub Kicinski for old in changes: 26500e6c61cSJakub Kicinski defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) 26600e6c61cSJakub Kicinski 26700e6c61cSJakub Kicinski # symmetric hashing prevents some of the configs below 26800e6c61cSJakub Kicinski if cfg_old.get("input-xfrm"): 26900e6c61cSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 27000e6c61cSJakub Kicinski "input-xfrm": {}}) 27100e6c61cSJakub Kicinski 27200e6c61cSJakub Kicinski for fl_type in flow_types: 27300e6c61cSJakub Kicinski cur = _ethtool_get_cfg(cfg, fl_type) 27400e6c61cSJakub Kicinski if cur == "sdfn": 27500e6c61cSJakub Kicinski change_nl = {"ip-src", "ip-dst"} 27600e6c61cSJakub Kicinski change_ic = "sd" 27700e6c61cSJakub Kicinski else: 27800e6c61cSJakub Kicinski change_nl = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} 27900e6c61cSJakub Kicinski change_ic = "sdfn" 28000e6c61cSJakub Kicinski 28100e6c61cSJakub Kicinski cfg.ethnl.rss_set({ 28200e6c61cSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 28300e6c61cSJakub Kicinski "flow-hash": {fl_type: change_nl} 28400e6c61cSJakub Kicinski }) 28500e6c61cSJakub Kicinski reset = defer(ethtool, f"--disable-netlink -N {cfg.ifname} " 28600e6c61cSJakub Kicinski f"rx-flow-hash {fl_type} {cur}") 28700e6c61cSJakub Kicinski 28800e6c61cSJakub Kicinski cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 28900e6c61cSJakub Kicinski ksft_eq(change_nl, cfg_nl["flow-hash"][fl_type], 29000e6c61cSJakub Kicinski comment=f"Config for {fl_type} over Netlink") 29100e6c61cSJakub Kicinski cfg_ic = _ethtool_get_cfg(cfg, fl_type) 29200e6c61cSJakub Kicinski ksft_eq(change_ic, cfg_ic, 29300e6c61cSJakub Kicinski comment=f"Config for {fl_type} over IOCTL") 29400e6c61cSJakub Kicinski 29500e6c61cSJakub Kicinski reset.exec() 29600e6c61cSJakub Kicinski cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 29700e6c61cSJakub Kicinski ksft_eq(cfg_old["flow-hash"][fl_type], cfg_nl["flow-hash"][fl_type], 29800e6c61cSJakub Kicinski comment=f"Un-config for {fl_type} over Netlink") 29900e6c61cSJakub Kicinski cfg_ic = _ethtool_get_cfg(cfg, fl_type) 30000e6c61cSJakub Kicinski ksft_eq(cur, cfg_ic, comment=f"Un-config for {fl_type} over IOCTL") 30100e6c61cSJakub Kicinski 30200e6c61cSJakub Kicinski # Try to set multiple at once, the defer was already installed at the start 30300e6c61cSJakub Kicinski change = {"ip-src"} 30400e6c61cSJakub Kicinski if change == cfg_old["flow-hash"]["tcp4"]: 30500e6c61cSJakub Kicinski change = {"ip-dst"} 30600e6c61cSJakub Kicinski cfg.ethnl.rss_set({ 30700e6c61cSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 30800e6c61cSJakub Kicinski "flow-hash": {x: change for x in flow_types} 30900e6c61cSJakub Kicinski }) 31000e6c61cSJakub Kicinski 31100e6c61cSJakub Kicinski cfg_nl = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 31200e6c61cSJakub Kicinski for fl_type in flow_types: 31300e6c61cSJakub Kicinski ksft_eq(change, cfg_nl["flow-hash"][fl_type], 31400e6c61cSJakub Kicinski comment=f"multi-config for {fl_type} over Netlink") 31500e6c61cSJakub Kicinski 31600e6c61cSJakub Kicinski 31700e6c61cSJakub Kicinskidef test_rxfh_fields_set_xfrm(cfg): 31800e6c61cSJakub Kicinski """ Test changing Rx Flow Hash vs xfrm_input at once. """ 31900e6c61cSJakub Kicinski 32000e6c61cSJakub Kicinski def set_rss(cfg, xfrm, fh): 32100e6c61cSJakub Kicinski cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, 32200e6c61cSJakub Kicinski "input-xfrm": xfrm, "flow-hash": fh}) 32300e6c61cSJakub Kicinski 32400e6c61cSJakub Kicinski # Install the reset handler 32500e6c61cSJakub Kicinski cfg_old = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 32600e6c61cSJakub Kicinski # symmetric hashing is config-order-sensitive make sure we leave 32700e6c61cSJakub Kicinski # symmetric mode, or make the flow-hash sym-compatible first 32800e6c61cSJakub Kicinski changes = [{"flow-hash": cfg_old["flow-hash"],}, 32900e6c61cSJakub Kicinski {"input-xfrm": cfg_old.get("input-xfrm", {}),}] 33000e6c61cSJakub Kicinski if cfg_old.get("input-xfrm"): 33100e6c61cSJakub Kicinski changes = list(reversed(changes)) 33200e6c61cSJakub Kicinski for old in changes: 33300e6c61cSJakub Kicinski defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex},} | old) 33400e6c61cSJakub Kicinski 33500e6c61cSJakub Kicinski # Make sure we start with input-xfrm off, and tcp4 config non-sym 33600e6c61cSJakub Kicinski set_rss(cfg, {}, {}) 33700e6c61cSJakub Kicinski set_rss(cfg, {}, {"tcp4": {"ip-src"}}) 33800e6c61cSJakub Kicinski 33900e6c61cSJakub Kicinski # Setting sym and fixing tcp4 config not expected to pass right now 34000e6c61cSJakub Kicinski with ksft_raises(NlError): 34100e6c61cSJakub Kicinski set_rss(cfg, {"sym-xor"}, {"tcp4": {"ip-src", "ip-dst"}}) 34200e6c61cSJakub Kicinski # One at a time should work, hopefully 34300e6c61cSJakub Kicinski set_rss(cfg, 0, {"tcp4": {"ip-src", "ip-dst"}}) 34400e6c61cSJakub Kicinski no_support = False 34500e6c61cSJakub Kicinski try: 34600e6c61cSJakub Kicinski set_rss(cfg, {"sym-xor"}, {}) 34700e6c61cSJakub Kicinski except NlError: 34800e6c61cSJakub Kicinski try: 34900e6c61cSJakub Kicinski set_rss(cfg, {"sym-or-xor"}, {}) 35000e6c61cSJakub Kicinski except NlError: 35100e6c61cSJakub Kicinski no_support = True 35200e6c61cSJakub Kicinski if no_support: 35300e6c61cSJakub Kicinski raise KsftSkipEx("no input-xfrm supported") 35400e6c61cSJakub Kicinski # Disabling two at once should not work either without kernel changes 35500e6c61cSJakub Kicinski with ksft_raises(NlError): 35600e6c61cSJakub Kicinski set_rss(cfg, {}, {"tcp4": {"ip-src"}}) 35700e6c61cSJakub Kicinski 35800e6c61cSJakub Kicinski 35900e6c61cSJakub Kicinskidef test_rxfh_fields_ntf(cfg): 36000e6c61cSJakub Kicinski """ Test Rx Flow Hash notifications. """ 36100e6c61cSJakub Kicinski 36200e6c61cSJakub Kicinski cur = _ethtool_get_cfg(cfg, "tcp4") 36300e6c61cSJakub Kicinski if cur == "sdfn": 36400e6c61cSJakub Kicinski change = {"ip-src", "ip-dst"} 36500e6c61cSJakub Kicinski else: 36600e6c61cSJakub Kicinski change = {"l4-b-0-1", "l4-b-2-3", "ip-src", "ip-dst"} 36700e6c61cSJakub Kicinski 36800e6c61cSJakub Kicinski ethnl = EthtoolFamily() 36900e6c61cSJakub Kicinski ethnl.ntf_subscribe("monitor") 37000e6c61cSJakub Kicinski 37100e6c61cSJakub Kicinski ethnl.rss_set({ 37200e6c61cSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 37300e6c61cSJakub Kicinski "flow-hash": {"tcp4": change} 37400e6c61cSJakub Kicinski }) 37500e6c61cSJakub Kicinski reset = defer(ethtool, 37600e6c61cSJakub Kicinski f"--disable-netlink -N {cfg.ifname} rx-flow-hash tcp4 {cur}") 37700e6c61cSJakub Kicinski 37800e6c61cSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 37900e6c61cSJakub Kicinski if ntf is None: 38000e6c61cSJakub Kicinski raise KsftFailEx("No notification received after IOCTL change") 38100e6c61cSJakub Kicinski ksft_eq(ntf["name"], "rss-ntf") 38200e6c61cSJakub Kicinski ksft_eq(ntf["msg"]["flow-hash"]["tcp4"], change) 38300e6c61cSJakub Kicinski ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) 38400e6c61cSJakub Kicinski 38500e6c61cSJakub Kicinski reset.exec() 38600e6c61cSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 38700e6c61cSJakub Kicinski if ntf is None: 38800e6c61cSJakub Kicinski raise KsftFailEx("No notification received after Netlink change") 38900e6c61cSJakub Kicinski ksft_eq(ntf["name"], "rss-ntf") 39000e6c61cSJakub Kicinski ksft_ne(ntf["msg"]["flow-hash"]["tcp4"], change) 39100e6c61cSJakub Kicinski ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) 39200e6c61cSJakub Kicinski 39300e6c61cSJakub Kicinski 394*4c86c9fdSJakub Kicinskidef test_rss_ctx_add(cfg): 395*4c86c9fdSJakub Kicinski """ Test creating an additional RSS context via Netlink """ 396*4c86c9fdSJakub Kicinski 397*4c86c9fdSJakub Kicinski _require_2qs(cfg) 398*4c86c9fdSJakub Kicinski 399*4c86c9fdSJakub Kicinski # Test basic creation 400*4c86c9fdSJakub Kicinski ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) 401*4c86c9fdSJakub Kicinski d = defer(ethtool, f"-X {cfg.ifname} context {ctx.get('context')} delete") 402*4c86c9fdSJakub Kicinski ksft_ne(ctx.get("context", 0), 0) 403*4c86c9fdSJakub Kicinski ksft_ne(set(ctx.get("indir", [0])), {0}, 404*4c86c9fdSJakub Kicinski comment="Driver should init the indirection table") 405*4c86c9fdSJakub Kicinski 406*4c86c9fdSJakub Kicinski # Try requesting the ID we just got allocated 407*4c86c9fdSJakub Kicinski with ksft_raises(NlError) as cm: 408*4c86c9fdSJakub Kicinski ctx = cfg.ethnl.rss_create_act({ 409*4c86c9fdSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 410*4c86c9fdSJakub Kicinski "context": ctx.get("context"), 411*4c86c9fdSJakub Kicinski }) 412*4c86c9fdSJakub Kicinski ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") 413*4c86c9fdSJakub Kicinski d.exec() 414*4c86c9fdSJakub Kicinski ksft_eq(cm.exception.nl_msg.error, -errno.EBUSY) 415*4c86c9fdSJakub Kicinski 416*4c86c9fdSJakub Kicinski # Test creating with a specified RSS table, and context ID 417*4c86c9fdSJakub Kicinski ctx_id = ctx.get("context") 418*4c86c9fdSJakub Kicinski ctx = cfg.ethnl.rss_create_act({ 419*4c86c9fdSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 420*4c86c9fdSJakub Kicinski "context": ctx_id, 421*4c86c9fdSJakub Kicinski "indir": [1], 422*4c86c9fdSJakub Kicinski }) 423*4c86c9fdSJakub Kicinski ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") 424*4c86c9fdSJakub Kicinski ksft_eq(ctx.get("context"), ctx_id) 425*4c86c9fdSJakub Kicinski ksft_eq(set(ctx.get("indir", [0])), {1}) 426*4c86c9fdSJakub Kicinski 427*4c86c9fdSJakub Kicinski 428*4c86c9fdSJakub Kicinskidef test_rss_ctx_ntf(cfg): 429*4c86c9fdSJakub Kicinski """ Test notifications for creating additional RSS contexts """ 430*4c86c9fdSJakub Kicinski 431*4c86c9fdSJakub Kicinski ethnl = EthtoolFamily() 432*4c86c9fdSJakub Kicinski ethnl.ntf_subscribe("monitor") 433*4c86c9fdSJakub Kicinski 434*4c86c9fdSJakub Kicinski # Create / delete via Netlink 435*4c86c9fdSJakub Kicinski ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) 436*4c86c9fdSJakub Kicinski cfg.ethnl.rss_delete_act({ 437*4c86c9fdSJakub Kicinski "header": {"dev-index": cfg.ifindex}, 438*4c86c9fdSJakub Kicinski "context": ctx["context"], 439*4c86c9fdSJakub Kicinski }) 440*4c86c9fdSJakub Kicinski 441*4c86c9fdSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 442*4c86c9fdSJakub Kicinski if ntf is None: 443*4c86c9fdSJakub Kicinski raise KsftFailEx("[NL] No notification after context creation") 444*4c86c9fdSJakub Kicinski ksft_eq(ntf["name"], "rss-create-ntf") 445*4c86c9fdSJakub Kicinski ksft_eq(ctx, ntf["msg"]) 446*4c86c9fdSJakub Kicinski 447*4c86c9fdSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 448*4c86c9fdSJakub Kicinski if ntf is None: 449*4c86c9fdSJakub Kicinski raise KsftFailEx("[NL] No notification after context deletion") 450*4c86c9fdSJakub Kicinski ksft_eq(ntf["name"], "rss-delete-ntf") 451*4c86c9fdSJakub Kicinski 452*4c86c9fdSJakub Kicinski # Create / deleve via IOCTL 453*4c86c9fdSJakub Kicinski ctx_id = _ethtool_create(cfg, "--disable-netlink -X", "context new") 454*4c86c9fdSJakub Kicinski ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} delete") 455*4c86c9fdSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 456*4c86c9fdSJakub Kicinski if ntf is None: 457*4c86c9fdSJakub Kicinski raise KsftFailEx("[IOCTL] No notification after context creation") 458*4c86c9fdSJakub Kicinski ksft_eq(ntf["name"], "rss-create-ntf") 459*4c86c9fdSJakub Kicinski 460*4c86c9fdSJakub Kicinski ntf = next(ethnl.poll_ntf(duration=0.2), None) 461*4c86c9fdSJakub Kicinski if ntf is None: 462*4c86c9fdSJakub Kicinski raise KsftFailEx("[IOCTL] No notification after context deletion") 463*4c86c9fdSJakub Kicinski ksft_eq(ntf["name"], "rss-delete-ntf") 464*4c86c9fdSJakub Kicinski 465*4c86c9fdSJakub Kicinski 4664d13c6c4SJakub Kicinskidef main() -> None: 4674d13c6c4SJakub Kicinski """ Ksft boiler plate main """ 4684d13c6c4SJakub Kicinski 4694d13c6c4SJakub Kicinski with NetDrvEnv(__file__, nsim_test=False) as cfg: 4706e7eb93aSJakub Kicinski cfg.ethnl = EthtoolFamily() 4714d13c6c4SJakub Kicinski ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) 4724d13c6c4SJakub Kicinski ksft_exit() 4734d13c6c4SJakub Kicinski 4744d13c6c4SJakub Kicinski 4754d13c6c4SJakub Kicinskiif __name__ == "__main__": 4764d13c6c4SJakub Kicinski main() 477