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