xref: /linux/tools/testing/selftests/drivers/net/hds.py (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4import errno
5import os
6from typing import Union
7from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises, KsftSkipEx
8from lib.py import CmdExitFailure, EthtoolFamily, NlError
9from lib.py import NetDrvEnv
10from lib.py import defer, ethtool, ip, random
11
12
13def _get_hds_mode(cfg, netnl) -> str:
14    try:
15        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
16    except NlError as e:
17        raise KsftSkipEx('ring-get not supported by device')
18    if 'tcp-data-split' not in rings:
19        raise KsftSkipEx('tcp-data-split not supported by device')
20    return rings['tcp-data-split']
21
22
23def _xdp_onoff(cfg):
24    prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
25    ip("link set dev %s xdp obj %s sec xdp" %
26       (cfg.ifname, prog))
27    ip("link set dev %s xdp off" % cfg.ifname)
28
29
30def _ioctl_ringparam_modify(cfg, netnl) -> None:
31    """
32    Helper for performing a hopefully unimportant IOCTL SET.
33    IOCTL does not support HDS, so it should not affect the HDS config.
34    """
35    try:
36        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
37    except NlError as e:
38        raise KsftSkipEx('ring-get not supported by device')
39
40    if 'tx' not in rings:
41        raise KsftSkipEx('setting Tx ring size not supported')
42
43    try:
44        ethtool(f"--disable-netlink -G {cfg.ifname} tx {rings['tx'] // 2}")
45    except CmdExitFailure as e:
46        ethtool(f"--disable-netlink -G {cfg.ifname} tx {rings['tx'] * 2}")
47    defer(ethtool, f"-G {cfg.ifname} tx {rings['tx']}")
48
49
50def get_hds(cfg, netnl) -> None:
51    _get_hds_mode(cfg, netnl)
52
53
54def get_hds_thresh(cfg, netnl) -> None:
55    try:
56        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
57    except NlError as e:
58        raise KsftSkipEx('ring-get not supported by device')
59    if 'hds-thresh' not in rings:
60        raise KsftSkipEx('hds-thresh not supported by device')
61
62
63def _hds_reset(cfg, netnl, rings) -> None:
64    cur = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
65
66    arg = {'header': {'dev-index': cfg.ifindex}}
67    if cur.get('tcp-data-split') != rings.get('tcp-data-split'):
68        # Try to reset to "unknown" first, we don't know if the setting
69        # was the default or user chose it. Default seems more likely.
70        arg['tcp-data-split'] = "unknown"
71        netnl.rings_set(arg)
72        cur = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
73        if cur['tcp-data-split'] == rings['tcp-data-split']:
74            del arg['tcp-data-split']
75        else:
76            # Try the explicit setting
77            arg['tcp-data-split'] = rings['tcp-data-split']
78    if cur.get('hds-thresh') != rings.get('hds-thresh'):
79        arg['hds-thresh'] = rings['hds-thresh']
80    if len(arg) > 1:
81        netnl.rings_set(arg)
82
83
84def _defer_reset_hds(cfg, netnl) -> Union[dict, None]:
85    try:
86        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
87        if 'hds-thresh' in rings or 'tcp-data-split' in rings:
88            defer(_hds_reset, cfg, netnl, rings)
89    except NlError as e:
90        pass
91
92
93def set_hds_enable(cfg, netnl) -> None:
94    _defer_reset_hds(cfg, netnl)
95    try:
96        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'tcp-data-split': 'enabled'})
97    except NlError as e:
98        if e.error == errno.EINVAL:
99            raise KsftSkipEx("disabling of HDS not supported by the device")
100        elif e.error == errno.EOPNOTSUPP:
101            raise KsftSkipEx("ring-set not supported by the device")
102    try:
103        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
104    except NlError as e:
105        raise KsftSkipEx('ring-get not supported by device')
106    if 'tcp-data-split' not in rings:
107        raise KsftSkipEx('tcp-data-split not supported by device')
108
109    ksft_eq('enabled', rings['tcp-data-split'])
110
111def set_hds_disable(cfg, netnl) -> None:
112    _defer_reset_hds(cfg, netnl)
113    try:
114        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'tcp-data-split': 'disabled'})
115    except NlError as e:
116        if e.error == errno.EINVAL:
117            raise KsftSkipEx("disabling of HDS not supported by the device")
118        elif e.error == errno.EOPNOTSUPP:
119            raise KsftSkipEx("ring-set not supported by the device")
120    try:
121        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
122    except NlError as e:
123        raise KsftSkipEx('ring-get not supported by device')
124    if 'tcp-data-split' not in rings:
125        raise KsftSkipEx('tcp-data-split not supported by device')
126
127    ksft_eq('disabled', rings['tcp-data-split'])
128
129def set_hds_thresh_zero(cfg, netnl) -> None:
130    _defer_reset_hds(cfg, netnl)
131    try:
132        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': 0})
133    except NlError as e:
134        if e.error == errno.EINVAL:
135            raise KsftSkipEx("hds-thresh-set not supported by the device")
136        elif e.error == errno.EOPNOTSUPP:
137            raise KsftSkipEx("ring-set not supported by the device")
138    try:
139        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
140    except NlError as e:
141        raise KsftSkipEx('ring-get not supported by device')
142    if 'hds-thresh' not in rings:
143        raise KsftSkipEx('hds-thresh not supported by device')
144
145    ksft_eq(0, rings['hds-thresh'])
146
147def set_hds_thresh_random(cfg, netnl) -> None:
148    _defer_reset_hds(cfg, netnl)
149    try:
150        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
151    except NlError as e:
152        raise KsftSkipEx('ring-get not supported by device')
153    if 'hds-thresh' not in rings:
154        raise KsftSkipEx('hds-thresh not supported by device')
155    if 'hds-thresh-max' not in rings:
156        raise KsftSkipEx('hds-thresh-max not defined by device')
157
158    if rings['hds-thresh-max'] < 2:
159        raise KsftSkipEx('hds-thresh-max is too small')
160    elif rings['hds-thresh-max'] == 2:
161        hds_thresh = 1
162    else:
163        while True:
164            hds_thresh = random.randint(1, rings['hds-thresh-max'] - 1)
165            if hds_thresh != rings['hds-thresh']:
166                break
167
168    try:
169        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': hds_thresh})
170    except NlError as e:
171        if e.error == errno.EINVAL:
172            raise KsftSkipEx("hds-thresh-set not supported by the device")
173        elif e.error == errno.EOPNOTSUPP:
174            raise KsftSkipEx("ring-set not supported by the device")
175    rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
176    ksft_eq(hds_thresh, rings['hds-thresh'])
177
178def set_hds_thresh_max(cfg, netnl) -> None:
179    _defer_reset_hds(cfg, netnl)
180    try:
181        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
182    except NlError as e:
183        raise KsftSkipEx('ring-get not supported by device')
184    if 'hds-thresh' not in rings:
185        raise KsftSkipEx('hds-thresh not supported by device')
186    try:
187        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': rings['hds-thresh-max']})
188    except NlError as e:
189        if e.error == errno.EINVAL:
190            raise KsftSkipEx("hds-thresh-set not supported by the device")
191        elif e.error == errno.EOPNOTSUPP:
192            raise KsftSkipEx("ring-set not supported by the device")
193    rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
194    ksft_eq(rings['hds-thresh'], rings['hds-thresh-max'])
195
196def set_hds_thresh_gt(cfg, netnl) -> None:
197    _defer_reset_hds(cfg, netnl)
198    try:
199        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
200    except NlError as e:
201        raise KsftSkipEx('ring-get not supported by device')
202    if 'hds-thresh' not in rings:
203        raise KsftSkipEx('hds-thresh not supported by device')
204    if 'hds-thresh-max' not in rings:
205        raise KsftSkipEx('hds-thresh-max not defined by device')
206    hds_gt = rings['hds-thresh-max'] + 1
207    with ksft_raises(NlError) as e:
208        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': hds_gt})
209    ksft_eq(e.exception.nl_msg.error, -errno.EINVAL)
210
211
212def set_xdp(cfg, netnl) -> None:
213    """
214    Enable single-buffer XDP on the device.
215    When HDS is in "auto" / UNKNOWN mode, XDP installation should work.
216    """
217    mode = _get_hds_mode(cfg, netnl)
218    if mode == 'enabled':
219        _defer_reset_hds(cfg, netnl)
220        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
221                         'tcp-data-split': 'unknown'})
222
223    _xdp_onoff(cfg)
224
225
226def enabled_set_xdp(cfg, netnl) -> None:
227    """
228    Enable single-buffer XDP on the device.
229    When HDS is in "enabled" mode, XDP installation should not work.
230    """
231    _get_hds_mode(cfg, netnl)
232    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
233                     'tcp-data-split': 'enabled'})
234
235    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
236                            'tcp-data-split': 'unknown'})
237
238    with ksft_raises(CmdExitFailure) as e:
239        _xdp_onoff(cfg)
240
241
242def set_xdp(cfg, netnl) -> None:
243    """
244    Enable single-buffer XDP on the device.
245    When HDS is in "auto" / UNKNOWN mode, XDP installation should work.
246    """
247    mode = _get_hds_mode(cfg, netnl)
248    if mode == 'enabled':
249        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
250                         'tcp-data-split': 'unknown'})
251
252    _xdp_onoff(cfg)
253
254
255def enabled_set_xdp(cfg, netnl) -> None:
256    """
257    Enable single-buffer XDP on the device.
258    When HDS is in "enabled" mode, XDP installation should not work.
259    """
260    _get_hds_mode(cfg, netnl)  # Trigger skip if not supported
261
262    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
263                     'tcp-data-split': 'enabled'})
264    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
265                            'tcp-data-split': 'unknown'})
266
267    with ksft_raises(CmdExitFailure) as e:
268        _xdp_onoff(cfg)
269
270
271def ioctl(cfg, netnl) -> None:
272    mode1 = _get_hds_mode(cfg, netnl)
273    _ioctl_ringparam_modify(cfg, netnl)
274    mode2 = _get_hds_mode(cfg, netnl)
275
276    ksft_eq(mode1, mode2)
277
278
279def ioctl_set_xdp(cfg, netnl) -> None:
280    """
281    Like set_xdp(), but we perturb the settings via the legacy ioctl.
282    """
283    mode = _get_hds_mode(cfg, netnl)
284    if mode == 'enabled':
285        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
286                         'tcp-data-split': 'unknown'})
287
288    _ioctl_ringparam_modify(cfg, netnl)
289
290    _xdp_onoff(cfg)
291
292
293def ioctl_enabled_set_xdp(cfg, netnl) -> None:
294    """
295    Enable single-buffer XDP on the device.
296    When HDS is in "enabled" mode, XDP installation should not work.
297    """
298    _get_hds_mode(cfg, netnl)  # Trigger skip if not supported
299
300    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
301                     'tcp-data-split': 'enabled'})
302    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
303                            'tcp-data-split': 'unknown'})
304
305    with ksft_raises(CmdExitFailure) as e:
306        _xdp_onoff(cfg)
307
308
309def main() -> None:
310    with NetDrvEnv(__file__, queue_count=3) as cfg:
311        ksft_run([get_hds,
312                  get_hds_thresh,
313                  set_hds_disable,
314                  set_hds_enable,
315                  set_hds_thresh_random,
316                  set_hds_thresh_zero,
317                  set_hds_thresh_max,
318                  set_hds_thresh_gt,
319                  set_xdp,
320                  enabled_set_xdp,
321                  ioctl,
322                  ioctl_set_xdp,
323                  ioctl_enabled_set_xdp],
324                 args=(cfg, EthtoolFamily()))
325    ksft_exit()
326
327if __name__ == "__main__":
328    main()
329