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