Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests: drv-net: hw: convert the Toeplitz test to Python

Rewrite the existing toeplitz.sh test in Python. The conversion
is a lot less exact than the GRO one. We use Netlink APIs to
get the device RSS and IRQ information. We expect that the device
has neither RPS nor RFS configured, and set RPS up as part of
the test.

Reviewed-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20251120021024.2944527-11-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+215 -229
+1 -2
tools/testing/selftests/drivers/net/hw/Makefile
··· 24 24 rss_ctx.py \ 25 25 rss_flow_label.py \ 26 26 rss_input_xfrm.py \ 27 - toeplitz.sh \ 27 + toeplitz.py \ 28 28 tso.py \ 29 29 xsk_reconfig.py \ 30 30 # 31 31 32 32 TEST_FILES := \ 33 33 ethtool_lib.sh \ 34 - toeplitz_client.sh \ 35 34 # 36 35 37 36 TEST_INCLUDES := \
+5
tools/testing/selftests/drivers/net/hw/toeplitz.c
··· 53 53 #include <unistd.h> 54 54 55 55 #include "../../../kselftest.h" 56 + #include "../../../net/lib/ksft.h" 56 57 57 58 #define TOEPLITZ_KEY_MIN_LEN 40 58 59 #define TOEPLITZ_KEY_MAX_LEN 60 ··· 577 576 fd_sink = setup_sink(); 578 577 579 578 setup_rings(); 579 + 580 + /* Signal to test framework that we're ready to receive */ 581 + ksft_ready(); 582 + 580 583 process_rings(); 581 584 cleanup_rings(); 582 585
+209
tools/testing/selftests/drivers/net/hw/toeplitz.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + """ 5 + Toeplitz Rx hashing test: 6 + - rxhash (the hash value calculation itself); 7 + - RSS mapping from rxhash to rx queue; 8 + - RPS mapping from rxhash to cpu. 9 + """ 10 + 11 + import glob 12 + import os 13 + import socket 14 + from lib.py import ksft_run, ksft_exit, ksft_pr 15 + from lib.py import NetDrvEpEnv, EthtoolFamily, NetdevFamily 16 + from lib.py import cmd, bkg, rand_port, defer 17 + from lib.py import ksft_in 18 + from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx, KsftFailEx 19 + 20 + 21 + def _check_rps_and_rfs_not_configured(cfg): 22 + """Verify that RPS is not already configured.""" 23 + 24 + for rps_file in glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*/rps_cpus"): 25 + with open(rps_file, "r", encoding="utf-8") as fp: 26 + val = fp.read().strip() 27 + if set(val) - {"0", ","}: 28 + raise KsftSkipEx(f"RPS already configured on {rps_file}: {val}") 29 + 30 + rfs_file = "/proc/sys/net/core/rps_sock_flow_entries" 31 + with open(rfs_file, "r", encoding="utf-8") as fp: 32 + val = fp.read().strip() 33 + if val != "0": 34 + raise KsftSkipEx(f"RFS already configured {rfs_file}: {val}") 35 + 36 + 37 + def _get_rss_key(cfg): 38 + """ 39 + Read the RSS key from the device. 40 + Return a string in the traditional %02x:%02x:%02x:.. format. 41 + """ 42 + 43 + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) 44 + return ':'.join(f'{b:02x}' for b in rss["hkey"]) 45 + 46 + 47 + def _get_cpu_for_irq(irq): 48 + with open(f"/proc/irq/{irq}/smp_affinity_list", "r", 49 + encoding="utf-8") as fp: 50 + data = fp.read().strip() 51 + if "," in data or "-" in data: 52 + raise KsftFailEx(f"IRQ{irq} is not mapped to a single core: {data}") 53 + return int(data) 54 + 55 + 56 + def _get_irq_cpus(cfg): 57 + """ 58 + Read the list of IRQs for the device Rx queues. 59 + """ 60 + queues = cfg.netnl.queue_get({"ifindex": cfg.ifindex}, dump=True) 61 + napis = cfg.netnl.napi_get({"ifindex": cfg.ifindex}, dump=True) 62 + 63 + # Remap into ID-based dicts 64 + napis = {n["id"]: n for n in napis} 65 + queues = {f"{q['type']}{q['id']}": q for q in queues} 66 + 67 + cpus = [] 68 + for rx in range(9999): 69 + name = f"rx{rx}" 70 + if name not in queues: 71 + break 72 + cpus.append(_get_cpu_for_irq(napis[queues[name]["napi-id"]]["irq"])) 73 + 74 + return cpus 75 + 76 + 77 + def _get_unused_cpus(cfg, count=2): 78 + """ 79 + Get CPUs that are not used by Rx queues. 80 + Returns a list of at least 'count' CPU numbers. 81 + """ 82 + 83 + # Get CPUs used by Rx queues 84 + rx_cpus = set(_get_irq_cpus(cfg)) 85 + 86 + # Get total number of CPUs 87 + num_cpus = os.cpu_count() 88 + 89 + # Find unused CPUs 90 + unused_cpus = [cpu for cpu in range(num_cpus) if cpu not in rx_cpus] 91 + 92 + if len(unused_cpus) < count: 93 + raise KsftSkipEx(f"Need at {count} CPUs not used by Rx queues, found {len(unused_cpus)}") 94 + 95 + return unused_cpus[:count] 96 + 97 + 98 + def _configure_rps(cfg, rps_cpus): 99 + """Configure RPS for all Rx queues.""" 100 + 101 + mask = 0 102 + for cpu in rps_cpus: 103 + mask |= (1 << cpu) 104 + mask = hex(mask)[2:] 105 + 106 + # Set RPS bitmap for all rx queues 107 + for rps_file in glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*/rps_cpus"): 108 + with open(rps_file, "w", encoding="utf-8") as fp: 109 + fp.write(mask) 110 + 111 + return mask 112 + 113 + 114 + def _send_traffic(cfg, proto_flag, ipver, port): 115 + """Send 20 packets of requested type.""" 116 + 117 + # Determine protocol and IP version for socat 118 + if proto_flag == "-u": 119 + proto = "UDP" 120 + else: 121 + proto = "TCP" 122 + 123 + baddr = f"[{cfg.addr_v['6']}]" if ipver == "6" else cfg.addr_v["4"] 124 + 125 + # Run socat in a loop to send traffic periodically 126 + # Use sh -c with a loop similar to toeplitz_client.sh 127 + socat_cmd = f""" 128 + for i in `seq 20`; do 129 + echo "msg $i" | socat -{ipver} -t 0.1 - {proto}:{baddr}:{port}; 130 + sleep 0.001; 131 + done 132 + """ 133 + 134 + cmd(socat_cmd, shell=True, host=cfg.remote) 135 + 136 + 137 + def _test_variants(): 138 + for grp in ["", "rss", "rps"]: 139 + for l4 in ["tcp", "udp"]: 140 + for l3 in ["4", "6"]: 141 + name = f"{l4}_ipv{l3}" 142 + if grp: 143 + name = f"{grp}_{name}" 144 + yield KsftNamedVariant(name, "-" + l4[0], l3, grp) 145 + 146 + 147 + @ksft_variants(_test_variants()) 148 + def test(cfg, proto_flag, ipver, grp): 149 + """Run a single toeplitz test.""" 150 + 151 + cfg.require_ipver(ipver) 152 + 153 + # Check that rxhash is enabled 154 + ksft_in("receive-hashing: on", cmd(f"ethtool -k {cfg.ifname}").stdout) 155 + 156 + port = rand_port(socket.SOCK_DGRAM) 157 + key = _get_rss_key(cfg) 158 + 159 + toeplitz_path = cfg.test_dir / "toeplitz" 160 + rx_cmd = [ 161 + str(toeplitz_path), 162 + "-" + ipver, 163 + proto_flag, 164 + "-d", str(port), 165 + "-i", cfg.ifname, 166 + "-k", key, 167 + "-T", "1000", 168 + "-s", 169 + "-v" 170 + ] 171 + 172 + if grp: 173 + _check_rps_and_rfs_not_configured(cfg) 174 + if grp == "rss": 175 + irq_cpus = ",".join([str(x) for x in _get_irq_cpus(cfg)]) 176 + rx_cmd += ["-C", irq_cpus] 177 + ksft_pr(f"RSS using CPUs: {irq_cpus}") 178 + elif grp == "rps": 179 + # Get CPUs not used by Rx queues and configure them for RPS 180 + rps_cpus = _get_unused_cpus(cfg, count=2) 181 + rps_mask = _configure_rps(cfg, rps_cpus) 182 + defer(_configure_rps, cfg, []) 183 + rx_cmd += ["-r", rps_mask] 184 + ksft_pr(f"RPS using CPUs: {rps_cpus}, mask: {rps_mask}") 185 + 186 + # Run rx in background, it will exit once it has seen enough packets 187 + with bkg(" ".join(rx_cmd), ksft_ready=True, exit_wait=True) as rx_proc: 188 + while rx_proc.proc.poll() is None: 189 + _send_traffic(cfg, proto_flag, ipver, port) 190 + 191 + # Check rx result 192 + ksft_pr("Receiver output:") 193 + ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# ')) 194 + if rx_proc.stderr: 195 + ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# ')) 196 + 197 + 198 + def main() -> None: 199 + """Ksft boilerplate main.""" 200 + 201 + with NetDrvEpEnv(__file__) as cfg: 202 + cfg.ethnl = EthtoolFamily() 203 + cfg.netnl = NetdevFamily() 204 + ksft_run(cases=[test], args=(cfg,)) 205 + ksft_exit() 206 + 207 + 208 + if __name__ == "__main__": 209 + main()
-199
tools/testing/selftests/drivers/net/hw/toeplitz.sh
··· 1 - #!/bin/bash 2 - # SPDX-License-Identifier: GPL-2.0 3 - # 4 - # extended toeplitz test: test rxhash plus, optionally, either (1) rss mapping 5 - # from rxhash to rx queue ('-rss') or (2) rps mapping from rxhash to cpu 6 - # ('-rps <rps_map>') 7 - # 8 - # irq-pattern-prefix can be derived from /sys/kernel/irq/*/action, 9 - # which is a driver-specific encoding. 10 - # 11 - # invoke as ./toeplitz.sh (-i <iface>) -u|-t -4|-6 \ 12 - # [(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)] 13 - 14 - source $(dirname $0)/../../../net/lib/setup_loopback.sh 15 - readonly SERVER_IP4="192.168.1.200/24" 16 - readonly SERVER_IP6="fda8::1/64" 17 - readonly SERVER_MAC="aa:00:00:00:00:02" 18 - 19 - readonly CLIENT_IP4="192.168.1.100/24" 20 - readonly CLIENT_IP6="fda8::2/64" 21 - readonly CLIENT_MAC="aa:00:00:00:00:01" 22 - 23 - PORT=8000 24 - KEY="$(</proc/sys/net/core/netdev_rss_key)" 25 - TEST_RSS=false 26 - RPS_MAP="" 27 - PROTO_FLAG="" 28 - IP_FLAG="" 29 - DEV="eth0" 30 - 31 - # Return the number of rxqs among which RSS is configured to spread packets. 32 - # This is determined by reading the RSS indirection table using ethtool. 33 - get_rss_cfg_num_rxqs() { 34 - echo $(ethtool -x "${DEV}" | 35 - grep -E [[:space:]]+[0-9]+:[[:space:]]+ | 36 - cut -d: -f2- | 37 - awk '{$1=$1};1' | 38 - tr ' ' '\n' | 39 - sort -u | 40 - wc -l) 41 - } 42 - 43 - # Return a list of the receive irq handler cpus. 44 - # The list is ordered by the irqs, so first rxq-0 cpu, then rxq-1 cpu, etc. 45 - # Reads /sys/kernel/irq/ in order, so algorithm depends on 46 - # irq_{rxq-0} < irq_{rxq-1}, etc. 47 - get_rx_irq_cpus() { 48 - CPUS="" 49 - # sort so that irq 2 is read before irq 10 50 - SORTED_IRQS=$(for i in /sys/kernel/irq/*; do echo $i; done | sort -V) 51 - # Consider only as many queues as RSS actually uses. We assume that 52 - # if RSS_CFG_NUM_RXQS=N, then RSS uses rxqs 0-(N-1). 53 - RSS_CFG_NUM_RXQS=$(get_rss_cfg_num_rxqs) 54 - RXQ_COUNT=0 55 - 56 - for i in ${SORTED_IRQS} 57 - do 58 - [[ "${RXQ_COUNT}" -lt "${RSS_CFG_NUM_RXQS}" ]] || break 59 - # lookup relevant IRQs by action name 60 - [[ -e "$i/actions" ]] || continue 61 - cat "$i/actions" | grep -q "${IRQ_PATTERN}" || continue 62 - irqname=$(<"$i/actions") 63 - 64 - # does the IRQ get called 65 - irqcount=$(cat "$i/per_cpu_count" | tr -d '0,') 66 - [[ -n "${irqcount}" ]] || continue 67 - 68 - # lookup CPU 69 - irq=$(basename "$i") 70 - cpu=$(cat "/proc/irq/$irq/smp_affinity_list") 71 - 72 - if [[ -z "${CPUS}" ]]; then 73 - CPUS="${cpu}" 74 - else 75 - CPUS="${CPUS},${cpu}" 76 - fi 77 - RXQ_COUNT=$((RXQ_COUNT+1)) 78 - done 79 - 80 - echo "${CPUS}" 81 - } 82 - 83 - get_disable_rfs_cmd() { 84 - echo "echo 0 > /proc/sys/net/core/rps_sock_flow_entries;" 85 - } 86 - 87 - get_set_rps_bitmaps_cmd() { 88 - CMD="" 89 - for i in /sys/class/net/${DEV}/queues/rx-*/rps_cpus 90 - do 91 - CMD="${CMD} echo $1 > ${i};" 92 - done 93 - 94 - echo "${CMD}" 95 - } 96 - 97 - get_disable_rps_cmd() { 98 - echo "$(get_set_rps_bitmaps_cmd 0)" 99 - } 100 - 101 - die() { 102 - echo "$1" 103 - exit 1 104 - } 105 - 106 - check_nic_rxhash_enabled() { 107 - local -r pattern="receive-hashing:\ on" 108 - 109 - ethtool -k "${DEV}" | grep -q "${pattern}" || die "rxhash must be enabled" 110 - } 111 - 112 - parse_opts() { 113 - local prog=$0 114 - shift 1 115 - 116 - while [[ "$1" =~ "-" ]]; do 117 - if [[ "$1" = "-irq_prefix" ]]; then 118 - shift 119 - IRQ_PATTERN="^$1-[0-9]*$" 120 - elif [[ "$1" = "-u" || "$1" = "-t" ]]; then 121 - PROTO_FLAG="$1" 122 - elif [[ "$1" = "-4" ]]; then 123 - IP_FLAG="$1" 124 - SERVER_IP="${SERVER_IP4}" 125 - CLIENT_IP="${CLIENT_IP4}" 126 - elif [[ "$1" = "-6" ]]; then 127 - IP_FLAG="$1" 128 - SERVER_IP="${SERVER_IP6}" 129 - CLIENT_IP="${CLIENT_IP6}" 130 - elif [[ "$1" = "-rss" ]]; then 131 - TEST_RSS=true 132 - elif [[ "$1" = "-rps" ]]; then 133 - shift 134 - RPS_MAP="$1" 135 - elif [[ "$1" = "-i" ]]; then 136 - shift 137 - DEV="$1" 138 - else 139 - die "Usage: ${prog} (-i <iface>) -u|-t -4|-6 \ 140 - [(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)]" 141 - fi 142 - shift 143 - done 144 - } 145 - 146 - setup() { 147 - setup_loopback_environment "${DEV}" 148 - 149 - # Set up server_ns namespace and client_ns namespace 150 - setup_macvlan_ns "${DEV}" $server_ns server \ 151 - "${SERVER_MAC}" "${SERVER_IP}" 152 - setup_macvlan_ns "${DEV}" $client_ns client \ 153 - "${CLIENT_MAC}" "${CLIENT_IP}" 154 - } 155 - 156 - cleanup() { 157 - cleanup_macvlan_ns $server_ns server $client_ns client 158 - cleanup_loopback "${DEV}" 159 - } 160 - 161 - parse_opts $0 $@ 162 - 163 - setup 164 - trap cleanup EXIT 165 - 166 - check_nic_rxhash_enabled 167 - 168 - # Actual test starts here 169 - if [[ "${TEST_RSS}" = true ]]; then 170 - # RPS/RFS must be disabled because they move packets between cpus, 171 - # which breaks the PACKET_FANOUT_CPU identification of RSS decisions. 172 - eval "$(get_disable_rfs_cmd) $(get_disable_rps_cmd)" \ 173 - ip netns exec $server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \ 174 - -d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \ 175 - -C "$(get_rx_irq_cpus)" -s -v & 176 - elif [[ ! -z "${RPS_MAP}" ]]; then 177 - eval "$(get_disable_rfs_cmd) $(get_set_rps_bitmaps_cmd ${RPS_MAP})" \ 178 - ip netns exec $server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \ 179 - -d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \ 180 - -r "0x${RPS_MAP}" -s -v & 181 - else 182 - ip netns exec $server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \ 183 - -d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 -s -v & 184 - fi 185 - 186 - server_pid=$! 187 - 188 - ip netns exec $client_ns ./toeplitz_client.sh "${PROTO_FLAG}" \ 189 - "${IP_FLAG}" "${SERVER_IP%%/*}" "${PORT}" & 190 - 191 - client_pid=$! 192 - 193 - wait "${server_pid}" 194 - exit_code=$? 195 - kill -9 "${client_pid}" 196 - if [[ "${exit_code}" -eq 0 ]]; then 197 - echo "Test Succeeded!" 198 - fi 199 - exit "${exit_code}"
-28
tools/testing/selftests/drivers/net/hw/toeplitz_client.sh
··· 1 - #!/bin/bash 2 - # SPDX-License-Identifier: GPL-2.0 3 - # 4 - # A simple program for generating traffic for the toeplitz test. 5 - # 6 - # This program sends packets periodically for, conservatively, 20 seconds. The 7 - # intent is for the calling program to kill this program once it is no longer 8 - # needed, rather than waiting for the 20 second expiration. 9 - 10 - send_traffic() { 11 - expiration=$((SECONDS+20)) 12 - while [[ "${SECONDS}" -lt "${expiration}" ]] 13 - do 14 - if [[ "${PROTO}" == "-u" ]]; then 15 - echo "msg $i" | nc "${IPVER}" -u -w 0 "${ADDR}" "${PORT}" 16 - else 17 - echo "msg $i" | nc "${IPVER}" -w 0 "${ADDR}" "${PORT}" 18 - fi 19 - sleep 0.001 20 - done 21 - } 22 - 23 - PROTO=$1 24 - IPVER=$2 25 - ADDR=$3 26 - PORT=$4 27 - 28 - send_traffic