xref: /linux/tools/testing/selftests/drivers/net/ring_reconfig.py (revision 6dfafbd0299a60bfb5d5e277fdf100037c7ded07)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4"""
5Test channel and ring size configuration via ethtool (-L / -G).
6"""
7
8from lib.py import ksft_run, ksft_exit, ksft_pr
9from lib.py import ksft_eq
10from lib.py import NetDrvEpEnv, EthtoolFamily, GenerateTraffic
11from lib.py import defer, NlError
12
13
14def channels(cfg) -> None:
15    """
16    Twiddle channel counts in various combinations of parameters.
17    We're only looking for driver adhering to the requested config
18    if the config is accepted and crashes.
19    """
20    ehdr = {'header':{'dev-index': cfg.ifindex}}
21    chans = cfg.eth.channels_get(ehdr)
22
23    all_keys = ["rx", "tx", "combined"]
24    mixes = [{"combined"}, {"rx", "tx"}, {"rx", "combined"}, {"tx", "combined"},
25             {"rx", "tx", "combined"},]
26
27    # Get the set of keys that device actually supports
28    restore = {}
29    supported = set()
30    for key in all_keys:
31        if key + "-max" in chans:
32            supported.add(key)
33            restore |= {key + "-count": chans[key + "-count"]}
34
35    defer(cfg.eth.channels_set, ehdr | restore)
36
37    def test_config(config):
38        try:
39            cfg.eth.channels_set(ehdr | config)
40            get = cfg.eth.channels_get(ehdr)
41            for k, v in config.items():
42                ksft_eq(get.get(k, 0), v)
43        except NlError as e:
44            failed.append(mix)
45            ksft_pr("Can't set", config, e)
46        else:
47            ksft_pr("Okay", config)
48
49    failed = []
50    for mix in mixes:
51        if not mix.issubset(supported):
52            continue
53
54        # Set all the values in the mix to 1, other supported to 0
55        config = {}
56        for key in all_keys:
57            config[key + "-count"] = 1 if key in mix else 0
58        test_config(config)
59
60    for mix in mixes:
61        if not mix.issubset(supported):
62            continue
63        if mix in failed:
64            continue
65
66        # Set all the values in the mix to max, other supported to 0
67        config = {}
68        for key in all_keys:
69            config[key + "-count"] = chans[key + '-max'] if key in mix else 0
70        test_config(config)
71
72
73def _configure_min_ring_cnt(cfg) -> None:
74    """ Try to configure a single Rx/Tx ring. """
75    ehdr = {'header':{'dev-index': cfg.ifindex}}
76    chans = cfg.eth.channels_get(ehdr)
77
78    all_keys = ["rx-count", "tx-count", "combined-count"]
79    restore = {}
80    config = {}
81    for key in all_keys:
82        if key in chans:
83            restore[key] = chans[key]
84            config[key] = 0
85
86    if chans.get('combined-count', 0) > 1:
87        config['combined-count'] = 1
88    elif chans.get('rx-count', 0) > 1 and chans.get('tx-count', 0) > 1:
89        config['tx-count'] = 1
90        config['rx-count'] = 1
91    else:
92        # looks like we're already on 1 channel
93        return
94
95    cfg.eth.channels_set(ehdr | config)
96    defer(cfg.eth.channels_set, ehdr | restore)
97
98
99def ringparam(cfg) -> None:
100    """
101    Tweak the ringparam configuration. Try to run some traffic over min
102    ring size to make sure it actually functions.
103    """
104    ehdr = {'header':{'dev-index': cfg.ifindex}}
105    rings = cfg.eth.rings_get(ehdr)
106
107    restore = {}
108    maxes = {}
109    params = set()
110    for key in rings.keys():
111        if 'max' in key:
112            param = key[:-4]
113            maxes[param] = rings[key]
114            params.add(param)
115            restore[param] = rings[param]
116
117    defer(cfg.eth.rings_set, ehdr | restore)
118
119    # Speed up the reconfig by configuring just one ring
120    _configure_min_ring_cnt(cfg)
121
122    # Try to reach min on all settings
123    for param in params:
124        val = rings[param]
125        while True:
126            try:
127                cfg.eth.rings_set({'header':{'dev-index': cfg.ifindex},
128                                   param: val // 2})
129                if val == 0:
130                    break
131                val //= 2
132            except NlError:
133                break
134
135        get = cfg.eth.rings_get(ehdr)
136        ksft_eq(get[param], val)
137
138        ksft_pr(f"Reached min for '{param}' at {val} (max {rings[param]})")
139
140    GenerateTraffic(cfg).wait_pkts_and_stop(10000)
141
142    # Try max across all params, if the driver supports large rings
143    # this may OOM so we ignore errors
144    try:
145        ksft_pr("Applying max settings")
146        config = {p: maxes[p] for p in params}
147        cfg.eth.rings_set(ehdr | config)
148    except NlError as e:
149        ksft_pr("Can't set max params", config, e)
150    else:
151        GenerateTraffic(cfg).wait_pkts_and_stop(10000)
152
153
154def main() -> None:
155    """ Ksft boiler plate main """
156
157    with NetDrvEpEnv(__file__) as cfg:
158        cfg.eth = EthtoolFamily()
159
160        ksft_run([channels,
161                  ringparam],
162                 args=(cfg, ))
163    ksft_exit()
164
165
166if __name__ == "__main__":
167    main()
168