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