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_raises, KsftSkipEx, KsftFailEx 6from lib.py import EthtoolFamily, NetdevFamily, NlError 7from lib.py import NetDrvEnv 8from lib.py import cmd, defer, ip 9import errno 10import glob 11import os 12import socket 13import struct 14import subprocess 15 16def sys_get_queues(ifname, qtype='rx') -> int: 17 folders = glob.glob(f'/sys/class/net/{ifname}/queues/{qtype}-*') 18 return len(folders) 19 20 21def nl_get_queues(cfg, nl, qtype='rx'): 22 queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 23 if queues: 24 return len([q for q in queues if q['type'] == qtype]) 25 return None 26 27def check_xdp(cfg, nl, xdp_queue_id=0) -> None: 28 test_dir = os.path.dirname(os.path.realpath(__file__)) 29 xdp = subprocess.Popen([f"{test_dir}/xdp_helper", f"{cfg.ifindex}", f"{xdp_queue_id}"], 30 stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1, 31 text=True) 32 defer(xdp.kill) 33 34 stdout, stderr = xdp.communicate(timeout=10) 35 rx = tx = False 36 37 if xdp.returncode == 255: 38 raise KsftSkipEx('AF_XDP unsupported') 39 elif xdp.returncode > 0: 40 raise KsftFailEx('unable to create AF_XDP socket') 41 42 queues = nl.queue_get({'ifindex': cfg.ifindex}, dump=True) 43 if not queues: 44 raise KsftSkipEx("Netlink reports no queues") 45 46 for q in queues: 47 if q['id'] == 0: 48 if q['type'] == 'rx': 49 rx = True 50 if q['type'] == 'tx': 51 tx = True 52 53 ksft_eq(q['xsk'], {}) 54 else: 55 if 'xsk' in q: 56 _fail("Check failed: xsk attribute set.") 57 58 ksft_eq(rx, True) 59 ksft_eq(tx, True) 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 if channels['combined-count'] == 0: 85 rx_type = 'rx' 86 else: 87 rx_type = 'combined' 88 89 expected = curr_queues - 1 90 cmd(f"ethtool -L {cfg.dev['ifname']} {rx_type} {expected}", timeout=10) 91 queues = nl_get_queues(cfg, nl) 92 ksft_eq(queues, expected) 93 94 expected = curr_queues 95 cmd(f"ethtool -L {cfg.dev['ifname']} {rx_type} {expected}", timeout=10) 96 queues = nl_get_queues(cfg, nl) 97 ksft_eq(queues, expected) 98 99 100@ksft_disruptive 101def check_down(cfg, nl) -> None: 102 # Check the NAPI IDs before interface goes down and hides them 103 napis = nl.napi_get({'ifindex': cfg.ifindex}, dump=True) 104 105 ip(f"link set dev {cfg.dev['ifname']} down") 106 defer(ip, f"link set dev {cfg.dev['ifname']} up") 107 108 with ksft_raises(NlError) as cm: 109 nl.queue_get({'ifindex': cfg.ifindex, 'id': 0, 'type': 'rx'}) 110 ksft_eq(cm.exception.nl_msg.error, -errno.ENOENT) 111 112 if napis: 113 with ksft_raises(NlError) as cm: 114 nl.napi_get({'id': napis[0]['id']}) 115 ksft_eq(cm.exception.nl_msg.error, -errno.ENOENT) 116 117 118def main() -> None: 119 with NetDrvEnv(__file__, queue_count=100) as cfg: 120 ksft_run([get_queues, addremove_queues, check_down, check_xdp], args=(cfg, NetdevFamily())) 121 ksft_exit() 122 123 124if __name__ == "__main__": 125 main() 126