xref: /linux/tools/testing/selftests/drivers/net/gro.py (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
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