[PATCH net-next 8/9] selftests: drv-net: gro: add test for packet ordering
From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-02-05 22:07:27
Also in:
linux-kselftest
Subsystem:
kernel selftest framework, networking drivers, networking [general], the rest · Maintainers:
Shuah Khan, Shuah Khan, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add a test to check if the NIC reorders packets if the hit GRO. Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- tools/testing/selftests/net/lib/gro.c | 38 +++++++++++++++++-- .../selftests/drivers/net/hw/gro_hw.py | 29 ++++++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
index d37676c009e8..3d3312b37248 100644
--- a/tools/testing/selftests/net/lib/gro.c
+++ b/tools/testing/selftests/net/lib/gro.c@@ -131,6 +131,7 @@ static int ethhdr_proto = -1; static bool ipip; static uint64_t txtime_ns; static int num_flows = 4; +static bool order_check; #define CAPACITY_PAYLOAD_LEN 200
@@ -1134,6 +1135,7 @@ static void check_capacity_pkts(int fd) static char buffer[IP_MAXPACKET + ETH_HLEN + 1]; struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN); struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN); + int num_pkt = 0, num_coal = 0, pkt_idx; const char *fail_reason = NULL; int flow_order[num_flows * 2]; int coalesced[num_flows];
@@ -1142,8 +1144,6 @@ static void check_capacity_pkts(int fd) int total_data = 0; int pkt_size = -1; int data_len = 0; - int num_pkt = 0; - int num_coal = 0; int flow_id; int sport;
@@ -1201,6 +1201,34 @@ static void check_capacity_pkts(int fd) total_data += data_len; } + /* Check flow ordering. We expect to see all non-coalesced first segs + * then interleaved coalesced and non-coalesced second frames. + */ + pkt_idx = 0; + for (flow_id = 0; order_check && flow_id < num_flows; flow_id++) { + bool coaled = coalesced[flow_id] > CAPACITY_PAYLOAD_LEN; + + if (coaled) + continue; + + if (flow_order[pkt_idx] != flow_id) { + vlog("Flow order mismatch (non-coalesced) at position %d: expected flow %d, got flow %d\n", + pkt_idx, flow_id, flow_order[pkt_idx]); + fail_reason = fail_reason ?: "bad packet order (1)"; + } + pkt_idx++; + } + for (flow_id = 0; order_check && flow_id < num_flows; flow_id++) { + bool coaled = coalesced[flow_id] > CAPACITY_PAYLOAD_LEN; + + if (flow_order[pkt_idx] != flow_id) { + vlog("Flow order mismatch at position %d: expected flow %d, got flow %d, coalesced: %d\n", + pkt_idx, flow_id, flow_order[pkt_idx], coaled); + fail_reason = fail_reason ?: "bad packet order (2)"; + } + pkt_idx++; + } + if (!fail_reason) { vlog("All %d flows coalesced correctly\n", num_flows); printf("Test succeeded\n\n");
@@ -1614,12 +1642,13 @@ static void parse_args(int argc, char **argv) { "saddr", required_argument, NULL, 's' }, { "smac", required_argument, NULL, 'S' }, { "test", required_argument, NULL, 't' }, + { "order-check", no_argument, NULL, 'o' }, { "verbose", no_argument, NULL, 'v' }, { 0, 0, 0, 0 } }; int c; - while ((c = getopt_long(argc, argv, "46d:D:ei:n:rs:S:t:v", opts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "46d:D:ei:n:rs:S:t:ov", opts, NULL)) != -1) { switch (c) { case '4': proto = PF_INET;
@@ -1658,6 +1687,9 @@ static void parse_args(int argc, char **argv) case 't': testname = optarg; break; + case 'o': + order_check = true; + break; case 'v': verbose = true; break;
diff --git a/tools/testing/selftests/drivers/net/hw/gro_hw.py b/tools/testing/selftests/drivers/net/hw/gro_hw.py
index 3bca19e8f339..18a3b1bceefd 100755
--- a/tools/testing/selftests/drivers/net/hw/gro_hw.py
+++ b/tools/testing/selftests/drivers/net/hw/gro_hw.py@@ -10,7 +10,7 @@ import glob import re from lib.py import ksft_run, ksft_exit, ksft_pr -from lib.py import ksft_eq, ksft_ge +from lib.py import ksft_eq, ksft_ge, ksft_variants from lib.py import NetDrvEpEnv, NetdevFamily from lib.py import KsftSkipEx from lib.py import bkg, cmd, defer, ethtool, ip
@@ -78,7 +78,8 @@ GRO_DPORT = 8000 return test_queue -def _run_gro_test(cfg, test_name, num_flows=None, ignore_fail=False): +def _run_gro_test(cfg, test_name, num_flows=None, ignore_fail=False, + order_check=False): """Run gro binary with given test and return output.""" if not hasattr(cfg, "bin_remote"): cfg.bin_local = cfg.net_lib_dir / "gro"
@@ -98,6 +99,8 @@ GRO_DPORT = 8000 ] if num_flows: base_args.append(f"--num-flows {num_flows}") + if order_check: + base_args.append("--order-check") args = " ".join(base_args)
@@ -257,13 +260,33 @@ def _check_gro_stats(cfg, test_queue, stats_before, expect_wire=gro_coalesced * 2) +@ksft_variants([4, 32, 512]) +def test_gro_order(cfg, num_flows): + """ + Test that HW GRO preserves packet ordering between flows. + + Packets may get delayed until the aggreate is released, + but reordering between aggregates and packet terminating + the aggregate and normal packets should not happen. + + Note that this test is stricter than truly required. + Reordering packets between flows should not cause issues. + This test will also fail if traffic is run over an ECMP fabric. + """ + _setup_hw_gro(cfg) + _setup_isolated_queue(cfg) + + _run_gro_test(cfg, "capacity", num_flows=num_flows, order_check=True) + + def main() -> None: """ Ksft boiler plate main """ with NetDrvEpEnv(__file__, nsim_test=False) as cfg: cfg.netnl = NetdevFamily() ksft_run([test_gro_stats_single, - test_gro_stats_full], args=(cfg,)) + test_gro_stats_full, + test_gro_order], args=(cfg,)) ksft_exit()
--
2.53.0