1*fdb0267dSJakub Kicinski#!/usr/bin/env python3 2*fdb0267dSJakub Kicinski# SPDX-License-Identifier: GPL-2.0 3*fdb0267dSJakub Kicinski 4*fdb0267dSJakub Kicinski""" 5*fdb0267dSJakub KicinskiGRO (Generic Receive Offload) conformance tests. 6*fdb0267dSJakub Kicinski 7*fdb0267dSJakub KicinskiValidates that GRO coalescing works correctly by running the gro 8*fdb0267dSJakub Kicinskibinary in different configurations and checking for correct packet 9*fdb0267dSJakub Kicinskicoalescing behavior. 10*fdb0267dSJakub Kicinski 11*fdb0267dSJakub KicinskiTest cases: 12*fdb0267dSJakub Kicinski - data: Data packets with same size/headers and correct seq numbers coalesce 13*fdb0267dSJakub Kicinski - ack: Pure ACK packets do not coalesce 14*fdb0267dSJakub Kicinski - flags: Packets with PSH, SYN, URG, RST flags do not coalesce 15*fdb0267dSJakub Kicinski - tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce 16*fdb0267dSJakub Kicinski - ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce 17*fdb0267dSJakub Kicinski - large: Packets larger than GRO_MAX_SIZE don't coalesce 18*fdb0267dSJakub Kicinski""" 19*fdb0267dSJakub Kicinski 20*fdb0267dSJakub Kicinskiimport os 21*fdb0267dSJakub Kicinskifrom lib.py import ksft_run, ksft_exit, ksft_pr 22*fdb0267dSJakub Kicinskifrom lib.py import NetDrvEpEnv, KsftXfailEx 23*fdb0267dSJakub Kicinskifrom lib.py import cmd, defer, bkg, ip 24*fdb0267dSJakub Kicinskifrom lib.py import ksft_variants 25*fdb0267dSJakub Kicinski 26*fdb0267dSJakub Kicinski 27*fdb0267dSJakub Kicinskidef _resolve_dmac(cfg, ipver): 28*fdb0267dSJakub Kicinski """ 29*fdb0267dSJakub Kicinski Find the destination MAC address remote host should use to send packets 30*fdb0267dSJakub Kicinski towards the local host. It may be a router / gateway address. 31*fdb0267dSJakub Kicinski """ 32*fdb0267dSJakub Kicinski 33*fdb0267dSJakub Kicinski attr = "dmac" + ipver 34*fdb0267dSJakub Kicinski # Cache the response across test cases 35*fdb0267dSJakub Kicinski if hasattr(cfg, attr): 36*fdb0267dSJakub Kicinski return getattr(cfg, attr) 37*fdb0267dSJakub Kicinski 38*fdb0267dSJakub Kicinski route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}", 39*fdb0267dSJakub Kicinski json=True, host=cfg.remote)[0] 40*fdb0267dSJakub Kicinski gw = route.get("gateway") 41*fdb0267dSJakub Kicinski # Local L2 segment, address directly 42*fdb0267dSJakub Kicinski if not gw: 43*fdb0267dSJakub Kicinski setattr(cfg, attr, cfg.dev['address']) 44*fdb0267dSJakub Kicinski return getattr(cfg, attr) 45*fdb0267dSJakub Kicinski 46*fdb0267dSJakub Kicinski # ping to make sure neighbor is resolved, 47*fdb0267dSJakub Kicinski # bind to an interface, for v6 the GW is likely link local 48*fdb0267dSJakub Kicinski cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote) 49*fdb0267dSJakub Kicinski 50*fdb0267dSJakub Kicinski neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}", 51*fdb0267dSJakub Kicinski json=True, host=cfg.remote)[0] 52*fdb0267dSJakub Kicinski setattr(cfg, attr, neigh['lladdr']) 53*fdb0267dSJakub Kicinski return getattr(cfg, attr) 54*fdb0267dSJakub Kicinski 55*fdb0267dSJakub Kicinski 56*fdb0267dSJakub Kicinskidef _write_defer_restore(cfg, path, val, defer_undo=False): 57*fdb0267dSJakub Kicinski with open(path, "r", encoding="utf-8") as fp: 58*fdb0267dSJakub Kicinski orig_val = fp.read().strip() 59*fdb0267dSJakub Kicinski if str(val) == orig_val: 60*fdb0267dSJakub Kicinski return 61*fdb0267dSJakub Kicinski with open(path, "w", encoding="utf-8") as fp: 62*fdb0267dSJakub Kicinski fp.write(val) 63*fdb0267dSJakub Kicinski if defer_undo: 64*fdb0267dSJakub Kicinski defer(_write_defer_restore, cfg, path, orig_val) 65*fdb0267dSJakub Kicinski 66*fdb0267dSJakub Kicinski 67*fdb0267dSJakub Kicinskidef _set_mtu_restore(dev, mtu, host): 68*fdb0267dSJakub Kicinski if dev['mtu'] < mtu: 69*fdb0267dSJakub Kicinski ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host) 70*fdb0267dSJakub Kicinski defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host) 71*fdb0267dSJakub Kicinski 72*fdb0267dSJakub Kicinski 73*fdb0267dSJakub Kicinskidef _setup(cfg, test_name): 74*fdb0267dSJakub Kicinski """ Setup hardware loopback mode for GRO testing. """ 75*fdb0267dSJakub Kicinski 76*fdb0267dSJakub Kicinski if not hasattr(cfg, "bin_remote"): 77*fdb0267dSJakub Kicinski cfg.bin_local = cfg.test_dir / "gro" 78*fdb0267dSJakub Kicinski cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) 79*fdb0267dSJakub Kicinski 80*fdb0267dSJakub Kicinski # "large" test needs at least 4k MTU 81*fdb0267dSJakub Kicinski if test_name == "large": 82*fdb0267dSJakub Kicinski _set_mtu_restore(cfg.dev, 4096, None) 83*fdb0267dSJakub Kicinski _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote) 84*fdb0267dSJakub Kicinski 85*fdb0267dSJakub Kicinski flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout" 86*fdb0267dSJakub Kicinski irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs" 87*fdb0267dSJakub Kicinski 88*fdb0267dSJakub Kicinski _write_defer_restore(cfg, flush_path, "200000", defer_undo=True) 89*fdb0267dSJakub Kicinski _write_defer_restore(cfg, irq_path, "10", defer_undo=True) 90*fdb0267dSJakub Kicinski 91*fdb0267dSJakub Kicinski try: 92*fdb0267dSJakub Kicinski # Disable TSO for local tests 93*fdb0267dSJakub Kicinski cfg.require_nsim() # will raise KsftXfailEx if not running on nsim 94*fdb0267dSJakub Kicinski 95*fdb0267dSJakub Kicinski cmd(f"ethtool -K {cfg.ifname} gro on tso off") 96*fdb0267dSJakub Kicinski cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote) 97*fdb0267dSJakub Kicinski except KsftXfailEx: 98*fdb0267dSJakub Kicinski pass 99*fdb0267dSJakub Kicinski 100*fdb0267dSJakub Kicinskidef _gro_variants(): 101*fdb0267dSJakub Kicinski """Generator that yields all combinations of protocol and test types.""" 102*fdb0267dSJakub Kicinski 103*fdb0267dSJakub Kicinski for protocol in ["ipv4", "ipv6", "ipip"]: 104*fdb0267dSJakub Kicinski for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]: 105*fdb0267dSJakub Kicinski yield protocol, test_name 106*fdb0267dSJakub Kicinski 107*fdb0267dSJakub Kicinski 108*fdb0267dSJakub Kicinski@ksft_variants(_gro_variants()) 109*fdb0267dSJakub Kicinskidef test(cfg, protocol, test_name): 110*fdb0267dSJakub Kicinski """Run a single GRO test with retries.""" 111*fdb0267dSJakub Kicinski 112*fdb0267dSJakub Kicinski ipver = "6" if protocol[-1] == "6" else "4" 113*fdb0267dSJakub Kicinski cfg.require_ipver(ipver) 114*fdb0267dSJakub Kicinski 115*fdb0267dSJakub Kicinski _setup(cfg, test_name) 116*fdb0267dSJakub Kicinski 117*fdb0267dSJakub Kicinski base_cmd_args = [ 118*fdb0267dSJakub Kicinski f"--{protocol}", 119*fdb0267dSJakub Kicinski f"--dmac {_resolve_dmac(cfg, ipver)}", 120*fdb0267dSJakub Kicinski f"--smac {cfg.remote_dev['address']}", 121*fdb0267dSJakub Kicinski f"--daddr {cfg.addr_v[ipver]}", 122*fdb0267dSJakub Kicinski f"--saddr {cfg.remote_addr_v[ipver]}", 123*fdb0267dSJakub Kicinski f"--test {test_name}", 124*fdb0267dSJakub Kicinski "--verbose" 125*fdb0267dSJakub Kicinski ] 126*fdb0267dSJakub Kicinski base_args = " ".join(base_cmd_args) 127*fdb0267dSJakub Kicinski 128*fdb0267dSJakub Kicinski # Each test is run 6 times to deflake, because given the receive timing, 129*fdb0267dSJakub Kicinski # not all packets that should coalesce will be considered in the same flow 130*fdb0267dSJakub Kicinski # on every try. 131*fdb0267dSJakub Kicinski max_retries = 6 132*fdb0267dSJakub Kicinski for attempt in range(max_retries): 133*fdb0267dSJakub Kicinski rx_cmd = f"{cfg.bin_local} {base_args} --rx --iface {cfg.ifname}" 134*fdb0267dSJakub Kicinski tx_cmd = f"{cfg.bin_remote} {base_args} --iface {cfg.remote_ifname}" 135*fdb0267dSJakub Kicinski 136*fdb0267dSJakub Kicinski fail_now = attempt >= max_retries - 1 137*fdb0267dSJakub Kicinski 138*fdb0267dSJakub Kicinski with bkg(rx_cmd, ksft_ready=True, exit_wait=True, 139*fdb0267dSJakub Kicinski fail=fail_now) as rx_proc: 140*fdb0267dSJakub Kicinski cmd(tx_cmd, host=cfg.remote) 141*fdb0267dSJakub Kicinski 142*fdb0267dSJakub Kicinski if rx_proc.ret == 0: 143*fdb0267dSJakub Kicinski return 144*fdb0267dSJakub Kicinski 145*fdb0267dSJakub Kicinski ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# ')) 146*fdb0267dSJakub Kicinski ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# ')) 147*fdb0267dSJakub Kicinski 148*fdb0267dSJakub Kicinski if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"): 149*fdb0267dSJakub Kicinski ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment") 150*fdb0267dSJakub Kicinski return 151*fdb0267dSJakub Kicinski 152*fdb0267dSJakub Kicinski ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...") 153*fdb0267dSJakub Kicinski 154*fdb0267dSJakub Kicinski 155*fdb0267dSJakub Kicinskidef main() -> None: 156*fdb0267dSJakub Kicinski """ Ksft boiler plate main """ 157*fdb0267dSJakub Kicinski 158*fdb0267dSJakub Kicinski with NetDrvEpEnv(__file__) as cfg: 159*fdb0267dSJakub Kicinski ksft_run(cases=[test], args=(cfg,)) 160*fdb0267dSJakub Kicinski ksft_exit() 161*fdb0267dSJakub Kicinski 162*fdb0267dSJakub Kicinski 163*fdb0267dSJakub Kicinskiif __name__ == "__main__": 164*fdb0267dSJakub Kicinski main() 165