1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4from lib.py import ksft_disruptive, ksft_exit, ksft_run 5from lib.py import ksft_eq, ksft_not_in, ksft_raises, KsftSkipEx, KsftFailEx 6from lib.py import EthtoolFamily, NetdevFamily, NlError 7from lib.py import NetDrvEnv 8from lib.py import bkg, cmd, defer, ip 9import errno 10import glob 11import os 12import socket 13import struct 14 15def sys_get_queues(ifname, qtype='rx') -> int: 16 folders = glob.glob(f'/sys/class/net/{ifname}/queues/{qtype}-*') 17 return len(folders) 18 19 20def nl_get_queues(cfg, nl, qtype='rx'): 21 queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 22 if queues: 23 return len([q for q in queues if q['type'] == qtype]) 24 return None 25 26 27def check_xsk(cfg, nl, xdp_queue_id=0) -> None: 28 # Probe for support 29 xdp = cmd(f'{cfg.test_dir / "xdp_helper"} - -', fail=False) 30 if xdp.ret == 255: 31 raise KsftSkipEx('AF_XDP unsupported') 32 elif xdp.ret > 0: 33 raise KsftFailEx('unable to create AF_XDP socket') 34 35 with bkg(f'{cfg.test_dir / "xdp_helper"} {cfg.ifindex} {xdp_queue_id}', 36 ksft_wait=3): 37 38 rx = tx = False 39 40 queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 41 if not queues: 42 raise KsftSkipEx("Netlink reports no queues") 43 44 for q in queues: 45 if q['id'] == 0: 46 if q['type'] == 'rx': 47 rx = True 48 if q['type'] == 'tx': 49 tx = True 50 51 ksft_eq(q.get('xsk', None), {}, 52 comment="xsk attr on queue we configured") 53 else: 54 ksft_not_in('xsk', q, 55 comment="xsk attr on queue we didn't configure") 56 57 ksft_eq(rx, True) 58 ksft_eq(tx, True) 59 60 61def get_queues(cfg, nl) -> None: 62 snl = NetdevFamily(recv_size=4096) 63 64 for qtype in ['rx', 'tx']: 65 queues = nl_get_queues(cfg, snl, qtype) 66 if not queues: 67 raise KsftSkipEx('queue-get not supported by device') 68 69 expected = sys_get_queues(cfg.dev['ifname'], qtype) 70 ksft_eq(queues, expected) 71 72 73def addremove_queues(cfg, nl) -> None: 74 queues = nl_get_queues(cfg, nl) 75 if not queues: 76 raise KsftSkipEx('queue-get not supported by device') 77 78 curr_queues = sys_get_queues(cfg.dev['ifname']) 79 if curr_queues == 1: 80 raise KsftSkipEx('cannot decrement queue: already at 1') 81 82 netnl = EthtoolFamily() 83 channels = netnl.channels_get({'header': {'dev-index': cfg.ifindex}}) 84 rx_type = 'rx' 85 if channels.get('combined-count', 0) > 0: 86 rx_type = 'combined' 87 88 expected = curr_queues - 1 89 cmd(f"ethtool -L {cfg.dev['ifname']} {rx_type} {expected}", timeout=10) 90 queues = nl_get_queues(cfg, nl) 91 ksft_eq(queues, expected) 92 93 expected = curr_queues 94 cmd(f"ethtool -L {cfg.dev['ifname']} {rx_type} {expected}", timeout=10) 95 queues = nl_get_queues(cfg, nl) 96 ksft_eq(queues, expected) 97 98 99@ksft_disruptive 100def check_down(cfg, nl) -> None: 101 # Check the NAPI IDs before interface goes down and hides them 102 napis = nl.napi_get({'ifindex': cfg.ifindex}, dump=True) 103 104 ip(f"link set dev {cfg.dev['ifname']} down") 105 defer(ip, f"link set dev {cfg.dev['ifname']} up") 106 107 with ksft_raises(NlError) as cm: 108 nl.queue_get({'ifindex': cfg.ifindex, 'id': 0, 'type': 'rx'}) 109 ksft_eq(cm.exception.nl_msg.error, -errno.ENOENT) 110 111 if napis: 112 with ksft_raises(NlError) as cm: 113 nl.napi_get({'id': napis[0]['id']}) 114 ksft_eq(cm.exception.nl_msg.error, -errno.ENOENT) 115 116 117def main() -> None: 118 with NetDrvEnv(__file__, queue_count=100) as cfg: 119 ksft_run([get_queues, addremove_queues, check_down, check_xsk], 120 args=(cfg, NetdevFamily())) 121 ksft_exit() 122 123 124if __name__ == "__main__": 125 main() 126