File Index
Files and directories
Sign in
FILE

gro.py

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/drivers/net/gro.py PYTHON
  1#!/usr/bin/env python3
  2# SPDX-License-Identifier: GPL-2.0
  3
  4"""
  5GRO (Generic Receive Offload) conformance tests.
  6
  7Validates that GRO coalescing works correctly by running the gro
  8binary in different configurations and checking for correct packet
  9coalescing behavior.
 10
 11Test 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
 20import os
 21from lib.py import ksft_run, ksft_exit, ksft_pr
 22from lib.py import NetDrvEpEnv, KsftXfailEx
 23from lib.py import cmd, defer, bkg, ip
 24from lib.py import ksft_variants
 25
 26
 27def _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
 56def _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
 67def _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
 73def _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
100def _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())
109def 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
155def 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
163if __name__ == "__main__":
164    main()
~/ Read-only file preview.