Path: blob/master/tools/testing/selftests/drivers/net/gro.py
122953 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_same: Same size data packets coalesce12- data_lrg_sml: Large packet followed by smaller one coalesces13- data_sml_lrg: Small packet followed by larger one doesn't coalesce14- ack: Pure ACK packets do not coalesce15- flags_psh: Packets with PSH flag don't coalesce16- flags_syn: Packets with SYN flag don't coalesce17- flags_rst: Packets with RST flag don't coalesce18- flags_urg: Packets with URG flag don't coalesce19- flags_cwr: Packets with CWR flag don't coalesce20- tcp_csum: Packets with incorrect checksum don't coalesce21- tcp_seq: Packets with non-consecutive seqno don't coalesce22- tcp_ts: Packets with different timestamp options don't coalesce23- tcp_opt: Packets with different TCP options don't coalesce24- ip_ecn: Packets with different ECN don't coalesce25- ip_tos: Packets with different TOS don't coalesce26- ip_ttl: (IPv4) Packets with different TTL don't coalesce27- ip_opt: (IPv4) Packets with IP options don't coalesce28- ip_frag4: (IPv4) IPv4 fragments don't coalesce29- ip_id_df*: (IPv4) IP ID field coalescing tests30- ip_frag6: (IPv6) IPv6 fragments don't coalesce31- ip_v6ext_same: (IPv6) IPv6 ext header with same payload coalesces32- ip_v6ext_diff: (IPv6) IPv6 ext header with different payload doesn't coalesce33- large_max: Packets exceeding GRO_MAX_SIZE don't coalesce34- large_rem: Large packet remainder handling35"""3637import os38from lib.py import ksft_run, ksft_exit, ksft_pr39from lib.py import NetDrvEpEnv, KsftXfailEx40from lib.py import bkg, cmd, defer, ethtool, ip41from lib.py import ksft_variants424344def _resolve_dmac(cfg, ipver):45"""46Find the destination MAC address remote host should use to send packets47towards the local host. It may be a router / gateway address.48"""4950attr = "dmac" + ipver51# Cache the response across test cases52if hasattr(cfg, attr):53return getattr(cfg, attr)5455route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}",56json=True, host=cfg.remote)[0]57gw = route.get("gateway")58# Local L2 segment, address directly59if not gw:60setattr(cfg, attr, cfg.dev['address'])61return getattr(cfg, attr)6263# ping to make sure neighbor is resolved,64# bind to an interface, for v6 the GW is likely link local65cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote)6667neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}",68json=True, host=cfg.remote)[0]69setattr(cfg, attr, neigh['lladdr'])70return getattr(cfg, attr)717273def _write_defer_restore(cfg, path, val, defer_undo=False):74with open(path, "r", encoding="utf-8") as fp:75orig_val = fp.read().strip()76if str(val) == orig_val:77return78with open(path, "w", encoding="utf-8") as fp:79fp.write(val)80if defer_undo:81defer(_write_defer_restore, cfg, path, orig_val)828384def _set_mtu_restore(dev, mtu, host):85if dev['mtu'] < mtu:86ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host)87defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)888990def _set_ethtool_feat(dev, current, feats, host=None):91s2n = {True: "on", False: "off"}9293new = ["-K", dev]94old = ["-K", dev]95no_change = True96for name, state in feats.items():97new += [name, s2n[state]]98old += [name, s2n[current[name]["active"]]]99100if current[name]["active"] != state:101no_change = False102if current[name]["fixed"]:103raise KsftXfailEx(f"Device does not support {name}")104if no_change:105return106107eth_cmd = ethtool(" ".join(new), host=host)108defer(ethtool, " ".join(old), host=host)109110# If ethtool printed something kernel must have modified some features111if eth_cmd.stdout:112ksft_pr(eth_cmd)113114115def _setup(cfg, mode, test_name):116""" Setup hardware loopback mode for GRO testing. """117118if not hasattr(cfg, "bin_remote"):119cfg.bin_local = cfg.test_dir / "gro"120cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)121122if not hasattr(cfg, "feat"):123cfg.feat = ethtool(f"-k {cfg.ifname}", json=True)[0]124cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}",125host=cfg.remote, json=True)[0]126127# "large_*" tests need at least 4k MTU128if test_name.startswith("large_"):129_set_mtu_restore(cfg.dev, 4096, None)130_set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)131132if mode == "sw":133flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"134irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"135136_write_defer_restore(cfg, flush_path, "200000", defer_undo=True)137_write_defer_restore(cfg, irq_path, "10", defer_undo=True)138139_set_ethtool_feat(cfg.ifname, cfg.feat,140{"generic-receive-offload": True,141"rx-gro-hw": False,142"large-receive-offload": False})143elif mode == "hw":144_set_ethtool_feat(cfg.ifname, cfg.feat,145{"generic-receive-offload": False,146"rx-gro-hw": True,147"large-receive-offload": False})148149# Some NICs treat HW GRO as a GRO sub-feature so disabling GRO150# will also clear HW GRO. Use a hack of installing XDP generic151# to skip SW GRO, even when enabled.152feat = ethtool(f"-k {cfg.ifname}", json=True)[0]153if not feat["rx-gro-hw"]["active"]:154ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround")155prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"156ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp")157defer(ip, f"link set dev {cfg.ifname} xdpgeneric off")158159# Attaching XDP may change features, fetch the latest state160feat = ethtool(f"-k {cfg.ifname}", json=True)[0]161162_set_ethtool_feat(cfg.ifname, feat,163{"generic-receive-offload": True,164"rx-gro-hw": True,165"large-receive-offload": False})166elif mode == "lro":167# netdevsim advertises LRO for feature inheritance testing with168# bonding/team tests but it doesn't actually perform the offload169cfg.require_nsim(nsim_test=False)170171_set_ethtool_feat(cfg.ifname, cfg.feat,172{"generic-receive-offload": False,173"rx-gro-hw": False,174"large-receive-offload": True})175176try:177# Disable TSO for local tests178cfg.require_nsim() # will raise KsftXfailEx if not running on nsim179180_set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,181{"tcp-segmentation-offload": False},182host=cfg.remote)183except KsftXfailEx:184pass185186187def _gro_variants():188"""Generator that yields all combinations of protocol and test types."""189190# Tests that work for all protocols191common_tests = [192"data_same", "data_lrg_sml", "data_sml_lrg",193"ack",194"flags_psh", "flags_syn", "flags_rst", "flags_urg", "flags_cwr",195"tcp_csum", "tcp_seq", "tcp_ts", "tcp_opt",196"ip_ecn", "ip_tos",197"large_max", "large_rem",198]199200# Tests specific to IPv4201ipv4_tests = [202"ip_ttl", "ip_opt", "ip_frag4",203"ip_id_df1_inc", "ip_id_df1_fixed",204"ip_id_df0_inc", "ip_id_df0_fixed",205"ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc",206]207208# Tests specific to IPv6209ipv6_tests = [210"ip_frag6", "ip_v6ext_same", "ip_v6ext_diff",211]212213for mode in ["sw", "hw", "lro"]:214for protocol in ["ipv4", "ipv6", "ipip"]:215for test_name in common_tests:216yield mode, protocol, test_name217218if protocol in ["ipv4", "ipip"]:219for test_name in ipv4_tests:220yield mode, protocol, test_name221elif protocol == "ipv6":222for test_name in ipv6_tests:223yield mode, protocol, test_name224225226@ksft_variants(_gro_variants())227def test(cfg, mode, protocol, test_name):228"""Run a single GRO test with retries."""229230ipver = "6" if protocol[-1] == "6" else "4"231cfg.require_ipver(ipver)232233_setup(cfg, mode, test_name)234235base_cmd_args = [236f"--{protocol}",237f"--dmac {_resolve_dmac(cfg, ipver)}",238f"--smac {cfg.remote_dev['address']}",239f"--daddr {cfg.addr_v[ipver]}",240f"--saddr {cfg.remote_addr_v[ipver]}",241f"--test {test_name}",242"--verbose"243]244base_args = " ".join(base_cmd_args)245246# Each test is run 6 times to deflake, because given the receive timing,247# not all packets that should coalesce will be considered in the same flow248# on every try.249max_retries = 6250for attempt in range(max_retries):251rx_cmd = f"{cfg.bin_local} {base_args} --rx --iface {cfg.ifname}"252tx_cmd = f"{cfg.bin_remote} {base_args} --iface {cfg.remote_ifname}"253254fail_now = attempt >= max_retries - 1255256with bkg(rx_cmd, ksft_ready=True, exit_wait=True,257fail=fail_now) as rx_proc:258cmd(tx_cmd, host=cfg.remote)259260if rx_proc.ret == 0:261return262263ksft_pr(rx_proc)264265if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):266ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")267return268269ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...")270271272def main() -> None:273""" Ksft boiler plate main """274275with NetDrvEpEnv(__file__) as cfg:276ksft_run(cases=[test], args=(cfg,))277ksft_exit()278279280if __name__ == "__main__":281main()282283284