xref: /linux/tools/testing/selftests/drivers/net/hw/rss_api.py (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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