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

Merge branch 'selftests-drv-net-convert-gro-and-toeplitz-tests-to-work-for-drivers-in-nipa'

Jakub Kicinski says:

====================
selftests: drv-net: convert GRO and Toeplitz tests to work for drivers in NIPA

Main objective of this series is to convert the gro.sh and toeplitz.sh
tests to be "NIPA-compatible" - meaning make use of the Python env,
which lets us run the tests against either netdevsim or a real device.

The tests seem to have been written with a different flow in mind.
Namely they source different bash "setup" scripts depending on arguments
passed to the test. While I have nothing against the use of bash and
the overall architecture - the existing code needs quite a bit of work
(don't assume MAC/IP addresses, support remote endpoint over SSH).
If I'm the one fixing it, I'd rather convert them to our "simplistic"
Python.

This series rewrites the tests in Python while addressing their
shortcomings. The functionality of running the test over loopback
on a real device is retained but with a different method of invocation
(see the last patch).

Once again we are dealing with a script which run over a variety of
protocols (combination of [ipv4, ipv6, ipip] x [tcp, udp]). The first
4 patches add support for test variants to our scripts. We use the
term "variant" in the same sense as the C kselftest_harness.h -
variant is just a set of static input arguments.

Note that neither GRO nor the Toeplitz test fully passes for me on
any HW I have access to. But this is unrelated to the conversion.
This series is not making any real functional changes to the tests,
it is limited to improving the "test harness" scripts.
====================

Link: https://patch.msgid.link/20251120021024.2944527-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+632 -577
+14 -12
drivers/net/netdevsim/netdev.c
··· 133 133 if (!nsim_ipsec_tx(ns, skb)) 134 134 goto out_drop_any; 135 135 136 - peer_ns = rcu_dereference(ns->peer); 137 - if (!peer_ns) 138 - goto out_drop_any; 136 + /* Check if loopback mode is enabled */ 137 + if (dev->features & NETIF_F_LOOPBACK) { 138 + peer_ns = ns; 139 + peer_dev = dev; 140 + } else { 141 + peer_ns = rcu_dereference(ns->peer); 142 + if (!peer_ns) 143 + goto out_drop_any; 144 + peer_dev = peer_ns->netdev; 145 + } 139 146 140 147 dr = nsim_do_psp(skb, ns, peer_ns, &psp_ext); 141 148 if (dr) 142 149 goto out_drop_free; 143 150 144 - peer_dev = peer_ns->netdev; 145 151 rxq = skb_get_queue_mapping(skb); 146 152 if (rxq >= peer_dev->num_rx_queues) 147 153 rxq = rxq % peer_dev->num_rx_queues; ··· 439 433 } 440 434 441 435 /* skb might be discard at netif_receive_skb, save the len */ 442 - skblen = skb->len; 443 - skb_mark_napi_id(skb, &rq->napi); 444 - ret = netif_receive_skb(skb); 445 - if (ret == NET_RX_SUCCESS) 446 - dev_dstats_rx_add(dev, skblen); 447 - else 448 - dev_dstats_rx_dropped(dev); 436 + dev_dstats_rx_add(dev, skb->len); 437 + napi_gro_receive(&rq->napi, skb); 449 438 } 450 439 451 440 nsim_start_peer_tx_queue(dev, rq); ··· 982 981 NETIF_F_FRAGLIST | 983 982 NETIF_F_HW_CSUM | 984 983 NETIF_F_LRO | 985 - NETIF_F_TSO; 984 + NETIF_F_TSO | 985 + NETIF_F_LOOPBACK; 986 986 dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; 987 987 dev->max_mtu = ETH_MAX_MTU; 988 988 dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
+1
tools/testing/selftests/drivers/net/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 + gro 2 3 napi_id_helper 3 4 psp_responder
+2
tools/testing/selftests/drivers/net/Makefile
··· 6 6 ../../net/lib.sh \ 7 7 8 8 TEST_GEN_FILES := \ 9 + gro \ 9 10 napi_id_helper \ 10 11 # end of TEST_GEN_FILES 11 12 12 13 TEST_PROGS := \ 14 + gro.py \ 13 15 hds.py \ 14 16 napi_id.py \ 15 17 napi_threaded.py \
+164
tools/testing/selftests/drivers/net/gro.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + """ 5 + GRO (Generic Receive Offload) conformance tests. 6 + 7 + Validates that GRO coalescing works correctly by running the gro 8 + binary in different configurations and checking for correct packet 9 + coalescing behavior. 10 + 11 + Test cases: 12 + - data: Data packets with same size/headers and correct seq numbers coalesce 13 + - ack: Pure ACK packets do not coalesce 14 + - flags: Packets with PSH, SYN, URG, RST flags do not coalesce 15 + - tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce 16 + - ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce 17 + - large: Packets larger than GRO_MAX_SIZE don't coalesce 18 + """ 19 + 20 + import os 21 + from lib.py import ksft_run, ksft_exit, ksft_pr 22 + from lib.py import NetDrvEpEnv, KsftXfailEx 23 + from lib.py import cmd, defer, bkg, ip 24 + from lib.py import ksft_variants 25 + 26 + 27 + def _resolve_dmac(cfg, ipver): 28 + """ 29 + Find the destination MAC address remote host should use to send packets 30 + towards the local host. It may be a router / gateway address. 31 + """ 32 + 33 + attr = "dmac" + ipver 34 + # Cache the response across test cases 35 + if hasattr(cfg, attr): 36 + return getattr(cfg, attr) 37 + 38 + route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}", 39 + json=True, host=cfg.remote)[0] 40 + gw = route.get("gateway") 41 + # Local L2 segment, address directly 42 + if not gw: 43 + setattr(cfg, attr, cfg.dev['address']) 44 + return getattr(cfg, attr) 45 + 46 + # ping to make sure neighbor is resolved, 47 + # bind to an interface, for v6 the GW is likely link local 48 + cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote) 49 + 50 + neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}", 51 + json=True, host=cfg.remote)[0] 52 + setattr(cfg, attr, neigh['lladdr']) 53 + return getattr(cfg, attr) 54 + 55 + 56 + def _write_defer_restore(cfg, path, val, defer_undo=False): 57 + with open(path, "r", encoding="utf-8") as fp: 58 + orig_val = fp.read().strip() 59 + if str(val) == orig_val: 60 + return 61 + with open(path, "w", encoding="utf-8") as fp: 62 + fp.write(val) 63 + if defer_undo: 64 + defer(_write_defer_restore, cfg, path, orig_val) 65 + 66 + 67 + def _set_mtu_restore(dev, mtu, host): 68 + if dev['mtu'] < mtu: 69 + ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host) 70 + defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host) 71 + 72 + 73 + def _setup(cfg, test_name): 74 + """ Setup hardware loopback mode for GRO testing. """ 75 + 76 + if not hasattr(cfg, "bin_remote"): 77 + cfg.bin_local = cfg.test_dir / "gro" 78 + cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) 79 + 80 + # "large" test needs at least 4k MTU 81 + if test_name == "large": 82 + _set_mtu_restore(cfg.dev, 4096, None) 83 + _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote) 84 + 85 + flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout" 86 + irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs" 87 + 88 + _write_defer_restore(cfg, flush_path, "200000", defer_undo=True) 89 + _write_defer_restore(cfg, irq_path, "10", defer_undo=True) 90 + 91 + try: 92 + # Disable TSO for local tests 93 + cfg.require_nsim() # will raise KsftXfailEx if not running on nsim 94 + 95 + cmd(f"ethtool -K {cfg.ifname} gro on tso off") 96 + cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote) 97 + except KsftXfailEx: 98 + pass 99 + 100 + def _gro_variants(): 101 + """Generator that yields all combinations of protocol and test types.""" 102 + 103 + for protocol in ["ipv4", "ipv6", "ipip"]: 104 + for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]: 105 + yield protocol, test_name 106 + 107 + 108 + @ksft_variants(_gro_variants()) 109 + def test(cfg, protocol, test_name): 110 + """Run a single GRO test with retries.""" 111 + 112 + ipver = "6" if protocol[-1] == "6" else "4" 113 + cfg.require_ipver(ipver) 114 + 115 + _setup(cfg, test_name) 116 + 117 + base_cmd_args = [ 118 + f"--{protocol}", 119 + f"--dmac {_resolve_dmac(cfg, ipver)}", 120 + f"--smac {cfg.remote_dev['address']}", 121 + f"--daddr {cfg.addr_v[ipver]}", 122 + f"--saddr {cfg.remote_addr_v[ipver]}", 123 + f"--test {test_name}", 124 + "--verbose" 125 + ] 126 + base_args = " ".join(base_cmd_args) 127 + 128 + # Each test is run 6 times to deflake, because given the receive timing, 129 + # not all packets that should coalesce will be considered in the same flow 130 + # on every try. 131 + max_retries = 6 132 + for attempt in range(max_retries): 133 + rx_cmd = f"{cfg.bin_local} {base_args} --rx --iface {cfg.ifname}" 134 + tx_cmd = f"{cfg.bin_remote} {base_args} --iface {cfg.remote_ifname}" 135 + 136 + fail_now = attempt >= max_retries - 1 137 + 138 + with bkg(rx_cmd, ksft_ready=True, exit_wait=True, 139 + fail=fail_now) as rx_proc: 140 + cmd(tx_cmd, host=cfg.remote) 141 + 142 + if rx_proc.ret == 0: 143 + return 144 + 145 + ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# ')) 146 + ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# ')) 147 + 148 + if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"): 149 + ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment") 150 + return 151 + 152 + ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...") 153 + 154 + 155 + def main() -> None: 156 + """ Ksft boiler plate main """ 157 + 158 + with NetDrvEpEnv(__file__) as cfg: 159 + ksft_run(cases=[test], args=(cfg,)) 160 + ksft_exit() 161 + 162 + 163 + if __name__ == "__main__": 164 + main()
+1
tools/testing/selftests/drivers/net/hw/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 iou-zcrx 3 3 ncdevmem 4 + toeplitz
+5 -1
tools/testing/selftests/drivers/net/hw/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0+ OR MIT 2 2 3 - TEST_GEN_FILES = iou-zcrx 3 + TEST_GEN_FILES := \ 4 + iou-zcrx \ 5 + toeplitz \ 6 + # end of TEST_GEN_FILES 4 7 5 8 TEST_PROGS = \ 6 9 csum.py \ ··· 24 21 rss_ctx.py \ 25 22 rss_flow_label.py \ 26 23 rss_input_xfrm.py \ 24 + toeplitz.py \ 27 25 tso.py \ 28 26 xsk_reconfig.py \ 29 27 #
+2 -2
tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
··· 25 25 fd_read_timeout, ip, rand_port, wait_port_listen, wait_file 26 26 from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx 27 27 from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ 28 - ksft_setup 28 + ksft_setup, ksft_variants, KsftNamedVariant 29 29 from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ 30 30 ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none 31 31 from drivers.net.lib.py import GenerateTraffic, Remote ··· 40 40 "wait_port_listen", "wait_file", 41 41 "KsftSkipEx", "KsftFailEx", "KsftXfailEx", 42 42 "ksft_disruptive", "ksft_exit", "ksft_pr", "ksft_run", 43 - "ksft_setup", 43 + "ksft_setup", "ksft_variants", "KsftNamedVariant", 44 44 "ksft_eq", "ksft_ge", "ksft_in", "ksft_is", "ksft_lt", 45 45 "ksft_ne", "ksft_not_in", "ksft_raises", "ksft_true", "ksft_gt", 46 46 "ksft_not_none", "ksft_not_none",
+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()
+2 -2
tools/testing/selftests/drivers/net/lib/py/__init__.py
··· 25 25 fd_read_timeout, ip, rand_port, wait_port_listen, wait_file 26 26 from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx 27 27 from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ 28 - ksft_setup 28 + ksft_setup, ksft_variants, KsftNamedVariant 29 29 from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ 30 30 ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none 31 31 ··· 38 38 "wait_port_listen", "wait_file", 39 39 "KsftSkipEx", "KsftFailEx", "KsftXfailEx", 40 40 "ksft_disruptive", "ksft_exit", "ksft_pr", "ksft_run", 41 - "ksft_setup", 41 + "ksft_setup", "ksft_variants", "KsftNamedVariant", 42 42 "ksft_eq", "ksft_ge", "ksft_in", "ksft_is", "ksft_lt", 43 43 "ksft_ne", "ksft_not_in", "ksft_raises", "ksft_true", "ksft_gt", 44 44 "ksft_not_none", "ksft_not_none"]
+2
tools/testing/selftests/drivers/net/lib/py/env.py
··· 168 168 169 169 # resolve remote interface name 170 170 self.remote_ifname = self.resolve_remote_ifc() 171 + self.remote_dev = ip("-d link show dev " + self.remote_ifname, 172 + host=self.remote, json=True)[0] 171 173 172 174 self._required_cmd = {} 173 175
+14 -28
tools/testing/selftests/drivers/net/xdp.py
··· 12 12 from enum import Enum 13 13 14 14 from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge, ksft_ne, ksft_pr 15 + from lib.py import KsftNamedVariant, ksft_variants 15 16 from lib.py import KsftFailEx, NetDrvEpEnv 16 17 from lib.py import EthtoolFamily, NetdevFamily, NlError 17 18 from lib.py import bkg, cmd, rand_port, wait_port_listen ··· 673 672 _validate_res(res, offset_lst, pkt_sz_lst) 674 673 675 674 676 - def _test_xdp_native_ifc_stats(cfg, act): 675 + @ksft_variants([ 676 + KsftNamedVariant("pass", XDPAction.PASS), 677 + KsftNamedVariant("drop", XDPAction.DROP), 678 + KsftNamedVariant("tx", XDPAction.TX), 679 + ]) 680 + def test_xdp_native_qstats(cfg, act): 681 + """ 682 + Send 1000 messages. Expect XDP action specified in @act. 683 + Make sure the packets were counted to interface level qstats 684 + (Rx, and Tx if act is TX). 685 + """ 686 + 677 687 cfg.require_cmd("socat") 678 688 679 689 bpf_info = BPFProgInfo("xdp_prog", "xdp_native.bpf.o", "xdp", 1500) ··· 745 733 ksft_ge(after['tx-packets'], before['tx-packets']) 746 734 747 735 748 - def test_xdp_native_qstats_pass(cfg): 749 - """ 750 - Send 2000 messages, expect XDP_PASS, make sure the packets were counted 751 - to interface level qstats (Rx). 752 - """ 753 - _test_xdp_native_ifc_stats(cfg, XDPAction.PASS) 754 - 755 - 756 - def test_xdp_native_qstats_drop(cfg): 757 - """ 758 - Send 2000 messages, expect XDP_DROP, make sure the packets were counted 759 - to interface level qstats (Rx). 760 - """ 761 - _test_xdp_native_ifc_stats(cfg, XDPAction.DROP) 762 - 763 - 764 - def test_xdp_native_qstats_tx(cfg): 765 - """ 766 - Send 2000 messages, expect XDP_TX, make sure the packets were counted 767 - to interface level qstats (Rx and Tx) 768 - """ 769 - _test_xdp_native_ifc_stats(cfg, XDPAction.TX) 770 - 771 - 772 736 def main(): 773 737 """ 774 738 Main function to execute the XDP tests. ··· 769 781 test_xdp_native_adjst_tail_shrnk_data, 770 782 test_xdp_native_adjst_head_grow_data, 771 783 test_xdp_native_adjst_head_shrnk_data, 772 - test_xdp_native_qstats_pass, 773 - test_xdp_native_qstats_drop, 774 - test_xdp_native_qstats_tx, 784 + test_xdp_native_qstats, 775 785 ], 776 786 args=(cfg,)) 777 787 ksft_exit()
-2
tools/testing/selftests/net/.gitignore
··· 7 7 diag_uid 8 8 epoll_busy_poll 9 9 fin_ack_lat 10 - gro 11 10 hwtstamp_config 12 11 io_uring_zerocopy_tx 13 12 ioam6_parser ··· 56 57 tfo 57 58 timestamping 58 59 tls 59 - toeplitz 60 60 tools 61 61 tun 62 62 txring_overwrite
-7
tools/testing/selftests/net/Makefile
··· 38 38 fq_band_pktlimit.sh \ 39 39 gre_gso.sh \ 40 40 gre_ipv6_lladdr.sh \ 41 - gro.sh \ 42 41 icmp.sh \ 43 42 icmp_redirect.sh \ 44 43 io_uring_zerocopy_tx.sh \ ··· 120 121 # end of TEST_PROGS 121 122 122 123 TEST_PROGS_EXTENDED := \ 123 - toeplitz.sh \ 124 - toeplitz_client.sh \ 125 124 xfrm_policy_add_speed.sh \ 126 125 # end of TEST_PROGS_EXTENDED 127 126 ··· 127 130 bind_bhash \ 128 131 cmsg_sender \ 129 132 fin_ack_lat \ 130 - gro \ 131 133 hwtstamp_config \ 132 134 io_uring_zerocopy_tx \ 133 135 ioam6_parser \ ··· 155 159 tcp_mmap \ 156 160 tfo \ 157 161 timestamping \ 158 - toeplitz \ 159 162 txring_overwrite \ 160 163 txtimestamp \ 161 164 udpgso \ ··· 188 193 in_netns.sh \ 189 194 lib.sh \ 190 195 settings \ 191 - setup_loopback.sh \ 192 - setup_veth.sh \ 193 196 # end of TEST_FILES 194 197 195 198 # YNL files, must be before "include ..lib.mk"
+4 -1
tools/testing/selftests/net/gro.c tools/testing/selftests/drivers/net/gro.c
··· 57 57 #include <string.h> 58 58 #include <unistd.h> 59 59 60 - #include "../kselftest.h" 60 + #include "../../kselftest.h" 61 + #include "../../net/lib/ksft.h" 61 62 62 63 #define DPORT 8000 63 64 #define SPORT 1500 ··· 1127 1126 setup_sock_filter(rxfd); 1128 1127 set_timeout(rxfd); 1129 1128 bind_packetsocket(rxfd); 1129 + 1130 + ksft_ready(); 1130 1131 1131 1132 memset(correct_payload, 0, sizeof(correct_payload)); 1132 1133
-105
tools/testing/selftests/net/gro.sh
··· 1 - #!/bin/bash 2 - # SPDX-License-Identifier: GPL-2.0 3 - 4 - readonly SERVER_MAC="aa:00:00:00:00:02" 5 - readonly CLIENT_MAC="aa:00:00:00:00:01" 6 - readonly TESTS=("data" "ack" "flags" "tcp" "ip" "large") 7 - readonly PROTOS=("ipv4" "ipv6" "ipip") 8 - dev="" 9 - test="all" 10 - proto="ipv4" 11 - 12 - run_test() { 13 - local server_pid=0 14 - local exit_code=0 15 - local protocol=$1 16 - local test=$2 17 - local ARGS=( "--${protocol}" "--dmac" "${SERVER_MAC}" \ 18 - "--smac" "${CLIENT_MAC}" "--test" "${test}" "--verbose" ) 19 - 20 - setup_ns 21 - # Each test is run 6 times to deflake, because given the receive timing, 22 - # not all packets that should coalesce will be considered in the same flow 23 - # on every try. 24 - for tries in {1..6}; do 25 - # Actual test starts here 26 - ip netns exec $server_ns ./gro "${ARGS[@]}" "--rx" "--iface" "server" \ 27 - 1>>log.txt & 28 - server_pid=$! 29 - sleep 0.5 # to allow for socket init 30 - ip netns exec $client_ns ./gro "${ARGS[@]}" "--iface" "client" \ 31 - 1>>log.txt 32 - wait "${server_pid}" 33 - exit_code=$? 34 - if [[ ${test} == "large" && -n "${KSFT_MACHINE_SLOW}" && \ 35 - ${exit_code} -ne 0 ]]; then 36 - echo "Ignoring errors due to slow environment" 1>&2 37 - exit_code=0 38 - fi 39 - if [[ "${exit_code}" -eq 0 ]]; then 40 - break; 41 - fi 42 - done 43 - cleanup_ns 44 - echo ${exit_code} 45 - } 46 - 47 - run_all_tests() { 48 - local failed_tests=() 49 - for proto in "${PROTOS[@]}"; do 50 - for test in "${TESTS[@]}"; do 51 - echo "running test ${proto} ${test}" >&2 52 - exit_code=$(run_test $proto $test) 53 - if [[ "${exit_code}" -ne 0 ]]; then 54 - failed_tests+=("${proto}_${test}") 55 - fi; 56 - done; 57 - done 58 - if [[ ${#failed_tests[@]} -ne 0 ]]; then 59 - echo "failed tests: ${failed_tests[*]}. \ 60 - Please see log.txt for more logs" 61 - exit 1 62 - else 63 - echo "All Tests Succeeded!" 64 - fi; 65 - } 66 - 67 - usage() { 68 - echo "Usage: $0 \ 69 - [-i <DEV>] \ 70 - [-t data|ack|flags|tcp|ip|large] \ 71 - [-p <ipv4|ipv6>]" 1>&2; 72 - exit 1; 73 - } 74 - 75 - while getopts "i:t:p:" opt; do 76 - case "${opt}" in 77 - i) 78 - dev="${OPTARG}" 79 - ;; 80 - t) 81 - test="${OPTARG}" 82 - ;; 83 - p) 84 - proto="${OPTARG}" 85 - ;; 86 - *) 87 - usage 88 - ;; 89 - esac 90 - done 91 - 92 - if [ -n "$dev" ]; then 93 - source setup_loopback.sh 94 - else 95 - source setup_veth.sh 96 - fi 97 - 98 - setup 99 - trap cleanup EXIT 100 - if [[ "${test}" == "all" ]]; then 101 - run_all_tests 102 - else 103 - exit_code=$(run_test "${proto}" "${test}") 104 - exit $exit_code 105 - fi;
+1
tools/testing/selftests/net/lib/Makefile
··· 8 8 TEST_FILES := \ 9 9 ../../../../net/ynl \ 10 10 ../../../../../Documentation/netlink/specs \ 11 + ksft_setup_loopback.sh \ 11 12 # end of TEST_FILES 12 13 13 14 TEST_GEN_FILES := \
+111
tools/testing/selftests/net/lib/ksft_setup_loopback.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + # Setup script for running ksft tests over a real interface in loopback mode. 5 + # This scripts replaces the historical setup_loopback.sh. It puts 6 + # a (presumably) real hardware interface into loopback mode, creates macvlan 7 + # interfaces on top and places them in a network namespace for isolation. 8 + # 9 + # NETIF env variable must be exported to indicate the real target device. 10 + # Note that the test will override NETIF with one of the macvlans, the 11 + # actual ksft test will only see the macvlans. 12 + # 13 + # Example use: 14 + # export NETIF=eth0 15 + # ./net/lib/ksft_setup_loopback.sh ./drivers/net/gro.py 16 + 17 + if [ -z "$NETIF" ]; then 18 + echo "Error: NETIF variable not set" 19 + exit 1 20 + fi 21 + if ! [ -d "/sys/class/net/$NETIF" ]; then 22 + echo "Error: Can't find $NETIF, invalid netdevice" 23 + exit 1 24 + fi 25 + 26 + # Save original settings for cleanup 27 + readonly FLUSH_PATH="/sys/class/net/${NETIF}/gro_flush_timeout" 28 + readonly IRQ_PATH="/sys/class/net/${NETIF}/napi_defer_hard_irqs" 29 + FLUSH_TIMEOUT="$(< "${FLUSH_PATH}")" 30 + readonly FLUSH_TIMEOUT 31 + HARD_IRQS="$(< "${IRQ_PATH}")" 32 + readonly HARD_IRQS 33 + 34 + SERVER_NS=$(mktemp -u server-XXXXXXXX) 35 + readonly SERVER_NS 36 + CLIENT_NS=$(mktemp -u client-XXXXXXXX) 37 + readonly CLIENT_NS 38 + readonly SERVER_MAC="aa:00:00:00:00:02" 39 + readonly CLIENT_MAC="aa:00:00:00:00:01" 40 + 41 + # ksft expects addresses to communicate with remote 42 + export LOCAL_V6=2001:db8:1::1 43 + export REMOTE_V6=2001:db8:1::2 44 + 45 + cleanup() { 46 + local exit_code=$? 47 + 48 + echo "Cleaning up..." 49 + 50 + # Remove macvlan interfaces and namespaces 51 + ip -netns "${SERVER_NS}" link del dev server 2>/dev/null || true 52 + ip netns del "${SERVER_NS}" 2>/dev/null || true 53 + ip -netns "${CLIENT_NS}" link del dev client 2>/dev/null || true 54 + ip netns del "${CLIENT_NS}" 2>/dev/null || true 55 + 56 + # Disable loopback 57 + ethtool -K "${NETIF}" loopback off 2>/dev/null || true 58 + sleep 1 59 + 60 + echo "${FLUSH_TIMEOUT}" >"${FLUSH_PATH}" 61 + echo "${HARD_IRQS}" >"${IRQ_PATH}" 62 + 63 + exit $exit_code 64 + } 65 + 66 + trap cleanup EXIT INT TERM 67 + 68 + # Enable loopback mode 69 + echo "Enabling loopback on ${NETIF}..." 70 + ethtool -K "${NETIF}" loopback on || { 71 + echo "Failed to enable loopback mode" 72 + exit 1 73 + } 74 + # The interface may need time to get carrier back, but selftests 75 + # will wait for carrier, so no need to wait / sleep here. 76 + 77 + # Use timer on host to trigger the network stack 78 + # Also disable device interrupt to not depend on NIC interrupt 79 + # Reduce test flakiness caused by unexpected interrupts 80 + echo 100000 >"${FLUSH_PATH}" 81 + echo 50 >"${IRQ_PATH}" 82 + 83 + # Create server namespace with macvlan 84 + ip netns add "${SERVER_NS}" 85 + ip link add link "${NETIF}" dev server address "${SERVER_MAC}" type macvlan 86 + ip link set dev server netns "${SERVER_NS}" 87 + ip -netns "${SERVER_NS}" link set dev server up 88 + ip -netns "${SERVER_NS}" addr add $LOCAL_V6/64 dev server 89 + ip -netns "${SERVER_NS}" link set dev lo up 90 + 91 + # Create client namespace with macvlan 92 + ip netns add "${CLIENT_NS}" 93 + ip link add link "${NETIF}" dev client address "${CLIENT_MAC}" type macvlan 94 + ip link set dev client netns "${CLIENT_NS}" 95 + ip -netns "${CLIENT_NS}" link set dev client up 96 + ip -netns "${CLIENT_NS}" addr add $REMOTE_V6/64 dev client 97 + ip -netns "${CLIENT_NS}" link set dev lo up 98 + 99 + echo "Setup complete!" 100 + echo " Device: ${NETIF}" 101 + echo " Server NS: ${SERVER_NS}" 102 + echo " Client NS: ${CLIENT_NS}" 103 + echo "" 104 + 105 + # Setup environment variables for tests 106 + export NETIF=server 107 + export REMOTE_TYPE=netns 108 + export REMOTE_ARGS="${CLIENT_NS}" 109 + 110 + # Run the command 111 + ip netns exec "${SERVER_NS}" "$@"
+3 -2
tools/testing/selftests/net/lib/py/__init__.py
··· 8 8 from .ksft import KsftFailEx, KsftSkipEx, KsftXfailEx, ksft_pr, ksft_eq, \ 9 9 ksft_ne, ksft_true, ksft_not_none, ksft_in, ksft_not_in, ksft_is, \ 10 10 ksft_ge, ksft_gt, ksft_lt, ksft_raises, ksft_busy_wait, \ 11 - ktap_result, ksft_disruptive, ksft_setup, ksft_run, ksft_exit 11 + ktap_result, ksft_disruptive, ksft_setup, ksft_run, ksft_exit, \ 12 + ksft_variants, KsftNamedVariant 12 13 from .netns import NetNS, NetNSEnter 13 14 from .nsim import NetdevSim, NetdevSimDev 14 15 from .utils import CmdExitFailure, fd_read_timeout, cmd, bkg, defer, \ ··· 22 21 "ksft_ne", "ksft_true", "ksft_not_none", "ksft_in", "ksft_not_in", 23 22 "ksft_is", "ksft_ge", "ksft_gt", "ksft_lt", "ksft_raises", 24 23 "ksft_busy_wait", "ktap_result", "ksft_disruptive", "ksft_setup", 25 - "ksft_run", "ksft_exit", 24 + "ksft_run", "ksft_exit", "ksft_variants", "KsftNamedVariant", 26 25 "NetNS", "NetNSEnter", 27 26 "CmdExitFailure", "fd_read_timeout", "cmd", "bkg", "defer", 28 27 "bpftool", "ip", "ethtool", "bpftrace", "rand_port",
+78 -13
tools/testing/selftests/net/lib/py/ksft.py
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 - import builtins 4 3 import functools 5 4 import inspect 6 5 import signal 7 6 import sys 8 7 import time 9 8 import traceback 9 + from collections import namedtuple 10 10 from .consts import KSFT_MAIN_NAME 11 11 from .utils import global_defer_queue 12 12 ··· 136 136 time.sleep(sleep) 137 137 138 138 139 - def ktap_result(ok, cnt=1, case="", comment=""): 139 + def ktap_result(ok, cnt=1, case_name="", comment=""): 140 140 global KSFT_RESULT_ALL 141 141 KSFT_RESULT_ALL = KSFT_RESULT_ALL and ok 142 142 ··· 146 146 res += "ok " 147 147 res += str(cnt) + " " 148 148 res += KSFT_MAIN_NAME 149 - if case: 150 - res += "." + str(case.__name__) 149 + if case_name: 150 + res += "." + case_name 151 151 if comment: 152 152 res += " # " + comment 153 153 print(res, flush=True) ··· 163 163 entry = global_defer_queue.pop() 164 164 try: 165 165 entry.exec_only() 166 - except: 166 + except BaseException: 167 167 ksft_pr(f"Exception while handling defer / cleanup (callback {i} of {qlen_start})!") 168 168 tb = traceback.format_exc() 169 169 for line in tb.strip().split('\n'): 170 170 ksft_pr("Defer Exception|", line) 171 171 KSFT_RESULT = False 172 + 173 + 174 + KsftCaseFunction = namedtuple("KsftCaseFunction", 175 + ['name', 'original_func', 'variants']) 172 176 173 177 174 178 def ksft_disruptive(func): ··· 185 181 @functools.wraps(func) 186 182 def wrapper(*args, **kwargs): 187 183 if not KSFT_DISRUPTIVE: 188 - raise KsftSkipEx(f"marked as disruptive") 184 + raise KsftSkipEx("marked as disruptive") 189 185 return func(*args, **kwargs) 190 186 return wrapper 187 + 188 + 189 + class KsftNamedVariant: 190 + """ Named string name + argument list tuple for @ksft_variants """ 191 + 192 + def __init__(self, name, *params): 193 + self.params = params 194 + self.name = name or "_".join([str(x) for x in self.params]) 195 + 196 + 197 + def ksft_variants(params): 198 + """ 199 + Decorator defining the sets of inputs for a test. 200 + The parameters will be included in the name of the resulting sub-case. 201 + Parameters can be either single object, tuple or a KsftNamedVariant. 202 + The argument can be a list or a generator. 203 + 204 + Example: 205 + 206 + @ksft_variants([ 207 + (1, "a"), 208 + (2, "b"), 209 + KsftNamedVariant("three", 3, "c"), 210 + ]) 211 + def my_case(cfg, a, b): 212 + pass # ... 213 + 214 + ksft_run(cases=[my_case], args=(cfg, )) 215 + 216 + Will generate cases: 217 + my_case.1_a 218 + my_case.2_b 219 + my_case.three 220 + """ 221 + 222 + return lambda func: KsftCaseFunction(func.__name__, func, params) 191 223 192 224 193 225 def ksft_setup(env): ··· 239 199 return False 240 200 try: 241 201 return bool(int(value)) 242 - except: 202 + except Exception: 243 203 raise Exception(f"failed to parse {name}") 244 204 245 205 if "DISRUPTIVE" in env: ··· 260 220 ksft_pr(f"Ignoring SIGTERM (cnt: {term_cnt}), already exiting...") 261 221 262 222 263 - def ksft_run(cases=None, globs=None, case_pfx=None, args=()): 264 - cases = cases or [] 223 + def _ksft_generate_test_cases(cases, globs, case_pfx, args): 224 + """Generate a flat list of (func, args, name) tuples""" 265 225 226 + cases = cases or [] 227 + test_cases = [] 228 + 229 + # If using the globs method find all relevant functions 266 230 if globs and case_pfx: 267 231 for key, value in globs.items(): 268 232 if not callable(value): ··· 276 232 cases.append(value) 277 233 break 278 234 235 + for func in cases: 236 + if isinstance(func, KsftCaseFunction): 237 + # Parametrized test - create case for each param 238 + for param in func.variants: 239 + if not isinstance(param, KsftNamedVariant): 240 + if not isinstance(param, tuple): 241 + param = (param, ) 242 + param = KsftNamedVariant(None, *param) 243 + 244 + test_cases.append((func.original_func, 245 + (*args, *param.params), 246 + func.name + "." + param.name)) 247 + else: 248 + test_cases.append((func, args, func.__name__)) 249 + 250 + return test_cases 251 + 252 + 253 + def ksft_run(cases=None, globs=None, case_pfx=None, args=()): 254 + test_cases = _ksft_generate_test_cases(cases, globs, case_pfx, args) 255 + 279 256 global term_cnt 280 257 term_cnt = 0 281 258 prev_sigterm = signal.signal(signal.SIGTERM, _ksft_intr) ··· 304 239 totals = {"pass": 0, "fail": 0, "skip": 0, "xfail": 0} 305 240 306 241 print("TAP version 13", flush=True) 307 - print("1.." + str(len(cases)), flush=True) 242 + print("1.." + str(len(test_cases)), flush=True) 308 243 309 244 global KSFT_RESULT 310 245 cnt = 0 311 246 stop = False 312 - for case in cases: 247 + for func, args, name in test_cases: 313 248 KSFT_RESULT = True 314 249 cnt += 1 315 250 comment = "" 316 251 cnt_key = "" 317 252 318 253 try: 319 - case(*args) 254 + func(*args) 320 255 except KsftSkipEx as e: 321 256 comment = "SKIP " + str(e) 322 257 cnt_key = 'skip' ··· 338 273 if not cnt_key: 339 274 cnt_key = 'pass' if KSFT_RESULT else 'fail' 340 275 341 - ktap_result(KSFT_RESULT, cnt, case, comment=comment) 276 + ktap_result(KSFT_RESULT, cnt, name, comment=comment) 342 277 totals[cnt_key] += 1 343 278 344 279 if stop:
+1 -1
tools/testing/selftests/net/lib/py/nsim.py
··· 27 27 self.port_index = port_index 28 28 self.ns = ns 29 29 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) 30 - ret = ip("-j link show dev %s" % ifname, ns=ns) 30 + ret = ip("-d -j link show dev %s" % ifname, ns=ns) 31 31 self.dev = json.loads(ret.stdout)[0] 32 32 self.ifindex = self.dev["ifindex"] 33 33
+12 -8
tools/testing/selftests/net/lib/py/utils.py
··· 32 32 Use bkg() instead to run a command in the background. 33 33 """ 34 34 def __init__(self, comm, shell=None, fail=True, ns=None, background=False, 35 - host=None, timeout=5, ksft_wait=None): 35 + host=None, timeout=5, ksft_ready=None, ksft_wait=None): 36 36 if ns: 37 37 comm = f'ip netns exec {ns} ' + comm 38 38 ··· 52 52 # ksft_wait lets us wait for the background process to fully start, 53 53 # we pass an FD to the child process, and wait for it to write back. 54 54 # Similarly term_fd tells child it's time to exit. 55 - pass_fds = () 55 + pass_fds = [] 56 56 env = os.environ.copy() 57 57 if ksft_wait is not None: 58 - rfd, ready_fd = os.pipe() 59 58 wait_fd, self.ksft_term_fd = os.pipe() 60 - pass_fds = (ready_fd, wait_fd, ) 61 - env["KSFT_READY_FD"] = str(ready_fd) 59 + pass_fds.append(wait_fd) 62 60 env["KSFT_WAIT_FD"] = str(wait_fd) 61 + ksft_ready = True # ksft_wait implies ready 62 + if ksft_ready is not None: 63 + rfd, ready_fd = os.pipe() 64 + pass_fds.append(ready_fd) 65 + env["KSFT_READY_FD"] = str(ready_fd) 63 66 64 67 self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, 65 68 stderr=subprocess.PIPE, pass_fds=pass_fds, 66 69 env=env) 67 70 if ksft_wait is not None: 68 - os.close(ready_fd) 69 71 os.close(wait_fd) 72 + if ksft_ready is not None: 73 + os.close(ready_fd) 70 74 msg = fd_read_timeout(rfd, ksft_wait) 71 75 os.close(rfd) 72 76 if not msg: ··· 120 116 with bkg("my_binary", ksft_wait=5): 121 117 """ 122 118 def __init__(self, comm, shell=None, fail=None, ns=None, host=None, 123 - exit_wait=False, ksft_wait=None): 119 + exit_wait=False, ksft_ready=None, ksft_wait=None): 124 120 super().__init__(comm, background=True, 125 121 shell=shell, fail=fail, ns=ns, host=host, 126 - ksft_wait=ksft_wait) 122 + ksft_ready=ksft_ready, ksft_wait=ksft_wait) 127 123 self.terminate = not exit_wait and not ksft_wait 128 124 self._exit_wait = exit_wait 129 125 self.check_fail = fail
-120
tools/testing/selftests/net/setup_loopback.sh
··· 1 - #!/bin/bash 2 - # SPDX-License-Identifier: GPL-2.0 3 - 4 - readonly FLUSH_PATH="/sys/class/net/${dev}/gro_flush_timeout" 5 - readonly IRQ_PATH="/sys/class/net/${dev}/napi_defer_hard_irqs" 6 - readonly FLUSH_TIMEOUT="$(< ${FLUSH_PATH})" 7 - readonly HARD_IRQS="$(< ${IRQ_PATH})" 8 - readonly server_ns=$(mktemp -u server-XXXXXXXX) 9 - readonly client_ns=$(mktemp -u client-XXXXXXXX) 10 - 11 - netdev_check_for_carrier() { 12 - local -r dev="$1" 13 - 14 - for i in {1..5}; do 15 - carrier="$(cat /sys/class/net/${dev}/carrier)" 16 - if [[ "${carrier}" -ne 1 ]] ; then 17 - echo "carrier not ready yet..." >&2 18 - sleep 1 19 - else 20 - echo "carrier ready" >&2 21 - break 22 - fi 23 - done 24 - echo "${carrier}" 25 - } 26 - 27 - # Assumes that there is no existing ipvlan device on the physical device 28 - setup_loopback_environment() { 29 - local dev="$1" 30 - 31 - # Fail hard if cannot turn on loopback mode for current NIC 32 - ethtool -K "${dev}" loopback on || exit 1 33 - sleep 1 34 - 35 - # Check for the carrier 36 - carrier=$(netdev_check_for_carrier ${dev}) 37 - if [[ "${carrier}" -ne 1 ]] ; then 38 - echo "setup_loopback_environment failed" 39 - exit 1 40 - fi 41 - } 42 - 43 - setup_macvlan_ns(){ 44 - local -r link_dev="$1" 45 - local -r ns_name="$2" 46 - local -r ns_dev="$3" 47 - local -r ns_mac="$4" 48 - local -r addr="$5" 49 - 50 - ip link add link "${link_dev}" dev "${ns_dev}" \ 51 - address "${ns_mac}" type macvlan 52 - exit_code=$? 53 - if [[ "${exit_code}" -ne 0 ]]; then 54 - echo "setup_macvlan_ns failed" 55 - exit $exit_code 56 - fi 57 - 58 - [[ -e /var/run/netns/"${ns_name}" ]] || ip netns add "${ns_name}" 59 - ip link set dev "${ns_dev}" netns "${ns_name}" 60 - ip -netns "${ns_name}" link set dev "${ns_dev}" up 61 - if [[ -n "${addr}" ]]; then 62 - ip -netns "${ns_name}" addr add dev "${ns_dev}" "${addr}" 63 - fi 64 - 65 - sleep 1 66 - } 67 - 68 - cleanup_macvlan_ns(){ 69 - while (( $# >= 2 )); do 70 - ns_name="$1" 71 - ns_dev="$2" 72 - ip -netns "${ns_name}" link del dev "${ns_dev}" 73 - ip netns del "${ns_name}" 74 - shift 2 75 - done 76 - } 77 - 78 - cleanup_loopback(){ 79 - local -r dev="$1" 80 - 81 - ethtool -K "${dev}" loopback off 82 - sleep 1 83 - 84 - # Check for the carrier 85 - carrier=$(netdev_check_for_carrier ${dev}) 86 - if [[ "${carrier}" -ne 1 ]] ; then 87 - echo "setup_loopback_environment failed" 88 - exit 1 89 - fi 90 - } 91 - 92 - setup_interrupt() { 93 - # Use timer on host to trigger the network stack 94 - # Also disable device interrupt to not depend on NIC interrupt 95 - # Reduce test flakiness caused by unexpected interrupts 96 - echo 100000 >"${FLUSH_PATH}" 97 - echo 50 >"${IRQ_PATH}" 98 - } 99 - 100 - setup_ns() { 101 - # Set up server_ns namespace and client_ns namespace 102 - setup_macvlan_ns "${dev}" ${server_ns} server "${SERVER_MAC}" 103 - setup_macvlan_ns "${dev}" ${client_ns} client "${CLIENT_MAC}" 104 - } 105 - 106 - cleanup_ns() { 107 - cleanup_macvlan_ns ${server_ns} server ${client_ns} client 108 - } 109 - 110 - setup() { 111 - setup_loopback_environment "${dev}" 112 - setup_interrupt 113 - } 114 - 115 - cleanup() { 116 - cleanup_loopback "${dev}" 117 - 118 - echo "${FLUSH_TIMEOUT}" >"${FLUSH_PATH}" 119 - echo "${HARD_IRQS}" >"${IRQ_PATH}" 120 - }
-45
tools/testing/selftests/net/setup_veth.sh
··· 1 - #!/bin/bash 2 - # SPDX-License-Identifier: GPL-2.0 3 - 4 - readonly server_ns=$(mktemp -u server-XXXXXXXX) 5 - readonly client_ns=$(mktemp -u client-XXXXXXXX) 6 - 7 - setup_veth_ns() { 8 - local -r link_dev="$1" 9 - local -r ns_name="$2" 10 - local -r ns_dev="$3" 11 - local -r ns_mac="$4" 12 - 13 - [[ -e /var/run/netns/"${ns_name}" ]] || ip netns add "${ns_name}" 14 - echo 200000 > "/sys/class/net/${ns_dev}/gro_flush_timeout" 15 - echo 1 > "/sys/class/net/${ns_dev}/napi_defer_hard_irqs" 16 - ip link set dev "${ns_dev}" netns "${ns_name}" mtu 65535 17 - ip -netns "${ns_name}" link set dev "${ns_dev}" up 18 - 19 - ip netns exec "${ns_name}" ethtool -K "${ns_dev}" gro on tso off 20 - } 21 - 22 - setup_ns() { 23 - # Set up server_ns namespace and client_ns namespace 24 - ip link add name server type veth peer name client 25 - 26 - setup_veth_ns "${dev}" ${server_ns} server "${SERVER_MAC}" 27 - setup_veth_ns "${dev}" ${client_ns} client "${CLIENT_MAC}" 28 - } 29 - 30 - cleanup_ns() { 31 - local ns_name 32 - 33 - for ns_name in ${client_ns} ${server_ns}; do 34 - [[ -e /var/run/netns/"${ns_name}" ]] && ip netns del "${ns_name}" 35 - done 36 - } 37 - 38 - setup() { 39 - # no global init setup step needed 40 - : 41 - } 42 - 43 - cleanup() { 44 - cleanup_ns 45 - }
+6 -1
tools/testing/selftests/net/toeplitz.c tools/testing/selftests/drivers/net/hw/toeplitz.c
··· 52 52 #include <sys/types.h> 53 53 #include <unistd.h> 54 54 55 - #include "../kselftest.h" 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
-199
tools/testing/selftests/net/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 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/net/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