mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 13:41:48 -04:00
selftests: drv-net: gro: add test for packet ordering
Add a test to check if the NIC reorders packets if the hit GRO. Link: https://patch.msgid.link/20260318033819.1469350-6-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -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 @@ def _setup_isolated_queue(cfg):
|
||||
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 @@ def _run_gro_test(cfg, test_name, num_flows=None, ignore_fail=False):
|
||||
]
|
||||
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 test_gro_stats_full(cfg):
|
||||
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 aggregate 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()
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1136,6 +1137,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];
|
||||
@@ -1144,8 +1146,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;
|
||||
|
||||
@@ -1203,6 +1203,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");
|
||||
@@ -1622,12 +1650,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;
|
||||
@@ -1666,6 +1695,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;
|
||||
|
||||
Reference in New Issue
Block a user