Path: blob/master/tools/testing/selftests/drivers/net/gro.py
38237 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.023"""4GRO (Generic Receive Offload) conformance tests.56Validates that GRO coalescing works correctly by running the gro7binary in different configurations and checking for correct packet8coalescing behavior.910Test cases:11- data: Data packets with same size/headers and correct seq numbers coalesce12- ack: Pure ACK packets do not coalesce13- flags: Packets with PSH, SYN, URG, RST flags do not coalesce14- tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce15- ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce16- large: Packets larger than GRO_MAX_SIZE don't coalesce17"""1819import os20from lib.py import ksft_run, ksft_exit, ksft_pr21from lib.py import NetDrvEpEnv, KsftXfailEx22from lib.py import cmd, defer, bkg, ip23from lib.py import ksft_variants242526def _resolve_dmac(cfg, ipver):27"""28Find the destination MAC address remote host should use to send packets29towards the local host. It may be a router / gateway address.30"""3132attr = "dmac" + ipver33# Cache the response across test cases34if hasattr(cfg, attr):35return getattr(cfg, attr)3637route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}",38json=True, host=cfg.remote)[0]39gw = route.get("gateway")40# Local L2 segment, address directly41if not gw:42setattr(cfg, attr, cfg.dev['address'])43return getattr(cfg, attr)4445# ping to make sure neighbor is resolved,46# bind to an interface, for v6 the GW is likely link local47cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote)4849neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}",50json=True, host=cfg.remote)[0]51setattr(cfg, attr, neigh['lladdr'])52return getattr(cfg, attr)535455def _write_defer_restore(cfg, path, val, defer_undo=False):56with open(path, "r", encoding="utf-8") as fp:57orig_val = fp.read().strip()58if str(val) == orig_val:59return60with open(path, "w", encoding="utf-8") as fp:61fp.write(val)62if defer_undo:63defer(_write_defer_restore, cfg, path, orig_val)646566def _set_mtu_restore(dev, mtu, host):67if dev['mtu'] < mtu:68ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host)69defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)707172def _setup(cfg, test_name):73""" Setup hardware loopback mode for GRO testing. """7475if not hasattr(cfg, "bin_remote"):76cfg.bin_local = cfg.test_dir / "gro"77cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)7879# "large" test needs at least 4k MTU80if test_name == "large":81_set_mtu_restore(cfg.dev, 4096, None)82_set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)8384flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"85irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"8687_write_defer_restore(cfg, flush_path, "200000", defer_undo=True)88_write_defer_restore(cfg, irq_path, "10", defer_undo=True)8990try:91# Disable TSO for local tests92cfg.require_nsim() # will raise KsftXfailEx if not running on nsim9394cmd(f"ethtool -K {cfg.ifname} gro on tso off")95cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote)96except KsftXfailEx:97pass9899def _gro_variants():100"""Generator that yields all combinations of protocol and test types."""101102for protocol in ["ipv4", "ipv6", "ipip"]:103for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]:104yield protocol, test_name105106107@ksft_variants(_gro_variants())108def test(cfg, protocol, test_name):109"""Run a single GRO test with retries."""110111ipver = "6" if protocol[-1] == "6" else "4"112cfg.require_ipver(ipver)113114_setup(cfg, test_name)115116base_cmd_args = [117f"--{protocol}",118f"--dmac {_resolve_dmac(cfg, ipver)}",119f"--smac {cfg.remote_dev['address']}",120f"--daddr {cfg.addr_v[ipver]}",121f"--saddr {cfg.remote_addr_v[ipver]}",122f"--test {test_name}",123"--verbose"124]125base_args = " ".join(base_cmd_args)126127# Each test is run 6 times to deflake, because given the receive timing,128# not all packets that should coalesce will be considered in the same flow129# on every try.130max_retries = 6131for attempt in range(max_retries):132rx_cmd = f"{cfg.bin_local} {base_args} --rx --iface {cfg.ifname}"133tx_cmd = f"{cfg.bin_remote} {base_args} --iface {cfg.remote_ifname}"134135fail_now = attempt >= max_retries - 1136137with bkg(rx_cmd, ksft_ready=True, exit_wait=True,138fail=fail_now) as rx_proc:139cmd(tx_cmd, host=cfg.remote)140141if rx_proc.ret == 0:142return143144ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# '))145ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# '))146147if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"):148ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")149return150151ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...")152153154def main() -> None:155""" Ksft boiler plate main """156157with NetDrvEpEnv(__file__) as cfg:158ksft_run(cases=[test], args=(cfg,))159ksft_exit()160161162if __name__ == "__main__":163main()164165166