selftests: drv-net: gro: run the test against HW GRO and LRO

Run the test against HW GRO and LRO. NICs I have pass the base cases.
Interestingly all are happy to build GROs larger than 64k.

Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260113000740.255360-6-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-01-12 16:07:39 -08:00
parent 8171f6a76b
commit d3b35898de
2 changed files with 58 additions and 17 deletions

View File

@ -87,11 +87,15 @@ def _set_ethtool_feat(dev, current, feats, host=None):
if no_change:
return
ethtool(" ".join(new), host=host)
eth_cmd = ethtool(" ".join(new), host=host)
defer(ethtool, " ".join(old), host=host)
# If ethtool printed something kernel must have modified some features
if eth_cmd.stdout:
ksft_pr(eth_cmd)
def _setup(cfg, test_name):
def _setup(cfg, mode, test_name):
""" Setup hardware loopback mode for GRO testing. """
if not hasattr(cfg, "bin_remote"):
@ -108,16 +112,49 @@ def _setup(cfg, test_name):
_set_mtu_restore(cfg.dev, 4096, None)
_set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
if mode == "sw":
flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
_write_defer_restore(cfg, flush_path, "200000", defer_undo=True)
_write_defer_restore(cfg, irq_path, "10", defer_undo=True)
_write_defer_restore(cfg, flush_path, "200000", defer_undo=True)
_write_defer_restore(cfg, irq_path, "10", defer_undo=True)
_set_ethtool_feat(cfg.ifname, cfg.feat,
{"generic-receive-offload": True,
"rx-gro-hw": False,
"large-receive-offload": False})
_set_ethtool_feat(cfg.ifname, cfg.feat,
{"generic-receive-offload": True,
"rx-gro-hw": False,
"large-receive-offload": False})
elif mode == "hw":
_set_ethtool_feat(cfg.ifname, cfg.feat,
{"generic-receive-offload": False,
"rx-gro-hw": True,
"large-receive-offload": False})
# Some NICs treat HW GRO as a GRO sub-feature so disabling GRO
# will also clear HW GRO. Use a hack of installing XDP generic
# to skip SW GRO, even when enabled.
feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
if not feat["rx-gro-hw"]["active"]:
ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround")
prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp")
defer(ip, f"link set dev {cfg.ifname} xdpgeneric off")
# Attaching XDP may change features, fetch the latest state
feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
_set_ethtool_feat(cfg.ifname, feat,
{"generic-receive-offload": True,
"rx-gro-hw": True,
"large-receive-offload": False})
elif mode == "lro":
# netdevsim advertises LRO for feature inheritance testing with
# bonding/team tests but it doesn't actually perform the offload
cfg.require_nsim(nsim_test=False)
_set_ethtool_feat(cfg.ifname, cfg.feat,
{"generic-receive-offload": False,
"rx-gro-hw": False,
"large-receive-offload": True})
try:
# Disable TSO for local tests
@ -133,19 +170,20 @@ def _setup(cfg, test_name):
def _gro_variants():
"""Generator that yields all combinations of protocol and test types."""
for protocol in ["ipv4", "ipv6", "ipip"]:
for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]:
yield protocol, test_name
for mode in ["sw", "hw", "lro"]:
for protocol in ["ipv4", "ipv6", "ipip"]:
for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]:
yield mode, protocol, test_name
@ksft_variants(_gro_variants())
def test(cfg, protocol, test_name):
def test(cfg, mode, protocol, test_name):
"""Run a single GRO test with retries."""
ipver = "6" if protocol[-1] == "6" else "4"
cfg.require_ipver(ipver)
_setup(cfg, test_name)
_setup(cfg, mode, test_name)
base_cmd_args = [
f"--{protocol}",

View File

@ -248,9 +248,12 @@ class NetDrvEpEnv(NetDrvEnvBase):
if not self.addr_v[ipver] or not self.remote_addr_v[ipver]:
raise KsftSkipEx(f"Test requires IPv{ipver} connectivity")
def require_nsim(self):
if self._ns is None:
def require_nsim(self, nsim_test=True):
"""Require or exclude netdevsim for this test"""
if nsim_test and self._ns is None:
raise KsftXfailEx("Test only works on netdevsim")
if nsim_test is False and self._ns is not None:
raise KsftXfailEx("Test does not work on netdevsim")
def _require_cmd(self, comm, key, host=None):
cached = self._required_cmd.get(comm, {})