| 1 | |
| 2 | |
| 3 |
|
| 4 | import errno
|
| 5 | import os
|
| 6 | import random
|
| 7 | from typing import Union
|
| 8 | from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises, KsftSkipEx
|
| 9 | from lib.py import CmdExitFailure, EthtoolFamily, NlError
|
| 10 | from lib.py import NetDrvEnv
|
| 11 | from lib.py import defer, ethtool, ip
|
| 12 |
|
| 13 |
|
| 14 | def _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 |
|
| 24 | def _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 |
|
| 31 | def _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 |
|
| 51 | def get_hds(cfg, netnl) -> None:
|
| 52 | _get_hds_mode(cfg, netnl)
|
| 53 |
|
| 54 |
|
| 55 | def 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 |
|
| 64 | def _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 | |
| 70 | |
| 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 | |
| 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 |
|
| 85 | def _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 |
|
| 94 | def 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 |
|
| 112 | def 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 |
|
| 130 | def 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 |
|
| 148 | def 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 |
|
| 179 | def 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 |
|
| 197 | def 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 |
|
| 213 | def 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 |
|
| 227 | def 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 |
|
| 243 | def 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 |
|
| 256 | def 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) |
| 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 |
|
| 272 | def 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 |
|
| 280 | def 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 |
|
| 294 | def 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) |
| 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 |
|
| 310 | def 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 |
|
| 328 | if __name__ == "__main__":
|
| 329 | main()
|