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(cfg.rpath("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.rpath("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 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_xsk], 121 args=(cfg, NetdevFamily())) 122 ksft_exit() 123 124 125if __name__ == "__main__": 126 main() 127