1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4import errno
5import os
6from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises, KsftSkipEx
7from lib.py import CmdExitFailure, EthtoolFamily, NlError
8from lib.py import NetDrvEnv
9from lib.py import defer, ethtool, ip
10
11
12def _get_hds_mode(cfg, netnl) -> str:
13    try:
14        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
15    except NlError as e:
16        raise KsftSkipEx('ring-get not supported by device')
17    if 'tcp-data-split' not in rings:
18        raise KsftSkipEx('tcp-data-split not supported by device')
19    return rings['tcp-data-split']
20
21
22def _xdp_onoff(cfg):
23    test_dir = os.path.dirname(os.path.realpath(__file__))
24    prog = test_dir + "/../../net/lib/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
62def set_hds_enable(cfg, netnl) -> None:
63    try:
64        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'tcp-data-split': 'enabled'})
65    except NlError as e:
66        if e.error == errno.EINVAL:
67            raise KsftSkipEx("disabling of HDS not supported by the device")
68        elif e.error == errno.EOPNOTSUPP:
69            raise KsftSkipEx("ring-set not supported by the device")
70    try:
71        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
72    except NlError as e:
73        raise KsftSkipEx('ring-get not supported by device')
74    if 'tcp-data-split' not in rings:
75        raise KsftSkipEx('tcp-data-split not supported by device')
76
77    ksft_eq('enabled', rings['tcp-data-split'])
78
79def set_hds_disable(cfg, netnl) -> None:
80    try:
81        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'tcp-data-split': 'disabled'})
82    except NlError as e:
83        if e.error == errno.EINVAL:
84            raise KsftSkipEx("disabling of HDS not supported by the device")
85        elif e.error == errno.EOPNOTSUPP:
86            raise KsftSkipEx("ring-set not supported by the device")
87    try:
88        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
89    except NlError as e:
90        raise KsftSkipEx('ring-get not supported by device')
91    if 'tcp-data-split' not in rings:
92        raise KsftSkipEx('tcp-data-split not supported by device')
93
94    ksft_eq('disabled', rings['tcp-data-split'])
95
96def set_hds_thresh_zero(cfg, netnl) -> None:
97    try:
98        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': 0})
99    except NlError as e:
100        if e.error == errno.EINVAL:
101            raise KsftSkipEx("hds-thresh-set not supported by the device")
102        elif e.error == errno.EOPNOTSUPP:
103            raise KsftSkipEx("ring-set not supported by the device")
104    try:
105        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
106    except NlError as e:
107        raise KsftSkipEx('ring-get not supported by device')
108    if 'hds-thresh' not in rings:
109        raise KsftSkipEx('hds-thresh not supported by device')
110
111    ksft_eq(0, rings['hds-thresh'])
112
113def set_hds_thresh_max(cfg, netnl) -> None:
114    try:
115        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
116    except NlError as e:
117        raise KsftSkipEx('ring-get not supported by device')
118    if 'hds-thresh' not in rings:
119        raise KsftSkipEx('hds-thresh not supported by device')
120    try:
121        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': rings['hds-thresh-max']})
122    except NlError as e:
123        if e.error == errno.EINVAL:
124            raise KsftSkipEx("hds-thresh-set not supported by the device")
125        elif e.error == errno.EOPNOTSUPP:
126            raise KsftSkipEx("ring-set not supported by the device")
127    rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
128    ksft_eq(rings['hds-thresh'], rings['hds-thresh-max'])
129
130def set_hds_thresh_gt(cfg, netnl) -> None:
131    try:
132        rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}})
133    except NlError as e:
134        raise KsftSkipEx('ring-get not supported by device')
135    if 'hds-thresh' not in rings:
136        raise KsftSkipEx('hds-thresh not supported by device')
137    if 'hds-thresh-max' not in rings:
138        raise KsftSkipEx('hds-thresh-max not defined by device')
139    hds_gt = rings['hds-thresh-max'] + 1
140    with ksft_raises(NlError) as e:
141        netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': hds_gt})
142    ksft_eq(e.exception.nl_msg.error, -errno.EINVAL)
143
144
145def set_xdp(cfg, netnl) -> None:
146    """
147    Enable single-buffer XDP on the device.
148    When HDS is in "auto" / UNKNOWN mode, XDP installation should work.
149    """
150    mode = _get_hds_mode(cfg, netnl)
151    if mode == 'enabled':
152        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
153                         'tcp-data-split': 'unknown'})
154
155    _xdp_onoff(cfg)
156
157
158def enabled_set_xdp(cfg, netnl) -> None:
159    """
160    Enable single-buffer XDP on the device.
161    When HDS is in "enabled" mode, XDP installation should not work.
162    """
163    _get_hds_mode(cfg, netnl)
164    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
165                     'tcp-data-split': 'enabled'})
166
167    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
168                            'tcp-data-split': 'unknown'})
169
170    with ksft_raises(CmdExitFailure) as e:
171        _xdp_onoff(cfg)
172
173
174def set_xdp(cfg, netnl) -> None:
175    """
176    Enable single-buffer XDP on the device.
177    When HDS is in "auto" / UNKNOWN mode, XDP installation should work.
178    """
179    mode = _get_hds_mode(cfg, netnl)
180    if mode == 'enabled':
181        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
182                         'tcp-data-split': 'unknown'})
183
184    _xdp_onoff(cfg)
185
186
187def enabled_set_xdp(cfg, netnl) -> None:
188    """
189    Enable single-buffer XDP on the device.
190    When HDS is in "enabled" mode, XDP installation should not work.
191    """
192    _get_hds_mode(cfg, netnl)  # Trigger skip if not supported
193
194    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
195                     'tcp-data-split': 'enabled'})
196    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
197                            'tcp-data-split': 'unknown'})
198
199    with ksft_raises(CmdExitFailure) as e:
200        _xdp_onoff(cfg)
201
202
203def ioctl(cfg, netnl) -> None:
204    mode1 = _get_hds_mode(cfg, netnl)
205    _ioctl_ringparam_modify(cfg, netnl)
206    mode2 = _get_hds_mode(cfg, netnl)
207
208    ksft_eq(mode1, mode2)
209
210
211def ioctl_set_xdp(cfg, netnl) -> None:
212    """
213    Like set_xdp(), but we perturb the settings via the legacy ioctl.
214    """
215    mode = _get_hds_mode(cfg, netnl)
216    if mode == 'enabled':
217        netnl.rings_set({'header': {'dev-index': cfg.ifindex},
218                         'tcp-data-split': 'unknown'})
219
220    _ioctl_ringparam_modify(cfg, netnl)
221
222    _xdp_onoff(cfg)
223
224
225def ioctl_enabled_set_xdp(cfg, netnl) -> None:
226    """
227    Enable single-buffer XDP on the device.
228    When HDS is in "enabled" mode, XDP installation should not work.
229    """
230    _get_hds_mode(cfg, netnl)  # Trigger skip if not supported
231
232    netnl.rings_set({'header': {'dev-index': cfg.ifindex},
233                     'tcp-data-split': 'enabled'})
234    defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex},
235                            'tcp-data-split': 'unknown'})
236
237    with ksft_raises(CmdExitFailure) as e:
238        _xdp_onoff(cfg)
239
240
241def main() -> None:
242    with NetDrvEnv(__file__, queue_count=3) as cfg:
243        ksft_run([get_hds,
244                  get_hds_thresh,
245                  set_hds_disable,
246                  set_hds_enable,
247                  set_hds_thresh_zero,
248                  set_hds_thresh_max,
249                  set_hds_thresh_gt,
250                  set_xdp,
251                  enabled_set_xdp,
252                  ioctl,
253                  ioctl_set_xdp,
254                  ioctl_enabled_set_xdp],
255                 args=(cfg, EthtoolFamily()))
256    ksft_exit()
257
258if __name__ == "__main__":
259    main()
260