* [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing
@ 2026-01-10 0:51 Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety Jakub Kicinski
` (5 more replies)
0 siblings, 6 replies; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
Add support for running our existing GRO test against HW GRO
and LRO implementation. The first 3 patches are just ksft lib
nice-to-haves, and patch 4 cleans up the existing gro Python.
Patches 5 and 6 are of most practical interest. The support
reconfiguring the NIC to disable SW GRO and enable HW GRO and LRO.
Additionally last patch breaks up the existing GRO cases to
track HW compliance at finer granularity.
v2:
- fix restoring all features
- apply the generic XDP hack selectively (print a msg when it happens)
- a lot of small tweaks and 4 extra patches
v1: https://lore.kernel.org/20251128005242.2604732-1-kuba@kernel.org
Jakub Kicinski (6):
selftests: net: py: teach ksft_pr() multi-line safety
selftests: net: py: teach cmd() how to print itself
selftests: drv-net: gro: use cmd print
selftests: drv-net: gro: improve feature config
selftests: drv-net: gro: run the test against HW GRO and LRO
selftests: drv-net: gro: break out all individual test cases
tools/testing/selftests/drivers/net/gro.c | 399 +++++++++++---------
tools/testing/selftests/drivers/net/gro.py | 158 ++++++--
tools/testing/selftests/net/lib/py/ksft.py | 29 +-
tools/testing/selftests/net/lib/py/utils.py | 23 ++
4 files changed, 406 insertions(+), 203 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself Jakub Kicinski
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
Make printing multi-line logs easier by automatically prefixing
each line in ksft_pr(). Make use of this when formatting exceptions.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/testing/selftests/net/lib/py/ksft.py | 29 ++++++++++++++--------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/net/lib/py/ksft.py b/tools/testing/selftests/net/lib/py/ksft.py
index 531e7fa1b3ea..4ca21d829b1c 100644
--- a/tools/testing/selftests/net/lib/py/ksft.py
+++ b/tools/testing/selftests/net/lib/py/ksft.py
@@ -32,8 +32,23 @@ KSFT_DISRUPTIVE = True
def ksft_pr(*objs, **kwargs):
+ """
+ Print logs to stdout.
+
+ Behaves like print() but log lines will be prefixed
+ with # to prevent breaking the TAP output formatting.
+
+ Extra arguments (on top of what print() supports):
+ line_pfx - add extra string before each line
+ """
+ sep = kwargs.pop("sep", " ")
+ pfx = kwargs.pop("line_pfx", "")
+ pfx = "#" + (" " + pfx if pfx else "")
kwargs["flush"] = True
- print("#", *objs, **kwargs)
+
+ text = sep.join(str(obj) for obj in objs)
+ prefixed = f"\n{pfx} ".join(text.split('\n'))
+ print(pfx, prefixed, **kwargs)
def _fail(*args):
@@ -165,9 +180,7 @@ KSFT_DISRUPTIVE = True
entry.exec_only()
except Exception:
ksft_pr(f"Exception while handling defer / cleanup (callback {i} of {qlen_start})!")
- tb = traceback.format_exc()
- for line in tb.strip().split('\n'):
- ksft_pr("Defer Exception|", line)
+ ksft_pr(traceback.format_exc(), line_pfx="Defer Exception|")
KSFT_RESULT = False
@@ -325,9 +338,7 @@ KsftCaseFunction = namedtuple("KsftCaseFunction",
cnt_key = 'xfail'
except BaseException as e:
stop |= isinstance(e, KeyboardInterrupt)
- tb = traceback.format_exc()
- for line in tb.strip().split('\n'):
- ksft_pr("Exception|", line)
+ ksft_pr(traceback.format_exc(), line_pfx="Exception|")
if stop:
ksft_pr(f"Stopping tests due to {type(e).__name__}.")
KSFT_RESULT = False
@@ -336,9 +347,7 @@ KsftCaseFunction = namedtuple("KsftCaseFunction",
try:
ksft_flush_defer()
except BaseException as e:
- tb = traceback.format_exc()
- for line in tb.strip().split('\n'):
- ksft_pr("Exception|", line)
+ ksft_pr(traceback.format_exc(), line_pfx="Exception|")
if isinstance(e, KeyboardInterrupt):
ksft_pr()
ksft_pr("WARN: defer() interrupted, cleanup may be incomplete.")
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print Jakub Kicinski
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
Teach cmd() how to print itself, to make debug prints easier.
Example output (leading # due to ksft_pr()):
# CMD: /root/ksft-net-drv/drivers/net/gro
# EXIT: 1
# STDOUT: ipv6 with ext header does coalesce:
# STDERR: Expected {200 }, Total 1 packets
# Received {100 [!=200]100 [!=0]}, Total 2 packets.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/testing/selftests/net/lib/py/utils.py | 23 +++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 106ee1f2df86..881a17fbb5fd 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -41,7 +41,9 @@ import time
self.ret = None
self.ksft_term_fd = None
+ self.host = host
self.comm = comm
+
if host:
self.proc = host.cmd(comm)
else:
@@ -99,6 +101,27 @@ import time
raise CmdExitFailure("Command failed: %s\nSTDOUT: %s\nSTDERR: %s" %
(self.proc.args, stdout, stderr), self)
+ def __repr__(self):
+ def str_fmt(name, s):
+ name += ': '
+ return (name + s.strip().replace('\n', '\n' + ' ' * len(name)))
+
+ ret = "CMD"
+ if self.host:
+ ret += "[remote]"
+ if self.ret is None:
+ ret += f" (unterminated): {self.comm}\n"
+ elif self.ret == 0:
+ ret += f" (success): {self.comm}\n"
+ else:
+ ret += f": {self.comm}\n"
+ ret += f" EXIT: {self.ret}\n"
+ if self.stdout:
+ ret += str_fmt(" STDOUT", self.stdout) + "\n"
+ if self.stderr:
+ ret += str_fmt(" STDERR", self.stderr) + "\n"
+ return ret.strip()
+
class bkg(cmd):
"""
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config Jakub Kicinski
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
Now that cmd() can be printed directly remove the old formatting.
Before:
# fragmented ip6 doesn't coalesce:
# Expected {200 100 100 }, Total 3 packets
# Received {200 100 }, Total 2 packets.
# /root/ksft-net-drv/drivers/net/gro: incorrect number of packets
Now:
# CMD: drivers/net/gro --ipv6 --dmac 9e:[...]
# EXIT: 1
# STDOUT: fragmented ip6 doesn't coalesce:
# STDERR: Expected {200 100 100 }, Total 3 packets
# Received {200 100 }, Total 2 packets.
# /root/ksft-net-drv/drivers/net/gro: incorrect number of packets
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/testing/selftests/drivers/net/gro.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index ba83713bf7b5..4e0fb19d1527 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -142,8 +142,7 @@ from lib.py import ksft_variants
if rx_proc.ret == 0:
return
- ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# '))
- ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# '))
+ ksft_pr(rx_proc)
if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"):
ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
` (2 preceding siblings ...)
2026-01-10 0:51 ` [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-10 17:43 ` Jakub Kicinski
2026-01-11 17:08 ` Willem de Bruijn
2026-01-10 0:51 ` [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases Jakub Kicinski
5 siblings, 2 replies; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski,
willemdebruijn.kernel
We'll need to do a lot more feature handling to test HW-GRO and LRO.
Clean up the feature handling for SW GRO a bit to let the next commit
focus on the new test cases, only.
Make sure HW GRO-like features are not enabled for the SW tests.
Be more careful about changing features as "nothing changed"
situations may result in non-zero error code from ethtool.
Don't disable TSO on the local interface (receiver) when running over
netdevsim, we just want GSO to break up the segments on the sender.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- reoder the imports
- fix restoring all features - don't assume all the features
have to be flipped, some may have already been in desired state
v1: https://lore.kernel.org/20251128005242.2604732-1-kuba@kernel.org
CC: willemdebruijn.kernel@gmail.com
CC: shuah@kernel.org
CC: linux-kselftest@vger.kernel.org
---
tools/testing/selftests/drivers/net/gro.py | 38 ++++++++++++++++++++--
1 file changed, 35 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index 4e0fb19d1527..b72a1d756d72 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -20,7 +20,7 @@ coalescing behavior.
import os
from lib.py import ksft_run, ksft_exit, ksft_pr
from lib.py import NetDrvEpEnv, KsftXfailEx
-from lib.py import cmd, defer, bkg, ip
+from lib.py import bkg, cmd, defer, ethtool, ip
from lib.py import ksft_variants
@@ -70,6 +70,27 @@ from lib.py import ksft_variants
defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
+def _set_ethtool_feat(dev, current, feats, host=None):
+ s2n = {True: "on", False: "off"}
+
+ new = ["-K", dev]
+ old = ["-K", dev]
+ no_change = True
+ for name, state in feats.items():
+ new += [name, s2n[state]]
+ old += [name, s2n[current[name]["active"]]]
+
+ if current[name]["active"] != state:
+ no_change = False
+ if current[name]["fixed"]:
+ raise KsftXfailEx(f"Device does not support {name}")
+ if no_change:
+ return
+
+ ethtool(" ".join(new), host=host)
+ defer(ethtool, " ".join(old), host=host)
+
+
def _setup(cfg, test_name):
""" Setup hardware loopback mode for GRO testing. """
@@ -77,6 +98,11 @@ from lib.py import ksft_variants
cfg.bin_local = cfg.test_dir / "gro"
cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
+ if not hasattr(cfg, "feat"):
+ cfg.feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+ cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}",
+ host=cfg.remote, json=True)[0]
+
# "large" test needs at least 4k MTU
if test_name == "large":
_set_mtu_restore(cfg.dev, 4096, None)
@@ -88,15 +114,21 @@ from lib.py import ksft_variants
_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})
+
try:
# Disable TSO for local tests
cfg.require_nsim() # will raise KsftXfailEx if not running on nsim
- cmd(f"ethtool -K {cfg.ifname} gro on tso off")
- cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote)
+ _set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat, {"tso": False},
+ host=cfg.remote)
except KsftXfailEx:
pass
+
def _gro_variants():
"""Generator that yields all combinations of protocol and test types."""
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
` (3 preceding siblings ...)
2026-01-10 0:51 ` [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-11 17:08 ` Willem de Bruijn
2026-01-10 0:51 ` [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases Jakub Kicinski
5 siblings, 1 reply; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski,
willemdebruijn.kernel
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.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- apply the generic XDP hack selectively (print a msg when it happens)
- print ethtool -K (setting) output if any, output means that
kernel reported that something didn't go according to plan
(features we wanted were not actually enabled)
v1: https://lore.kernel.org/20251128005242.2604732-2-kuba@kernel.org
CC: willemdebruijn.kernel@gmail.com
CC: shuah@kernel.org
CC: sdf@fomichev.me
CC: linux-kselftest@vger.kernel.org
---
tools/testing/selftests/drivers/net/gro.py | 64 +++++++++++++++++-----
1 file changed, 49 insertions(+), 15 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index b72a1d756d72..112560482d04 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -87,11 +87,15 @@ from lib.py import ksft_variants
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,45 @@ from lib.py import ksft_variants
_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":
+ _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
@@ -132,19 +165,20 @@ from lib.py import ksft_variants
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}",
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
` (4 preceding siblings ...)
2026-01-10 0:51 ` [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO Jakub Kicinski
@ 2026-01-10 0:51 ` Jakub Kicinski
2026-01-11 17:12 ` Willem de Bruijn
5 siblings, 1 reply; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 0:51 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
GRO test groups the cases into categories, e.g. "tcp" case
checks coalescing in presence of:
- packets with bad csum,
- sequence number mismatch,
- timestamp option value mismatch,
- different TCP options.
Since we now have TAP support grouping the cases like that
lowers our reporting granularity. This matters even more for
NICs performing HW GRO and LRO since it appears that most
implementation have _some_ bugs. Flagging the whole group
of tests as failed prevents us from catching regressions
in the things that work today.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/testing/selftests/drivers/net/gro.c | 399 ++++++++++++---------
tools/testing/selftests/drivers/net/gro.py | 65 +++-
2 files changed, 285 insertions(+), 179 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/gro.c b/tools/testing/selftests/drivers/net/gro.c
index e894037d2e3e..a5838de97ba8 100644
--- a/tools/testing/selftests/drivers/net/gro.c
+++ b/tools/testing/selftests/drivers/net/gro.c
@@ -3,26 +3,45 @@
* This testsuite provides conformance testing for GRO coalescing.
*
* Test cases:
- * 1.data
+ *
+ * data_*:
* Data packets of the same size and same header setup with correct
* sequence numbers coalesce. The one exception being the last data
* packet coalesced: it can be smaller than the rest and coalesced
* as long as it is in the same flow.
- * 2.ack
+ * - data_same: same size packets coalesce
+ * - data_lrg_sml: large then small coalesces
+ * - data_sml_lrg: small then large doesn't coalesce
+ *
+ * ack:
* Pure ACK does not coalesce.
- * 3.flags
- * Specific test cases: no packets with PSH, SYN, URG, RST set will
- * be coalesced.
- * 4.tcp
+ *
+ * flags_*:
+ * No packets with PSH, SYN, URG, RST set will be coalesced.
+ * - flags_psh, flags_syn, flags_rst, flags_urg
+ *
+ * tcp_*:
* Packets with incorrect checksum, non-consecutive seqno and
* different TCP header options shouldn't coalesce. Nit: given that
* some extension headers have paddings, such as timestamp, headers
- * that are padding differently would not be coalesced.
- * 5.ip:
- * Packets with different (ECN, TTL, TOS) header, ip options or
- * ip fragments (ipv6) shouldn't coalesce.
- * 6.large:
+ * that are padded differently would not be coalesced.
+ * - tcp_csum: incorrect checksum
+ * - tcp_seq: non-consecutive sequence numbers
+ * - tcp_ts: different timestamps
+ * - tcp_opt: different TCP options
+ *
+ * ip_*:
+ * Packets with different (ECN, TTL, TOS) header, IP options or
+ * IP fragments shouldn't coalesce.
+ * - ip_ecn, ip_tos: shared between IPv4/IPv6
+ * - ip_ttl, ip_opt, ip_frag4: IPv4 only
+ * - ip_id_df*: IPv4 IP ID field coalescing tests
+ * - ip_frag6, ip_v6ext_*: IPv6 only
+ *
+ * large_*:
* Packets larger than GRO_MAX_SIZE packets shouldn't coalesce.
+ * - large_max: exceeding max size
+ * - large_rem: remainder handling
*
* MSS is defined as 4096 - header because if it is too small
* (i.e. 1500 MTU - header), it will result in many packets,
@@ -95,7 +114,6 @@ static int tcp_offset = -1;
static int total_hdr_len = -1;
static int ethhdr_proto = -1;
static bool ipip;
-static const int num_flush_id_cases = 6;
static void vlog(const char *fmt, ...)
{
@@ -127,19 +145,19 @@ static void setup_sock_filter(int fd)
/* Overridden later if exthdrs are used: */
opt_ipproto_off = ipproto_off;
- if (strcmp(testname, "ip") == 0) {
- if (proto == PF_INET)
- optlen = sizeof(struct ip_timestamp);
- else {
- BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE);
- BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE);
- BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE);
+ if (strcmp(testname, "ip_opt") == 0) {
+ optlen = sizeof(struct ip_timestamp);
+ } else if (strcmp(testname, "ip_frag6") == 0 ||
+ strcmp(testname, "ip_v6ext_same") == 0 ||
+ strcmp(testname, "ip_v6ext_diff") == 0) {
+ BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE);
+ BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE);
+ BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE);
- /* same size for HBH and Fragment extension header types */
- optlen = MIN_EXTHDR_SIZE;
- opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr)
- + offsetof(struct ip6_ext, ip6e_nxt);
- }
+ /* same size for HBH and Fragment extension header types */
+ optlen = MIN_EXTHDR_SIZE;
+ opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr)
+ + offsetof(struct ip6_ext, ip6e_nxt);
}
/* this filter validates the following:
@@ -739,16 +757,6 @@ static void send_flush_id_case(int fd, struct sockaddr_ll *daddr, int tcase)
}
}
-static void test_flush_id(int fd, struct sockaddr_ll *daddr, char *fin_pkt)
-{
- for (int i = 0; i < num_flush_id_cases; i++) {
- sleep(1);
- send_flush_id_case(fd, daddr, i);
- sleep(1);
- write_packet(fd, fin_pkt, total_hdr_len, daddr);
- }
-}
-
static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr, char *ext_data1, char *ext_data2)
{
static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
@@ -1008,108 +1016,128 @@ static void gro_sender(void)
daddr.sll_halen = ETH_ALEN;
create_packet(fin_pkt, PAYLOAD_LEN * 2, 0, 0, 1);
- if (strcmp(testname, "data") == 0) {
+ /* data sub-tests */
+ if (strcmp(testname, "data_same") == 0) {
send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "data_lrg_sml") == 0) {
send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "data_sml_lrg") == 0) {
send_data_pkts(txfd, &daddr, PAYLOAD_LEN / 2, PAYLOAD_LEN);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+
+ /* ack test */
} else if (strcmp(testname, "ack") == 0) {
send_ack(txfd, &daddr);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- } else if (strcmp(testname, "flags") == 0) {
+
+ /* flags sub-tests */
+ } else if (strcmp(testname, "flags_psh") == 0) {
send_flags(txfd, &daddr, 1, 0, 0, 0);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "flags_syn") == 0) {
send_flags(txfd, &daddr, 0, 1, 0, 0);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "flags_rst") == 0) {
send_flags(txfd, &daddr, 0, 0, 1, 0);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "flags_urg") == 0) {
send_flags(txfd, &daddr, 0, 0, 0, 1);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- } else if (strcmp(testname, "tcp") == 0) {
+
+ /* tcp sub-tests */
+ } else if (strcmp(testname, "tcp_csum") == 0) {
send_changed_checksum(txfd, &daddr);
- /* Adding sleep before sending FIN so that it is not
- * received prior to other packets.
- */
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "tcp_seq") == 0) {
send_changed_seq(txfd, &daddr);
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "tcp_ts") == 0) {
send_changed_ts(txfd, &daddr);
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "tcp_opt") == 0) {
send_diff_opt(txfd, &daddr);
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- } else if (strcmp(testname, "ip") == 0) {
+
+ /* ip sub-tests - shared between IPv4 and IPv6 */
+ } else if (strcmp(testname, "ip_ecn") == 0) {
send_changed_ECN(txfd, &daddr);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
+ } else if (strcmp(testname, "ip_tos") == 0) {
send_changed_tos(txfd, &daddr);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- if (proto == PF_INET) {
- /* Modified packets may be received out of order.
- * Sleep function added to enforce test boundaries
- * so that fin pkts are not received prior to other pkts.
- */
- sleep(1);
- send_changed_ttl(txfd, &daddr);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- sleep(1);
- send_ip_options(txfd, &daddr);
- sleep(1);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ /* ip sub-tests - IPv4 only */
+ } else if (strcmp(testname, "ip_ttl") == 0) {
+ send_changed_ttl(txfd, &daddr);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_opt") == 0) {
+ send_ip_options(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_frag4") == 0) {
+ send_fragment4(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df1_inc") == 0) {
+ send_flush_id_case(txfd, &daddr, 0);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df1_fixed") == 0) {
+ send_flush_id_case(txfd, &daddr, 1);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df0_inc") == 0) {
+ send_flush_id_case(txfd, &daddr, 2);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df0_fixed") == 0) {
+ send_flush_id_case(txfd, &daddr, 3);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {
+ send_flush_id_case(txfd, &daddr, 4);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {
+ send_flush_id_case(txfd, &daddr, 5);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- sleep(1);
- send_fragment4(txfd, &daddr);
- sleep(1);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ /* ip sub-tests - IPv6 only */
+ } else if (strcmp(testname, "ip_frag6") == 0) {
+ send_fragment6(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_v6ext_same") == 0) {
+ send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "ip_v6ext_diff") == 0) {
+ send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- test_flush_id(txfd, &daddr, fin_pkt);
- } else if (proto == PF_INET6) {
- sleep(1);
- send_fragment6(txfd, &daddr);
- sleep(1);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
- sleep(1);
- /* send IPv6 packets with ext header with same payload */
- send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);
- sleep(1);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
-
- sleep(1);
- /* send IPv6 packets with ext header with different payload */
- send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);
- sleep(1);
- write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
- }
- } else if (strcmp(testname, "large") == 0) {
- /* 20 is the difference between min iphdr size
- * and min ipv6hdr size. Like MAX_HDR_SIZE,
- * MAX_PAYLOAD is defined with the larger header of the two.
- */
+ /* large sub-tests */
+ } else if (strcmp(testname, "large_max") == 0) {
int offset = (proto == PF_INET && !ipip) ? 20 : 0;
int remainder = (MAX_PAYLOAD + offset) % MSS;
send_large(txfd, &daddr, remainder);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "large_rem") == 0) {
+ int offset = (proto == PF_INET && !ipip) ? 20 : 0;
+ int remainder = (MAX_PAYLOAD + offset) % MSS;
send_large(txfd, &daddr, remainder + 1);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
} else {
- error(1, 0, "Unknown testcase");
+ error(1, 0, "Unknown testcase: %s", testname);
}
if (close(txfd))
@@ -1132,126 +1160,153 @@ static void gro_receiver(void)
memset(correct_payload, 0, sizeof(correct_payload));
- if (strcmp(testname, "data") == 0) {
+ /* data sub-tests */
+ if (strcmp(testname, "data_same") == 0) {
printf("pure data packet of same size: ");
correct_payload[0] = PAYLOAD_LEN * 2;
check_recv_pkts(rxfd, correct_payload, 1);
-
+ } else if (strcmp(testname, "data_lrg_sml") == 0) {
printf("large data packets followed by a smaller one: ");
correct_payload[0] = PAYLOAD_LEN * 1.5;
check_recv_pkts(rxfd, correct_payload, 1);
-
+ } else if (strcmp(testname, "data_sml_lrg") == 0) {
printf("small data packets followed by a larger one: ");
correct_payload[0] = PAYLOAD_LEN / 2;
correct_payload[1] = PAYLOAD_LEN;
check_recv_pkts(rxfd, correct_payload, 2);
+
+ /* ack test */
} else if (strcmp(testname, "ack") == 0) {
printf("duplicate ack and pure ack: ");
check_recv_pkts(rxfd, correct_payload, 3);
- } else if (strcmp(testname, "flags") == 0) {
+
+ /* flags sub-tests */
+ } else if (strcmp(testname, "flags_psh") == 0) {
correct_payload[0] = PAYLOAD_LEN * 3;
correct_payload[1] = PAYLOAD_LEN * 2;
-
printf("psh flag ends coalescing: ");
check_recv_pkts(rxfd, correct_payload, 2);
-
+ } else if (strcmp(testname, "flags_syn") == 0) {
correct_payload[0] = PAYLOAD_LEN * 2;
correct_payload[1] = 0;
correct_payload[2] = PAYLOAD_LEN * 2;
printf("syn flag ends coalescing: ");
check_recv_pkts(rxfd, correct_payload, 3);
-
+ } else if (strcmp(testname, "flags_rst") == 0) {
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = 0;
+ correct_payload[2] = PAYLOAD_LEN * 2;
printf("rst flag ends coalescing: ");
check_recv_pkts(rxfd, correct_payload, 3);
-
+ } else if (strcmp(testname, "flags_urg") == 0) {
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = 0;
+ correct_payload[2] = PAYLOAD_LEN * 2;
printf("urg flag ends coalescing: ");
check_recv_pkts(rxfd, correct_payload, 3);
- } else if (strcmp(testname, "tcp") == 0) {
+
+ /* tcp sub-tests */
+ } else if (strcmp(testname, "tcp_csum") == 0) {
correct_payload[0] = PAYLOAD_LEN;
correct_payload[1] = PAYLOAD_LEN;
+ printf("changed checksum does not coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "tcp_seq") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("Wrong Seq number doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "tcp_ts") == 0) {
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = PAYLOAD_LEN;
correct_payload[2] = PAYLOAD_LEN;
correct_payload[3] = PAYLOAD_LEN;
-
- printf("changed checksum does not coalesce: ");
- check_recv_pkts(rxfd, correct_payload, 2);
-
- printf("Wrong Seq number doesn't coalesce: ");
- check_recv_pkts(rxfd, correct_payload, 2);
-
printf("Different timestamp doesn't coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
check_recv_pkts(rxfd, correct_payload, 4);
-
- printf("Different options doesn't coalesce: ");
+ } else if (strcmp(testname, "tcp_opt") == 0) {
correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("Different options doesn't coalesce: ");
check_recv_pkts(rxfd, correct_payload, 2);
- } else if (strcmp(testname, "ip") == 0) {
+
+ /* ip sub-tests - shared between IPv4 and IPv6 */
+ } else if (strcmp(testname, "ip_ecn") == 0) {
correct_payload[0] = PAYLOAD_LEN;
correct_payload[1] = PAYLOAD_LEN;
-
printf("different ECN doesn't coalesce: ");
check_recv_pkts(rxfd, correct_payload, 2);
-
+ } else if (strcmp(testname, "ip_tos") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
printf("different tos doesn't coalesce: ");
check_recv_pkts(rxfd, correct_payload, 2);
- if (proto == PF_INET) {
- printf("different ttl doesn't coalesce: ");
- check_recv_pkts(rxfd, correct_payload, 2);
+ /* ip sub-tests - IPv4 only */
+ } else if (strcmp(testname, "ip_ttl") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("different ttl doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "ip_opt") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ correct_payload[2] = PAYLOAD_LEN;
+ printf("ip options doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 3);
+ } else if (strcmp(testname, "ip_frag4") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("fragmented ip4 doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "ip_id_df1_inc") == 0) {
+ printf("DF=1, Incrementing - should coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "ip_id_df1_fixed") == 0) {
+ printf("DF=1, Fixed - should coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "ip_id_df0_inc") == 0) {
+ printf("DF=0, Incrementing - should coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "ip_id_df0_fixed") == 0) {
+ printf("DF=0, Fixed - should coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {
+ printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = PAYLOAD_LEN;
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {
+ printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = PAYLOAD_LEN;
+ check_recv_pkts(rxfd, correct_payload, 2);
- printf("ip options doesn't coalesce: ");
- correct_payload[2] = PAYLOAD_LEN;
- check_recv_pkts(rxfd, correct_payload, 3);
+ /* ip sub-tests - IPv6 only */
+ } else if (strcmp(testname, "ip_frag6") == 0) {
+ /* GRO doesn't check for ipv6 hop limit when flushing.
+ * Hence no corresponding test to the ipv4 case.
+ */
+ printf("fragmented ip6 doesn't coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ correct_payload[1] = PAYLOAD_LEN;
+ correct_payload[2] = PAYLOAD_LEN;
+ check_recv_pkts(rxfd, correct_payload, 3);
+ } else if (strcmp(testname, "ip_v6ext_same") == 0) {
+ printf("ipv6 with ext header does coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN * 2;
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "ip_v6ext_diff") == 0) {
+ printf("ipv6 with ext header with different payloads doesn't coalesce: ");
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ check_recv_pkts(rxfd, correct_payload, 2);
- printf("fragmented ip4 doesn't coalesce: ");
- check_recv_pkts(rxfd, correct_payload, 2);
-
- /* is_atomic checks */
- printf("DF=1, Incrementing - should coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- check_recv_pkts(rxfd, correct_payload, 1);
-
- printf("DF=1, Fixed - should coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- check_recv_pkts(rxfd, correct_payload, 1);
-
- printf("DF=0, Incrementing - should coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- check_recv_pkts(rxfd, correct_payload, 1);
-
- printf("DF=0, Fixed - should coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- check_recv_pkts(rxfd, correct_payload, 1);
-
- printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- correct_payload[1] = PAYLOAD_LEN;
- check_recv_pkts(rxfd, correct_payload, 2);
-
- printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- correct_payload[1] = PAYLOAD_LEN;
- check_recv_pkts(rxfd, correct_payload, 2);
- } else if (proto == PF_INET6) {
- /* GRO doesn't check for ipv6 hop limit when flushing.
- * Hence no corresponding test to the ipv4 case.
- */
- printf("fragmented ip6 doesn't coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- correct_payload[1] = PAYLOAD_LEN;
- correct_payload[2] = PAYLOAD_LEN;
- check_recv_pkts(rxfd, correct_payload, 3);
-
- printf("ipv6 with ext header does coalesce: ");
- correct_payload[0] = PAYLOAD_LEN * 2;
- check_recv_pkts(rxfd, correct_payload, 1);
-
- printf("ipv6 with ext header with different payloads doesn't coalesce: ");
- correct_payload[0] = PAYLOAD_LEN;
- correct_payload[1] = PAYLOAD_LEN;
- check_recv_pkts(rxfd, correct_payload, 2);
- }
- } else if (strcmp(testname, "large") == 0) {
+ /* large sub-tests */
+ } else if (strcmp(testname, "large_max") == 0) {
int offset = (proto == PF_INET && !ipip) ? 20 : 0;
int remainder = (MAX_PAYLOAD + offset) % MSS;
@@ -1259,14 +1314,18 @@ static void gro_receiver(void)
correct_payload[1] = remainder;
printf("Shouldn't coalesce if exceed IP max pkt size: ");
check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "large_rem") == 0) {
+ int offset = (proto == PF_INET && !ipip) ? 20 : 0;
+ int remainder = (MAX_PAYLOAD + offset) % MSS;
/* last segment sent individually, doesn't start new segment */
- correct_payload[0] = correct_payload[0] - remainder;
+ correct_payload[0] = (MAX_PAYLOAD + offset) - remainder;
correct_payload[1] = remainder + 1;
correct_payload[2] = remainder + 1;
+ printf("last segment sent individually: ");
check_recv_pkts(rxfd, correct_payload, 3);
} else {
- error(1, 0, "Test case error, should never trigger");
+ error(1, 0, "Test case error: unknown testname %s", testname);
}
if (close(rxfd))
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index 112560482d04..08b22d7857bd 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -9,12 +9,29 @@ binary in different configurations and checking for correct packet
coalescing behavior.
Test cases:
- - data: Data packets with same size/headers and correct seq numbers coalesce
+ - data_same: Same size data packets coalesce
+ - data_lrg_sml: Large packet followed by smaller one coalesces
+ - data_sml_lrg: Small packet followed by larger one doesn't coalesce
- ack: Pure ACK packets do not coalesce
- - flags: Packets with PSH, SYN, URG, RST flags do not coalesce
- - tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce
- - ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce
- - large: Packets larger than GRO_MAX_SIZE don't coalesce
+ - flags_psh: Packets with PSH flag don't coalesce
+ - flags_syn: Packets with SYN flag don't coalesce
+ - flags_rst: Packets with RST flag don't coalesce
+ - flags_urg: Packets with URG flag don't coalesce
+ - tcp_csum: Packets with incorrect checksum don't coalesce
+ - tcp_seq: Packets with non-consecutive seqno don't coalesce
+ - tcp_ts: Packets with different timestamp options don't coalesce
+ - tcp_opt: Packets with different TCP options don't coalesce
+ - ip_ecn: Packets with different ECN don't coalesce
+ - ip_tos: Packets with different TOS don't coalesce
+ - ip_ttl: (IPv4) Packets with different TTL don't coalesce
+ - ip_opt: (IPv4) Packets with IP options don't coalesce
+ - ip_frag4: (IPv4) IPv4 fragments don't coalesce
+ - ip_id_df*: (IPv4) IP ID field coalescing tests
+ - ip_frag6: (IPv6) IPv6 fragments don't coalesce
+ - ip_v6ext_same: (IPv6) IPv6 ext header with same payload coalesces
+ - ip_v6ext_diff: (IPv6) IPv6 ext header with different payload doesn't coalesce
+ - large_max: Packets exceeding GRO_MAX_SIZE don't coalesce
+ - large_rem: Large packet remainder handling
"""
import os
@@ -107,8 +124,8 @@ from lib.py import ksft_variants
cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}",
host=cfg.remote, json=True)[0]
- # "large" test needs at least 4k MTU
- if test_name == "large":
+ # "large_*" tests need at least 4k MTU
+ if test_name.startswith("large_"):
_set_mtu_restore(cfg.dev, 4096, None)
_set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
@@ -165,11 +182,41 @@ from lib.py import ksft_variants
def _gro_variants():
"""Generator that yields all combinations of protocol and test types."""
+ # Tests that work for all protocols
+ common_tests = [
+ "data_same", "data_lrg_sml", "data_sml_lrg",
+ "ack",
+ "flags_psh", "flags_syn", "flags_rst", "flags_urg",
+ "tcp_csum", "tcp_seq", "tcp_ts", "tcp_opt",
+ "ip_ecn", "ip_tos",
+ "large_max", "large_rem",
+ ]
+
+ # Tests specific to IPv4
+ ipv4_tests = [
+ "ip_ttl", "ip_opt", "ip_frag4",
+ "ip_id_df1_inc", "ip_id_df1_fixed",
+ "ip_id_df0_inc", "ip_id_df0_fixed",
+ "ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc",
+ ]
+
+ # Tests specific to IPv6
+ ipv6_tests = [
+ "ip_frag6", "ip_v6ext_same", "ip_v6ext_diff",
+ ]
+
for mode in ["sw", "hw", "lro"]:
for protocol in ["ipv4", "ipv6", "ipip"]:
- for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]:
+ for test_name in common_tests:
yield mode, protocol, test_name
+ if protocol in ["ipv4", "ipip"]:
+ for test_name in ipv4_tests:
+ yield mode, protocol, test_name
+ elif protocol == "ipv6":
+ for test_name in ipv6_tests:
+ yield mode, protocol, test_name
+
@ksft_variants(_gro_variants())
def test(cfg, mode, protocol, test_name):
@@ -210,7 +257,7 @@ from lib.py import ksft_variants
ksft_pr(rx_proc)
- if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"):
+ if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
return
--
2.52.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config
2026-01-10 0:51 ` [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config Jakub Kicinski
@ 2026-01-10 17:43 ` Jakub Kicinski
2026-01-11 17:08 ` Willem de Bruijn
1 sibling, 0 replies; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-10 17:43 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, willemdebruijn.kernel
On Fri, 9 Jan 2026 16:51:19 -0800 Jakub Kicinski wrote:
> try:
> # Disable TSO for local tests
> cfg.require_nsim() # will raise KsftXfailEx if not running on nsim
>
> - cmd(f"ethtool -K {cfg.ifname} gro on tso off")
> - cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote)
> + _set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat, {"tso": False},
> + host=cfg.remote)
> except KsftXfailEx:
> pass
Looks like I haven't re-tested on netdevsim, this needs to spell out
tcp-segmentation-offload :S
--
pw-bot: cr
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config
2026-01-10 0:51 ` [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config Jakub Kicinski
2026-01-10 17:43 ` Jakub Kicinski
@ 2026-01-11 17:08 ` Willem de Bruijn
1 sibling, 0 replies; 15+ messages in thread
From: Willem de Bruijn @ 2026-01-11 17:08 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski,
willemdebruijn.kernel
Jakub Kicinski wrote:
> We'll need to do a lot more feature handling to test HW-GRO and LRO.
> Clean up the feature handling for SW GRO a bit to let the next commit
> focus on the new test cases, only.
>
> Make sure HW GRO-like features are not enabled for the SW tests.
> Be more careful about changing features as "nothing changed"
> situations may result in non-zero error code from ethtool.
>
> Don't disable TSO on the local interface (receiver) when running over
> netdevsim, we just want GSO to break up the segments on the sender.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
With the s/tso/tcp-segmentation-offload change
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO
2026-01-10 0:51 ` [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO Jakub Kicinski
@ 2026-01-11 17:08 ` Willem de Bruijn
0 siblings, 0 replies; 15+ messages in thread
From: Willem de Bruijn @ 2026-01-11 17:08 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski,
willemdebruijn.kernel
Jakub Kicinski wrote:
> 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.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases
2026-01-10 0:51 ` [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases Jakub Kicinski
@ 2026-01-11 17:12 ` Willem de Bruijn
2026-01-13 0:16 ` Jakub Kicinski
0 siblings, 1 reply; 15+ messages in thread
From: Willem de Bruijn @ 2026-01-11 17:12 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm, Jakub Kicinski
Jakub Kicinski wrote:
> GRO test groups the cases into categories, e.g. "tcp" case
> checks coalescing in presence of:
> - packets with bad csum,
> - sequence number mismatch,
> - timestamp option value mismatch,
> - different TCP options.
>
> Since we now have TAP support grouping the cases like that
> lowers our reporting granularity. This matters even more for
> NICs performing HW GRO and LRO since it appears that most
> implementation have _some_ bugs. Flagging the whole group
> of tests as failed prevents us from catching regressions
> in the things that work today.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Thanks for doing this!
Interesting that many devices do seem to fail some tests.
> ---
> tools/testing/selftests/drivers/net/gro.c | 399 ++++++++++++---------
> tools/testing/selftests/drivers/net/gro.py | 65 +++-
> 2 files changed, 285 insertions(+), 179 deletions(-)
>
> diff --git a/tools/testing/selftests/drivers/net/gro.c b/tools/testing/selftests/drivers/net/gro.c
> index e894037d2e3e..a5838de97ba8 100644
> --- a/tools/testing/selftests/drivers/net/gro.c
> +++ b/tools/testing/selftests/drivers/net/gro.c
> @@ -3,26 +3,45 @@
> * This testsuite provides conformance testing for GRO coalescing.
> *
> * Test cases:
> - * 1.data
> + *
> + * data_*:
> * Data packets of the same size and same header setup with correct
> * sequence numbers coalesce. The one exception being the last data
> * packet coalesced: it can be smaller than the rest and coalesced
> * as long as it is in the same flow.
> - * 2.ack
> + * - data_same: same size packets coalesce
> + * - data_lrg_sml: large then small coalesces
> + * - data_sml_lrg: small then large doesn't coalesce
> + *
> + * ack:
> * Pure ACK does not coalesce.
> - * 3.flags
> - * Specific test cases: no packets with PSH, SYN, URG, RST set will
> - * be coalesced.
> - * 4.tcp
> + *
> + * flags_*:
> + * No packets with PSH, SYN, URG, RST set will be coalesced.
> + * - flags_psh, flags_syn, flags_rst, flags_urg
> + *
> + * tcp_*:
> * Packets with incorrect checksum, non-consecutive seqno and
> * different TCP header options shouldn't coalesce. Nit: given that
> * some extension headers have paddings, such as timestamp, headers
> - * that are padding differently would not be coalesced.
> - * 5.ip:
> - * Packets with different (ECN, TTL, TOS) header, ip options or
> - * ip fragments (ipv6) shouldn't coalesce.
> - * 6.large:
> + * that are padded differently would not be coalesced.
> + * - tcp_csum: incorrect checksum
> + * - tcp_seq: non-consecutive sequence numbers
> + * - tcp_ts: different timestamps
> + * - tcp_opt: different TCP options
> + *
> + * ip_*:
> + * Packets with different (ECN, TTL, TOS) header, IP options or
> + * IP fragments shouldn't coalesce.
> + * - ip_ecn, ip_tos: shared between IPv4/IPv6
> + * - ip_ttl, ip_opt, ip_frag4: IPv4 only
> + * - ip_id_df*: IPv4 IP ID field coalescing tests
> + * - ip_frag6, ip_v6ext_*: IPv6 only
> + *
> + * large_*:
> * Packets larger than GRO_MAX_SIZE packets shouldn't coalesce.
> + * - large_max: exceeding max size
> + * - large_rem: remainder handling
> *
> * MSS is defined as 4096 - header because if it is too small
> * (i.e. 1500 MTU - header), it will result in many packets,
> @@ -95,7 +114,6 @@ static int tcp_offset = -1;
> static int total_hdr_len = -1;
> static int ethhdr_proto = -1;
> static bool ipip;
> -static const int num_flush_id_cases = 6;
>
> static void vlog(const char *fmt, ...)
> {
> @@ -127,19 +145,19 @@ static void setup_sock_filter(int fd)
> /* Overridden later if exthdrs are used: */
> opt_ipproto_off = ipproto_off;
>
> - if (strcmp(testname, "ip") == 0) {
> - if (proto == PF_INET)
> - optlen = sizeof(struct ip_timestamp);
> - else {
> - BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE);
> - BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE);
> - BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE);
> + if (strcmp(testname, "ip_opt") == 0) {
> + optlen = sizeof(struct ip_timestamp);
> + } else if (strcmp(testname, "ip_frag6") == 0 ||
> + strcmp(testname, "ip_v6ext_same") == 0 ||
> + strcmp(testname, "ip_v6ext_diff") == 0) {
> + BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE);
> + BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE);
> + BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE);
>
> - /* same size for HBH and Fragment extension header types */
> - optlen = MIN_EXTHDR_SIZE;
> - opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr)
> - + offsetof(struct ip6_ext, ip6e_nxt);
> - }
> + /* same size for HBH and Fragment extension header types */
> + optlen = MIN_EXTHDR_SIZE;
> + opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr)
> + + offsetof(struct ip6_ext, ip6e_nxt);
> }
>
> /* this filter validates the following:
> @@ -739,16 +757,6 @@ static void send_flush_id_case(int fd, struct sockaddr_ll *daddr, int tcase)
> }
> }
>
> -static void test_flush_id(int fd, struct sockaddr_ll *daddr, char *fin_pkt)
> -{
> - for (int i = 0; i < num_flush_id_cases; i++) {
> - sleep(1);
> - send_flush_id_case(fd, daddr, i);
> - sleep(1);
> - write_packet(fd, fin_pkt, total_hdr_len, daddr);
> - }
> -}
> -
> static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr, char *ext_data1, char *ext_data2)
> {
> static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
> @@ -1008,108 +1016,128 @@ static void gro_sender(void)
> daddr.sll_halen = ETH_ALEN;
> create_packet(fin_pkt, PAYLOAD_LEN * 2, 0, 0, 1);
>
> - if (strcmp(testname, "data") == 0) {
> + /* data sub-tests */
> + if (strcmp(testname, "data_same") == 0) {
> send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "data_lrg_sml") == 0) {
> send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "data_sml_lrg") == 0) {
> send_data_pkts(txfd, &daddr, PAYLOAD_LEN / 2, PAYLOAD_LEN);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> +
> + /* ack test */
> } else if (strcmp(testname, "ack") == 0) {
> send_ack(txfd, &daddr);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> - } else if (strcmp(testname, "flags") == 0) {
> +
> + /* flags sub-tests */
> + } else if (strcmp(testname, "flags_psh") == 0) {
> send_flags(txfd, &daddr, 1, 0, 0, 0);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "flags_syn") == 0) {
> send_flags(txfd, &daddr, 0, 1, 0, 0);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "flags_rst") == 0) {
> send_flags(txfd, &daddr, 0, 0, 1, 0);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "flags_urg") == 0) {
> send_flags(txfd, &daddr, 0, 0, 0, 1);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> - } else if (strcmp(testname, "tcp") == 0) {
> +
> + /* tcp sub-tests */
> + } else if (strcmp(testname, "tcp_csum") == 0) {
> send_changed_checksum(txfd, &daddr);
> - /* Adding sleep before sending FIN so that it is not
> - * received prior to other packets.
> - */
> usleep(fin_delay_us);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "tcp_seq") == 0) {
> send_changed_seq(txfd, &daddr);
> usleep(fin_delay_us);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "tcp_ts") == 0) {
> send_changed_ts(txfd, &daddr);
> usleep(fin_delay_us);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "tcp_opt") == 0) {
> send_diff_opt(txfd, &daddr);
> usleep(fin_delay_us);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> - } else if (strcmp(testname, "ip") == 0) {
> +
> + /* ip sub-tests - shared between IPv4 and IPv6 */
> + } else if (strcmp(testname, "ip_ecn") == 0) {
> send_changed_ECN(txfd, &daddr);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> + } else if (strcmp(testname, "ip_tos") == 0) {
> send_changed_tos(txfd, &daddr);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> - if (proto == PF_INET) {
> - /* Modified packets may be received out of order.
> - * Sleep function added to enforce test boundaries
> - * so that fin pkts are not received prior to other pkts.
> - */
> - sleep(1);
> - send_changed_ttl(txfd, &daddr);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>
> - sleep(1);
> - send_ip_options(txfd, &daddr);
> - sleep(1);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + /* ip sub-tests - IPv4 only */
> + } else if (strcmp(testname, "ip_ttl") == 0) {
> + send_changed_ttl(txfd, &daddr);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_opt") == 0) {
> + send_ip_options(txfd, &daddr);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_frag4") == 0) {
> + send_fragment4(txfd, &daddr);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df1_inc") == 0) {
> + send_flush_id_case(txfd, &daddr, 0);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df1_fixed") == 0) {
> + send_flush_id_case(txfd, &daddr, 1);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df0_inc") == 0) {
> + send_flush_id_case(txfd, &daddr, 2);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df0_fixed") == 0) {
> + send_flush_id_case(txfd, &daddr, 3);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {
> + send_flush_id_case(txfd, &daddr, 4);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {
> + send_flush_id_case(txfd, &daddr, 5);
(not critical at all) now that we no longer loop over the cases, might
be nice to have descriptive enums for the various flush_id cases.
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>
> - sleep(1);
> - send_fragment4(txfd, &daddr);
> - sleep(1);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + /* ip sub-tests - IPv6 only */
> + } else if (strcmp(testname, "ip_frag6") == 0) {
> + send_fragment6(txfd, &daddr);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_v6ext_same") == 0) {
> + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "ip_v6ext_diff") == 0) {
> + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);
> + usleep(fin_delay_us);
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>
> - test_flush_id(txfd, &daddr, fin_pkt);
> - } else if (proto == PF_INET6) {
> - sleep(1);
> - send_fragment6(txfd, &daddr);
> - sleep(1);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> - sleep(1);
> - /* send IPv6 packets with ext header with same payload */
> - send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);
> - sleep(1);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> -
> - sleep(1);
> - /* send IPv6 packets with ext header with different payload */
> - send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);
> - sleep(1);
> - write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> - }
> - } else if (strcmp(testname, "large") == 0) {
> - /* 20 is the difference between min iphdr size
> - * and min ipv6hdr size. Like MAX_HDR_SIZE,
> - * MAX_PAYLOAD is defined with the larger header of the two.
> - */
> + /* large sub-tests */
> + } else if (strcmp(testname, "large_max") == 0) {
> int offset = (proto == PF_INET && !ipip) ? 20 : 0;
> int remainder = (MAX_PAYLOAD + offset) % MSS;
>
> send_large(txfd, &daddr, remainder);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> + } else if (strcmp(testname, "large_rem") == 0) {
> + int offset = (proto == PF_INET && !ipip) ? 20 : 0;
> + int remainder = (MAX_PAYLOAD + offset) % MSS;
>
> send_large(txfd, &daddr, remainder + 1);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> } else {
> - error(1, 0, "Unknown testcase");
> + error(1, 0, "Unknown testcase: %s", testname);
> }
>
> if (close(txfd))
> @@ -1132,126 +1160,153 @@ static void gro_receiver(void)
>
> memset(correct_payload, 0, sizeof(correct_payload));
>
> - if (strcmp(testname, "data") == 0) {
> + /* data sub-tests */
> + if (strcmp(testname, "data_same") == 0) {
> printf("pure data packet of same size: ");
> correct_payload[0] = PAYLOAD_LEN * 2;
> check_recv_pkts(rxfd, correct_payload, 1);
> -
> + } else if (strcmp(testname, "data_lrg_sml") == 0) {
> printf("large data packets followed by a smaller one: ");
> correct_payload[0] = PAYLOAD_LEN * 1.5;
> check_recv_pkts(rxfd, correct_payload, 1);
> -
> + } else if (strcmp(testname, "data_sml_lrg") == 0) {
> printf("small data packets followed by a larger one: ");
> correct_payload[0] = PAYLOAD_LEN / 2;
> correct_payload[1] = PAYLOAD_LEN;
> check_recv_pkts(rxfd, correct_payload, 2);
> +
> + /* ack test */
> } else if (strcmp(testname, "ack") == 0) {
> printf("duplicate ack and pure ack: ");
> check_recv_pkts(rxfd, correct_payload, 3);
> - } else if (strcmp(testname, "flags") == 0) {
> +
> + /* flags sub-tests */
> + } else if (strcmp(testname, "flags_psh") == 0) {
> correct_payload[0] = PAYLOAD_LEN * 3;
> correct_payload[1] = PAYLOAD_LEN * 2;
> -
> printf("psh flag ends coalescing: ");
> check_recv_pkts(rxfd, correct_payload, 2);
> -
> + } else if (strcmp(testname, "flags_syn") == 0) {
> correct_payload[0] = PAYLOAD_LEN * 2;
> correct_payload[1] = 0;
> correct_payload[2] = PAYLOAD_LEN * 2;
> printf("syn flag ends coalescing: ");
> check_recv_pkts(rxfd, correct_payload, 3);
> -
> + } else if (strcmp(testname, "flags_rst") == 0) {
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = 0;
> + correct_payload[2] = PAYLOAD_LEN * 2;
> printf("rst flag ends coalescing: ");
> check_recv_pkts(rxfd, correct_payload, 3);
> -
> + } else if (strcmp(testname, "flags_urg") == 0) {
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = 0;
> + correct_payload[2] = PAYLOAD_LEN * 2;
> printf("urg flag ends coalescing: ");
> check_recv_pkts(rxfd, correct_payload, 3);
> - } else if (strcmp(testname, "tcp") == 0) {
> +
> + /* tcp sub-tests */
> + } else if (strcmp(testname, "tcp_csum") == 0) {
> correct_payload[0] = PAYLOAD_LEN;
> correct_payload[1] = PAYLOAD_LEN;
> + printf("changed checksum does not coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "tcp_seq") == 0) {
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> + printf("Wrong Seq number doesn't coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "tcp_ts") == 0) {
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = PAYLOAD_LEN;
> correct_payload[2] = PAYLOAD_LEN;
> correct_payload[3] = PAYLOAD_LEN;
> -
> - printf("changed checksum does not coalesce: ");
> - check_recv_pkts(rxfd, correct_payload, 2);
> -
> - printf("Wrong Seq number doesn't coalesce: ");
> - check_recv_pkts(rxfd, correct_payload, 2);
> -
> printf("Different timestamp doesn't coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> check_recv_pkts(rxfd, correct_payload, 4);
> -
> - printf("Different options doesn't coalesce: ");
> + } else if (strcmp(testname, "tcp_opt") == 0) {
> correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = PAYLOAD_LEN;
> + printf("Different options doesn't coalesce: ");
> check_recv_pkts(rxfd, correct_payload, 2);
> - } else if (strcmp(testname, "ip") == 0) {
> +
> + /* ip sub-tests - shared between IPv4 and IPv6 */
> + } else if (strcmp(testname, "ip_ecn") == 0) {
> correct_payload[0] = PAYLOAD_LEN;
> correct_payload[1] = PAYLOAD_LEN;
> -
> printf("different ECN doesn't coalesce: ");
> check_recv_pkts(rxfd, correct_payload, 2);
> -
> + } else if (strcmp(testname, "ip_tos") == 0) {
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> printf("different tos doesn't coalesce: ");
> check_recv_pkts(rxfd, correct_payload, 2);
>
> - if (proto == PF_INET) {
> - printf("different ttl doesn't coalesce: ");
> - check_recv_pkts(rxfd, correct_payload, 2);
> + /* ip sub-tests - IPv4 only */
> + } else if (strcmp(testname, "ip_ttl") == 0) {
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> + printf("different ttl doesn't coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "ip_opt") == 0) {
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> + correct_payload[2] = PAYLOAD_LEN;
> + printf("ip options doesn't coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 3);
> + } else if (strcmp(testname, "ip_frag4") == 0) {
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> + printf("fragmented ip4 doesn't coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "ip_id_df1_inc") == 0) {
> + printf("DF=1, Incrementing - should coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "ip_id_df1_fixed") == 0) {
> + printf("DF=1, Fixed - should coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "ip_id_df0_inc") == 0) {
> + printf("DF=0, Incrementing - should coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "ip_id_df0_fixed") == 0) {
> + printf("DF=0, Fixed - should coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {
> + printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = PAYLOAD_LEN;
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {
> + printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = PAYLOAD_LEN;
> + check_recv_pkts(rxfd, correct_payload, 2);
>
> - printf("ip options doesn't coalesce: ");
> - correct_payload[2] = PAYLOAD_LEN;
> - check_recv_pkts(rxfd, correct_payload, 3);
> + /* ip sub-tests - IPv6 only */
> + } else if (strcmp(testname, "ip_frag6") == 0) {
> + /* GRO doesn't check for ipv6 hop limit when flushing.
> + * Hence no corresponding test to the ipv4 case.
> + */
> + printf("fragmented ip6 doesn't coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + correct_payload[1] = PAYLOAD_LEN;
> + correct_payload[2] = PAYLOAD_LEN;
> + check_recv_pkts(rxfd, correct_payload, 3);
> + } else if (strcmp(testname, "ip_v6ext_same") == 0) {
> + printf("ipv6 with ext header does coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN * 2;
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "ip_v6ext_diff") == 0) {
> + printf("ipv6 with ext header with different payloads doesn't coalesce: ");
> + correct_payload[0] = PAYLOAD_LEN;
> + correct_payload[1] = PAYLOAD_LEN;
> + check_recv_pkts(rxfd, correct_payload, 2);
>
> - printf("fragmented ip4 doesn't coalesce: ");
> - check_recv_pkts(rxfd, correct_payload, 2);
> -
> - /* is_atomic checks */
> - printf("DF=1, Incrementing - should coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - check_recv_pkts(rxfd, correct_payload, 1);
> -
> - printf("DF=1, Fixed - should coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - check_recv_pkts(rxfd, correct_payload, 1);
> -
> - printf("DF=0, Incrementing - should coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - check_recv_pkts(rxfd, correct_payload, 1);
> -
> - printf("DF=0, Fixed - should coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - check_recv_pkts(rxfd, correct_payload, 1);
> -
> - printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - correct_payload[1] = PAYLOAD_LEN;
> - check_recv_pkts(rxfd, correct_payload, 2);
> -
> - printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - correct_payload[1] = PAYLOAD_LEN;
> - check_recv_pkts(rxfd, correct_payload, 2);
> - } else if (proto == PF_INET6) {
> - /* GRO doesn't check for ipv6 hop limit when flushing.
> - * Hence no corresponding test to the ipv4 case.
> - */
> - printf("fragmented ip6 doesn't coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - correct_payload[1] = PAYLOAD_LEN;
> - correct_payload[2] = PAYLOAD_LEN;
> - check_recv_pkts(rxfd, correct_payload, 3);
> -
> - printf("ipv6 with ext header does coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN * 2;
> - check_recv_pkts(rxfd, correct_payload, 1);
> -
> - printf("ipv6 with ext header with different payloads doesn't coalesce: ");
> - correct_payload[0] = PAYLOAD_LEN;
> - correct_payload[1] = PAYLOAD_LEN;
> - check_recv_pkts(rxfd, correct_payload, 2);
> - }
> - } else if (strcmp(testname, "large") == 0) {
> + /* large sub-tests */
> + } else if (strcmp(testname, "large_max") == 0) {
> int offset = (proto == PF_INET && !ipip) ? 20 : 0;
> int remainder = (MAX_PAYLOAD + offset) % MSS;
>
> @@ -1259,14 +1314,18 @@ static void gro_receiver(void)
> correct_payload[1] = remainder;
> printf("Shouldn't coalesce if exceed IP max pkt size: ");
> check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "large_rem") == 0) {
> + int offset = (proto == PF_INET && !ipip) ? 20 : 0;
> + int remainder = (MAX_PAYLOAD + offset) % MSS;
>
> /* last segment sent individually, doesn't start new segment */
> - correct_payload[0] = correct_payload[0] - remainder;
> + correct_payload[0] = (MAX_PAYLOAD + offset) - remainder;
> correct_payload[1] = remainder + 1;
> correct_payload[2] = remainder + 1;
> + printf("last segment sent individually: ");
> check_recv_pkts(rxfd, correct_payload, 3);
> } else {
> - error(1, 0, "Test case error, should never trigger");
> + error(1, 0, "Test case error: unknown testname %s", testname);
> }
>
> if (close(rxfd))
> diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
> index 112560482d04..08b22d7857bd 100755
> --- a/tools/testing/selftests/drivers/net/gro.py
> +++ b/tools/testing/selftests/drivers/net/gro.py
> @@ -9,12 +9,29 @@ binary in different configurations and checking for correct packet
> coalescing behavior.
>
> Test cases:
> - - data: Data packets with same size/headers and correct seq numbers coalesce
> + - data_same: Same size data packets coalesce
> + - data_lrg_sml: Large packet followed by smaller one coalesces
> + - data_sml_lrg: Small packet followed by larger one doesn't coalesce
> - ack: Pure ACK packets do not coalesce
> - - flags: Packets with PSH, SYN, URG, RST flags do not coalesce
> - - tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce
> - - ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce
> - - large: Packets larger than GRO_MAX_SIZE don't coalesce
> + - flags_psh: Packets with PSH flag don't coalesce
> + - flags_syn: Packets with SYN flag don't coalesce
> + - flags_rst: Packets with RST flag don't coalesce
> + - flags_urg: Packets with URG flag don't coalesce
> + - tcp_csum: Packets with incorrect checksum don't coalesce
> + - tcp_seq: Packets with non-consecutive seqno don't coalesce
> + - tcp_ts: Packets with different timestamp options don't coalesce
> + - tcp_opt: Packets with different TCP options don't coalesce
> + - ip_ecn: Packets with different ECN don't coalesce
> + - ip_tos: Packets with different TOS don't coalesce
> + - ip_ttl: (IPv4) Packets with different TTL don't coalesce
> + - ip_opt: (IPv4) Packets with IP options don't coalesce
> + - ip_frag4: (IPv4) IPv4 fragments don't coalesce
> + - ip_id_df*: (IPv4) IP ID field coalescing tests
> + - ip_frag6: (IPv6) IPv6 fragments don't coalesce
> + - ip_v6ext_same: (IPv6) IPv6 ext header with same payload coalesces
> + - ip_v6ext_diff: (IPv6) IPv6 ext header with different payload doesn't coalesce
> + - large_max: Packets exceeding GRO_MAX_SIZE don't coalesce
> + - large_rem: Large packet remainder handling
> """
>
> import os
> @@ -107,8 +124,8 @@ from lib.py import ksft_variants
> cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}",
> host=cfg.remote, json=True)[0]
>
> - # "large" test needs at least 4k MTU
> - if test_name == "large":
> + # "large_*" tests need at least 4k MTU
> + if test_name.startswith("large_"):
> _set_mtu_restore(cfg.dev, 4096, None)
> _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
>
> @@ -165,11 +182,41 @@ from lib.py import ksft_variants
> def _gro_variants():
> """Generator that yields all combinations of protocol and test types."""
>
> + # Tests that work for all protocols
> + common_tests = [
> + "data_same", "data_lrg_sml", "data_sml_lrg",
> + "ack",
> + "flags_psh", "flags_syn", "flags_rst", "flags_urg",
> + "tcp_csum", "tcp_seq", "tcp_ts", "tcp_opt",
> + "ip_ecn", "ip_tos",
> + "large_max", "large_rem",
> + ]
> +
> + # Tests specific to IPv4
> + ipv4_tests = [
> + "ip_ttl", "ip_opt", "ip_frag4",
> + "ip_id_df1_inc", "ip_id_df1_fixed",
> + "ip_id_df0_inc", "ip_id_df0_fixed",
> + "ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc",
> + ]
> +
> + # Tests specific to IPv6
> + ipv6_tests = [
> + "ip_frag6", "ip_v6ext_same", "ip_v6ext_diff",
> + ]
> +
> for mode in ["sw", "hw", "lro"]:
> for protocol in ["ipv4", "ipv6", "ipip"]:
> - for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]:
> + for test_name in common_tests:
> yield mode, protocol, test_name
>
> + if protocol in ["ipv4", "ipip"]:
> + for test_name in ipv4_tests:
> + yield mode, protocol, test_name
> + elif protocol == "ipv6":
> + for test_name in ipv6_tests:
> + yield mode, protocol, test_name
> +
>
> @ksft_variants(_gro_variants())
> def test(cfg, mode, protocol, test_name):
> @@ -210,7 +257,7 @@ from lib.py import ksft_variants
>
> ksft_pr(rx_proc)
>
> - if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"):
> + if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
> ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
> return
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety
2026-01-10 0:51 ` [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety Jakub Kicinski
@ 2026-01-12 10:37 ` Petr Machata
0 siblings, 0 replies; 15+ messages in thread
From: Petr Machata @ 2026-01-12 10:37 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm
Jakub Kicinski <kuba@kernel.org> writes:
> Make printing multi-line logs easier by automatically prefixing
> each line in ksft_pr(). Make use of this when formatting exceptions.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Petr Machata <petrm@nvidia.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself
2026-01-10 0:51 ` [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself Jakub Kicinski
@ 2026-01-12 10:37 ` Petr Machata
0 siblings, 0 replies; 15+ messages in thread
From: Petr Machata @ 2026-01-12 10:37 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm
Jakub Kicinski <kuba@kernel.org> writes:
> Teach cmd() how to print itself, to make debug prints easier.
> Example output (leading # due to ksft_pr()):
>
> # CMD: /root/ksft-net-drv/drivers/net/gro
> # EXIT: 1
> # STDOUT: ipv6 with ext header does coalesce:
> # STDERR: Expected {200 }, Total 1 packets
> # Received {100 [!=200]100 [!=0]}, Total 2 packets.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Petr Machata <petrm@nvidia.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print
2026-01-10 0:51 ` [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print Jakub Kicinski
@ 2026-01-12 10:37 ` Petr Machata
0 siblings, 0 replies; 15+ messages in thread
From: Petr Machata @ 2026-01-12 10:37 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm
Jakub Kicinski <kuba@kernel.org> writes:
> Now that cmd() can be printed directly remove the old formatting.
>
> Before:
>
> # fragmented ip6 doesn't coalesce:
> # Expected {200 100 100 }, Total 3 packets
> # Received {200 100 }, Total 2 packets.
> # /root/ksft-net-drv/drivers/net/gro: incorrect number of packets
>
> Now:
>
> # CMD: drivers/net/gro --ipv6 --dmac 9e:[...]
> # EXIT: 1
> # STDOUT: fragmented ip6 doesn't coalesce:
> # STDERR: Expected {200 100 100 }, Total 3 packets
> # Received {200 100 }, Total 2 packets.
> # /root/ksft-net-drv/drivers/net/gro: incorrect number of packets
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Petr Machata <petrm@nvidia.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases
2026-01-11 17:12 ` Willem de Bruijn
@ 2026-01-13 0:16 ` Jakub Kicinski
0 siblings, 0 replies; 15+ messages in thread
From: Jakub Kicinski @ 2026-01-13 0:16 UTC (permalink / raw)
To: Willem de Bruijn
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
linux-kselftest, sdf, willemb, petrm
On Sun, 11 Jan 2026 12:12:09 -0500 Willem de Bruijn wrote:
> Interesting that many devices do seem to fail some tests.
The case that convinced me to do this was TCP option handling.
Older Broadcom NICs appear to coalesce mismatched timestamps.
I suppose it may be related to the Microsoft sec which seems
to recommend coalescing incrementing timestamps like seq no :|
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-01-13 0:16 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-10 0:51 [PATCH net-next v2 0/6] selftests: drv-net: gro: enable HW GRO and LRO testing Jakub Kicinski
2026-01-10 0:51 ` [PATCH net-next v2 1/6] selftests: net: py: teach ksft_pr() multi-line safety Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 2/6] selftests: net: py: teach cmd() how to print itself Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 3/6] selftests: drv-net: gro: use cmd print Jakub Kicinski
2026-01-12 10:37 ` Petr Machata
2026-01-10 0:51 ` [PATCH net-next v2 4/6] selftests: drv-net: gro: improve feature config Jakub Kicinski
2026-01-10 17:43 ` Jakub Kicinski
2026-01-11 17:08 ` Willem de Bruijn
2026-01-10 0:51 ` [PATCH net-next v2 5/6] selftests: drv-net: gro: run the test against HW GRO and LRO Jakub Kicinski
2026-01-11 17:08 ` Willem de Bruijn
2026-01-10 0:51 ` [PATCH net-next v2 6/6] selftests: drv-net: gro: break out all individual test cases Jakub Kicinski
2026-01-11 17:12 ` Willem de Bruijn
2026-01-13 0:16 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox