* [PATCH net-next v3] selftests: drv-net: add BIG TCP test cases
@ 2026-09-13 5:24 Narcisa Vasile
2026-09-14 5:26 ` netdev-bot+sashiko
0 siblings, 1 reply; 3+ messages in thread
From: Narcisa Vasile @ 2026-09-13 5:24 UTC (permalink / raw)
To: netdev, kuba, daniel.zahka, andrew+netdev, davem, edumazet,
pabeni, shuah, horms, willemb, petrm, anubhavsinggh,
richardbgobert
Cc: linux-kselftest, linux-kernel, narcisav.kernel
Add four new test cases that validate coalescing
under increased size limits (BIG TCP):
big_tcp_data_same
- validates that equal-sized segments coalesce past
the legacy IP_MAXPACKET limit.
big_tcp_data_lrg_sml
- validates that a smaller final segment coalesces into the
previous chain of large-sized segments while crossing the
IP_MAXPACKET limit.
big_tcp_tcp_seq
- validates that a packet with a wrong sequence number doesn't
coalesce. The test uses a sequence number for which the low 16 bits
correspond to the correct sequence number to validate against
truncation bugs, since total aggregate length crosses over the
legacy size limit for BIG TCP.
big_tcp_large_max
- validates that coalescing stops at the configured BIG TCP limit.
Use a gro_flush_timeout value 2x higher for the BIG TCP test cases
to prevent under-coalescing.
Signed-off-by: Narcisa Vasile <narcisav.kernel@gmail.com>
---
v3:
- set gro_flush_timeout to twice the regular value for big tcp
test cases
- ignore under-coalescing failures for big tcp tests when KSFT_MACHINE_SLOW is set
v2: https://lore.kernel.org/netdev/20260912050327.20616-1-narcisav.kernel@gmail.com/
- increase gro_flush_timeout to 15x the regular value for the new tests
v1: https://lore.kernel.org/netdev/20260911050659.13367-1-narcisav.kernel@gmail.com/
.../testing/selftests/drivers/net/gro_lib.py | 59 +++++++-
tools/testing/selftests/net/lib/gro.c | 133 +++++++++++++++++-
2 files changed, 187 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/gro_lib.py b/tools/testing/selftests/drivers/net/gro_lib.py
index b7ac0660adc0..abecf60baa53 100644
--- a/tools/testing/selftests/drivers/net/gro_lib.py
+++ b/tools/testing/selftests/drivers/net/gro_lib.py
@@ -40,13 +40,17 @@ Test cases:
- 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
+ - big_tcp_data_same: Same size packets coalesce past IP_MAXPACKET
+ - big_tcp_data_lrg_sml: Smaller last packet coalesces past IP_MAXPACKET
+ - big_tcp_tcp_seq: Packets with 16-bit truncated seqno don't coalesce
+ - big_tcp_large_max: Packets exceeding the BIG TCP limit don't coalesce
"""
import glob
import os
import re
-from lib.py import ksft_run, ksft_exit, ksft_pr
-from lib.py import NetDrvEpEnv, KsftFailEx, KsftXfailEx
+from lib.py import ksft_run, ksft_exit, ksft_pr, ksft_eq
+from lib.py import NetDrvEpEnv, KsftFailEx, KsftSkipEx, KsftXfailEx
from lib.py import NetdevFamily, EthtoolFamily
from lib.py import bkg, cmd, ctl_file_write, defer, ethtool, ip
from lib.py import ksft_variants, KsftNamedVariant
@@ -55,6 +59,8 @@ from lib.py import ksft_variants, KsftNamedVariant
# gro.c uses hardcoded DPORT=8000
GRO_DPORT = 8000
+BIG_TCP_GRO_MAX_SIZE = 128000
+
def _resolve_dmac(cfg, ipver):
"""
@@ -91,6 +97,34 @@ def _set_mtu_restore(dev, mtu, host):
defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
+def _set_gro_size_restore(cfg, size):
+ """
+ Set the local device's GRO size limits, then confirm they stuck.
+ """
+
+ _set_mtu_restore(cfg.dev, 4096, None)
+ _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
+
+ if "gro_max_size" not in cfg.dev or "gro_ipv4_max_size" not in cfg.dev:
+ raise KsftSkipEx("iproute2 does not report the GRO size limits")
+
+ if (cfg.dev["gro_max_size"] == size and
+ cfg.dev["gro_ipv4_max_size"] == size):
+ return
+
+ old = (f"gro_max_size {cfg.dev['gro_max_size']} "
+ f"gro_ipv4_max_size {cfg.dev['gro_ipv4_max_size']}")
+ new = f"gro_max_size {size} gro_ipv4_max_size {size}"
+
+ ip(f"link set dev {cfg.ifname} {new}")
+ defer(ip, f"link set dev {cfg.ifname} {old}")
+
+ dev = ip("-d link show dev " + cfg.ifname, json=True)[0]
+ ksft_eq(dev["gro_max_size"], size, comment="gro_max_size not applied")
+ ksft_eq(dev["gro_ipv4_max_size"], size,
+ comment="gro_ipv4_max_size not applied")
+
+
def _set_ethtool_feat(dev, current, feats, host=None):
s2n = {True: "on", False: "off"}
@@ -239,7 +273,11 @@ def _setup(cfg, mode, test_name):
flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
- ctl_file_write(flush_path, "200000")
+ # "big_tcp_*" tests need a longer timeout, use 2x the regular timeout
+ if test_name.startswith("big_tcp_"):
+ ctl_file_write(flush_path, "400000")
+ else:
+ ctl_file_write(flush_path, "200000")
ctl_file_write(irq_path, "10")
_set_ethtool_feat(cfg.ifname, cfg.feat,
@@ -289,6 +327,9 @@ def _setup(cfg, mode, test_name):
except KsftXfailEx:
pass
+ if test_name.startswith("big_tcp_"):
+ _set_gro_size_restore(cfg, BIG_TCP_GRO_MAX_SIZE)
+
def _gro_variants():
"""Generator that yields all combinations of protocol and test types."""
@@ -304,6 +345,11 @@ def _gro_variants():
"large_max", "large_rem",
]
+ big_tcp_tests = [
+ "big_tcp_data_same", "big_tcp_data_lrg_sml",
+ "big_tcp_tcp_seq", "big_tcp_large_max",
+ ]
+
# Tests specific to IPv4
ipv4_tests = [
"ip_csum",
@@ -322,6 +368,10 @@ def _gro_variants():
for test_name in common_tests:
yield protocol, test_name
+ if protocol in ["ipv4", "ipv6"]:
+ for test_name in big_tcp_tests:
+ yield protocol, test_name
+
if protocol in ["ipv4", "ipip"]:
for test_name in ipv4_tests:
yield protocol, test_name
@@ -358,7 +408,8 @@ def run_test(cfg, mode, protocol, test_name):
if rx_proc.ret == 42:
raise KsftFailEx(f"GRO over-coalesced in {protocol}/{test_name}")
- if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
+ if (test_name.startswith(("large_", "big_tcp_")) and
+ os.environ.get("KSFT_MACHINE_SLOW")):
ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
return
diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
index 7a333155de1a..70b0deb3c11f 100644
--- a/tools/testing/selftests/net/lib/gro.c
+++ b/tools/testing/selftests/net/lib/gro.c
@@ -46,6 +46,12 @@
* - large_max: exceeding max size
* - large_rem: remainder handling
*
+ * big_tcp_*:
+ * - big_tcp_data_same: equal segments coalescing past IP_MAXPACKET
+ * - big_tcp_data_lrg_sml: a smaller final segment carrying it over
+ * - big_tcp_tcp_seq: 16-bit truncated sequence number must not coalesce
+ * - big_tcp_large_max: coalescing stops at the configured limit
+ *
* single, capacity:
* Boring cases used to test coalescing machinery itself and stats
* more than protocol behavior.
@@ -110,6 +116,16 @@
#define EXIT_OVER_COALESCE 42
+/* Must match BIG_TCP_GRO_MAX_SIZE in gro_lib.py. */
+#define BIG_TCP_GRO_MAX_SIZE 128000
+
+#define BIG_TCP_RECV_BUF_LEN \
+ (BIG_TCP_GRO_MAX_SIZE + MAX_MSS + L2_HLEN_MAX)
+#define BIG_TCP_MIN_MSS (ASSUMED_MTU - (MAX_HDR_LEN - ETH_HLEN))
+#define BIG_TCP_MAX_FILL_CNT \
+ ((int)((BIG_TCP_GRO_MAX_SIZE - 1 - (MAX_HDR_LEN - ETH_HLEN)) / \
+ BIG_TCP_MIN_MSS))
+
#define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
@@ -166,6 +182,27 @@ static int num_large_pkt(void)
return max_payload() / calc_mss();
}
+/* How many maximum sized segments fit under the configured limit. */
+static int big_tcp_large_cnt(void)
+{
+ return (BIG_TCP_GRO_MAX_SIZE - 1 - (total_hdr_len - ETH_HLEN)) /
+ calc_mss();
+}
+
+/* How many calc_mss() sized segments are needed to satisfy the
+ * following condition:
+ * pkt_count * calc_mss() < IP_MAXPACKET < (pkt_count + 1) * calc_mss()
+ */
+static int big_tcp_fill_cnt(void)
+{
+ return IP_MAXPACKET / calc_mss();
+}
+
+static int big_tcp_fill_len(void)
+{
+ return big_tcp_fill_cnt() * calc_mss();
+}
+
static void vlog(const char *fmt, ...)
{
va_list args;
@@ -560,6 +597,61 @@ static void send_data_pkts(int fd, struct sockaddr_ll *daddr,
write_packet(fd, buf, total_hdr_len + payload_len2, daddr);
}
+/* Send num_pkt segments of pkt_len bytes, then one of remainder len. */
+static void send_big_tcp(int fd, struct sockaddr_ll *daddr, int pkt_len,
+ int num_pkt, int remainder)
+{
+ static char pkts[BIG_TCP_MAX_FILL_CNT][MAX_HDR_LEN + MAX_MSS];
+ static char last[MAX_HDR_LEN + MAX_MSS];
+ const int filled = num_pkt * pkt_len;
+ int i;
+
+ if (num_pkt > BIG_TCP_MAX_FILL_CNT)
+ error(1, 0, "need %d packets, array holds %d",
+ num_pkt, BIG_TCP_MAX_FILL_CNT);
+
+ for (i = 0; i < num_pkt; i++)
+ create_packet(pkts[i], i * pkt_len, 0, pkt_len, 0);
+ create_packet(last, filled, 0, remainder, 0);
+
+ for (i = 0; i < num_pkt; i++)
+ write_packet(fd, pkts[i], total_hdr_len + pkt_len, daddr);
+ write_packet(fd, last, total_hdr_len + remainder, daddr);
+}
+
+/* In BIG TCP configuration, the total aggregate length can
+ * be greater than the legacy IP_MAXPACKET. Since the aggregate
+ * length is used in calculating the sequence numbers,
+ * send a packet with a sequence number that differs by IP_MAXPACKET + 1,
+ * to test against truncation bugs.
+ */
+static void send_big_tcp_bad_seq(int fd, struct sockaddr_ll *daddr)
+{
+ static char pkts[BIG_TCP_MAX_FILL_CNT][MAX_HDR_LEN + MAX_MSS];
+ const int num_pkt = big_tcp_fill_cnt() + 1;
+ static char last[MAX_HDR_LEN + MAX_MSS];
+ const int pkt_len = calc_mss();
+ int bad_seq;
+ int filled;
+ int i;
+
+ filled = num_pkt * pkt_len;
+ /* Low 16 bits match with the correct incoming sequence number. */
+ bad_seq = filled - (IP_MAXPACKET + 1);
+
+ if (num_pkt > BIG_TCP_MAX_FILL_CNT)
+ error(1, 0, "need %d packets, array holds %d",
+ num_pkt, BIG_TCP_MAX_FILL_CNT);
+
+ for (i = 0; i < num_pkt; i++)
+ create_packet(pkts[i], i * pkt_len, 0, pkt_len, 0);
+ create_packet(last, bad_seq, 0, pkt_len, 0);
+
+ for (i = 0; i < num_pkt; i++)
+ write_packet(fd, pkts[i], total_hdr_len + pkt_len, daddr);
+ write_packet(fd, last, total_hdr_len + pkt_len, daddr);
+}
+
/* If incoming segments make tracked segment length exceed
* legal IP datagram length, do not coalesce
*/
@@ -1161,7 +1253,7 @@ static void recv_error(int fd, int rcv_errno)
static void check_recv_pkts(int fd, int *correct_payload,
int correct_num_pkts)
{
- static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1];
+ static char buffer[BIG_TCP_RECV_BUF_LEN];
int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0);
struct iphdr *iph = (struct iphdr *)(buffer + nhoff);
struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff);
@@ -1541,6 +1633,25 @@ static void gro_sender(void)
send_large(txfd, &daddr, remainder + 1);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ /* big tcp sub-tests */
+ } else if (strcmp(testname, "big_tcp_data_same") == 0) {
+ send_big_tcp(txfd, &daddr, calc_mss(), big_tcp_fill_cnt(),
+ calc_mss());
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "big_tcp_data_lrg_sml") == 0) {
+ int remainder = calc_mss() / 2;
+
+ send_big_tcp(txfd, &daddr, calc_mss(), big_tcp_fill_cnt(),
+ remainder);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "big_tcp_tcp_seq") == 0) {
+ send_big_tcp_bad_seq(txfd, &daddr);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ } else if (strcmp(testname, "big_tcp_large_max") == 0) {
+ send_big_tcp(txfd, &daddr, calc_mss(), big_tcp_large_cnt(),
+ calc_mss());
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+
/* machinery sub-tests */
} else if (strcmp(testname, "single") == 0) {
static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
@@ -1768,6 +1879,26 @@ static void gro_receiver(void)
printf("last segment sent individually: ");
check_recv_pkts(rxfd, correct_payload, 3);
+ /* big tcp sub-tests */
+ } else if (strcmp(testname, "big_tcp_data_same") == 0) {
+ correct_payload[0] = big_tcp_fill_len() + calc_mss();
+ printf("data packets of same size past IP_MAXPACKET: ");
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "big_tcp_data_lrg_sml") == 0) {
+ correct_payload[0] = big_tcp_fill_len() + calc_mss() / 2;
+ printf("smaller last packet past IP_MAXPACKET: ");
+ check_recv_pkts(rxfd, correct_payload, 1);
+ } else if (strcmp(testname, "big_tcp_tcp_seq") == 0) {
+ correct_payload[0] = (big_tcp_fill_cnt() + 1) * calc_mss();
+ correct_payload[1] = calc_mss();
+ printf("aliased seq past IP_MAXPACKET doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+ } else if (strcmp(testname, "big_tcp_large_max") == 0) {
+ correct_payload[0] = big_tcp_large_cnt() * calc_mss();
+ correct_payload[1] = calc_mss();
+ printf("shouldn't coalesce past gro_max_size: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+
/* machinery sub-tests */
} else if (strcmp(testname, "single") == 0) {
printf("single data packet: ");
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH net-next v3] selftests: drv-net: add BIG TCP test cases
2026-09-13 5:24 [PATCH net-next v3] selftests: drv-net: add BIG TCP test cases Narcisa Vasile
@ 2026-09-14 5:26 ` netdev-bot+sashiko
2026-09-16 1:07 ` Narcisa Vasile
0 siblings, 1 reply; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-09-14 5:26 UTC (permalink / raw)
To: narcisav.kernel
Cc: netdev, kuba, daniel.zahka, andrew+netdev, davem, edumazet,
pabeni, shuah, horms, willemb, petrm, anubhavsinggh,
richardbgobert, linux-kselftest, linux-kernel
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 (1) · Low: 1
- [Medium] The four new `big_tcp_*` variants are yielded by
`_gro_variants()` in tools/testing/selftests/drivers/net/gro_lib.py…
- [Low] The patch changes the failure-tolerance policy in `run_test()`
from `test_name.startswith("large_")` to…
Pre-existing issues:
- [Medium] `check_recv_pkts()` (tools/testing/selftests/net/lib/gro.c)
reads `correct_payload[num_pkt]` once per received non-FIN frame with…
--
selftests: drv-net: add BIG TCP test cases
This adds four new GRO selftest cases (big_tcp_data_same,
big_tcp_data_lrg_sml, big_tcp_tcp_seq, big_tcp_large_max) that exercise
coalescing past the legacy IP_MAXPACKET limit by raising the device
gro_max_size/gro_ipv4_max_size to 128000. It also doubles
gro_flush_timeout for those cases and extends the KSFT_MACHINE_SLOW
failure tolerance to them.
A few questions below.
> diff --git a/tools/testing/selftests/drivers/net/gro_lib.py b/tools/testing/selftests/drivers/net/gro_lib.py
> index b7ac0660adc01..abecf60baa537 100644
> --- a/tools/testing/selftests/drivers/net/gro_lib.py
> +++ b/tools/testing/selftests/drivers/net/gro_lib.py
[ ... ]
> @@ -91,6 +97,34 @@ def _set_mtu_restore(dev, mtu, host):
> defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
>
>
> +def _set_gro_size_restore(cfg, size):
> + """
> + Set the local device's GRO size limits, then confirm they stuck.
> + """
> +
> + _set_mtu_restore(cfg.dev, 4096, None)
> + _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
> +
> + if "gro_max_size" not in cfg.dev or "gro_ipv4_max_size" not in cfg.dev:
> + raise KsftSkipEx("iproute2 does not report the GRO size limits")
> +
> + if (cfg.dev["gro_max_size"] == size and
> + cfg.dev["gro_ipv4_max_size"] == size):
> + return
> +
> + old = (f"gro_max_size {cfg.dev['gro_max_size']} "
> + f"gro_ipv4_max_size {cfg.dev['gro_ipv4_max_size']}")
> + new = f"gro_max_size {size} gro_ipv4_max_size {size}"
> +
> + ip(f"link set dev {cfg.ifname} {new}")
> + defer(ip, f"link set dev {cfg.ifname} {old}")
[ ... ]
> @@ -239,7 +273,11 @@ def _setup(cfg, mode, test_name):
> flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
> irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
>
> - ctl_file_write(flush_path, "200000")
> + # "big_tcp_*" tests need a longer timeout, use 2x the regular timeout
> + if test_name.startswith("big_tcp_"):
> + ctl_file_write(flush_path, "400000")
> + else:
> + ctl_file_write(flush_path, "200000")
> ctl_file_write(irq_path, "10")
[ ... ]
> @@ -322,6 +368,10 @@ def _gro_variants():
> for test_name in common_tests:
> yield protocol, test_name
>
> + if protocol in ["ipv4", "ipv6"]:
> + for test_name in big_tcp_tests:
> + yield protocol, test_name
> +
[Severity: Medium]
Should the new variants be restricted to the software GRO mode here?
_gro_variants() has no notion of the coalescing mode, so the four
big_tcp_* cases are also instantiated by gro_hw.py (gro_main(__file__,
"hw")) and gro_lro.py (gro_main(__file__, "lro")).
The expectations for those cases come only from the netdev software GRO
tunable. _set_gro_size_restore() writes gro_max_size /
gro_ipv4_max_size, and the only consumer of that value in the stack is
skb_gro_receive() in net/core/gro.c:
if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) ||
NAPI_GRO_CB(skb)->flush))
return -E2BIG;
git grep gro_max_size drivers/net/ returns nothing, and
netif_set_gro_max_size() / netif_set_gro_ipv4_max_size() in net/core/dev.h
only WRITE_ONCE the netdev fields without notifying the driver.
So with rx-gro-hw or LRO enabled the hardware coalescer keeps to its own
(commonly 64 KB) limit and cannot produce the >65535 byte aggregate that
big_tcp_data_same and friends expect, and big_tcp_large_max cannot stop
at big_tcp_large_cnt() * calc_mss(). In that case check_recv_pkts()
fails with "incorrect number of packets" or "incorrect packet geometry".
Note also that the compensating gro_flush_timeout bump added above only
happens inside the "if mode == \"sw\":" branch of _setup(), which suggests
these cases were designed for software GRO only.
> if protocol in ["ipv4", "ipip"]:
> for test_name in ipv4_tests:
> yield protocol, test_name
> @@ -358,7 +408,8 @@ def run_test(cfg, mode, protocol, test_name):
> if rx_proc.ret == 42:
> raise KsftFailEx(f"GRO over-coalesced in {protocol}/{test_name}")
>
> - if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
> + if (test_name.startswith(("large_", "big_tcp_")) and
> + os.environ.get("KSFT_MACHINE_SLOW")):
> ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
> return
[Severity: Low]
Could the commit message mention this policy change too?
The changelog only says:
"Use a gro_flush_timeout value 2x higher for the BIG TCP test cases
to prevent under-coalescing."
but run_test() now also returns success for any non-over-coalescing
failure of the four new cases whenever KSFT_MACHINE_SLOW is set. That is
described only in the v3 notes below the "---" marker, which are dropped
when the patch is applied, so git history would not record that the new
cases are advisory on slow machines.
> diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
> index 7a333155de1ab..70b0deb3c11fa 100644
> --- a/tools/testing/selftests/net/lib/gro.c
> +++ b/tools/testing/selftests/net/lib/gro.c
[ ... ]
> @@ -166,6 +182,27 @@ static int num_large_pkt(void)
> return max_payload() / calc_mss();
> }
>
> +/* How many maximum sized segments fit under the configured limit. */
> +static int big_tcp_large_cnt(void)
> +{
> + return (BIG_TCP_GRO_MAX_SIZE - 1 - (total_hdr_len - ETH_HLEN)) /
> + calc_mss();
> +}
> +
> +/* How many calc_mss() sized segments are needed to satisfy the
> + * following condition:
> + * pkt_count * calc_mss() < IP_MAXPACKET < (pkt_count + 1) * calc_mss()
> + */
> +static int big_tcp_fill_cnt(void)
> +{
> + return IP_MAXPACKET / calc_mss();
> +}
[ ... ]
> @@ -1161,7 +1253,7 @@ static void recv_error(int fd, int rcv_errno)
> static void check_recv_pkts(int fd, int *correct_payload,
> int correct_num_pkts)
> {
> - static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1];
> + static char buffer[BIG_TCP_RECV_BUF_LEN];
[Severity: Medium]
This isn't a bug introduced by this patch, but do the new cases make it
easy to read past the end of correct_payload[]?
check_recv_pkts() indexes the expectation array once per received
non-FIN frame with no bound on num_pkt:
tools/testing/selftests/net/lib/gro.c:check_recv_pkts() {
while (1) {
...
vlog("%d ", data_len);
if (data_len != correct_payload[num_pkt]) {
vlog("[!=%d]", correct_payload[num_pkt]);
bad_packet = true;
}
bytes_received += data_len;
num_pkt++;
}
}
while gro_receiver() supplies:
static int correct_payload[NUM_PACKETS];
with NUM_PACKETS defined as 4.
For IPv4 at the MTU 4096 that _set_gro_size_restore() configures,
calc_mss() is 4056 and big_tcp_fill_cnt() is 16, so big_tcp_data_same,
big_tcp_data_lrg_sml and big_tcp_tcp_seq put 17 data segments on the wire
while passing correct_num_pkts of 1 or 2, and big_tcp_large_max sends
big_tcp_large_cnt() + 1 = 32 segments while passing 2.
Whenever GRO under-coalesces - which the 6 retries, the doubled
gro_flush_timeout and the KSFT_MACHINE_SLOW tolerance in gro_lib.py all
anticipate, and which is unavoidable in the hw/lro modes where
gro_max_size has no effect - the loop runs 17 or 32 times and reads
correct_payload[4] through correct_payload[31].
Would it make sense to bound the loop with something like
if (num_pkt >= correct_num_pkts)
break;
or to size correct_payload[] for the worst case (BIG_TCP_MAX_FILL_CNT + 1)?
> int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0);
> struct iphdr *iph = (struct iphdr *)(buffer + nhoff);
> struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff);
[ ... ]
> @@ -1541,6 +1633,25 @@ static void gro_sender(void)
> send_large(txfd, &daddr, remainder + 1);
> write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>
> + /* big tcp sub-tests */
> + } else if (strcmp(testname, "big_tcp_data_same") == 0) {
> + send_big_tcp(txfd, &daddr, calc_mss(), big_tcp_fill_cnt(),
> + calc_mss());
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
[ ... ]
> + } else if (strcmp(testname, "big_tcp_large_max") == 0) {
> + send_big_tcp(txfd, &daddr, calc_mss(), big_tcp_large_cnt(),
> + calc_mss());
> + write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> +
[ ... ]
> @@ -1768,6 +1879,26 @@ static void gro_receiver(void)
> printf("last segment sent individually: ");
> check_recv_pkts(rxfd, correct_payload, 3);
>
> + /* big tcp sub-tests */
> + } else if (strcmp(testname, "big_tcp_data_same") == 0) {
> + correct_payload[0] = big_tcp_fill_len() + calc_mss();
> + printf("data packets of same size past IP_MAXPACKET: ");
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "big_tcp_data_lrg_sml") == 0) {
> + correct_payload[0] = big_tcp_fill_len() + calc_mss() / 2;
> + printf("smaller last packet past IP_MAXPACKET: ");
> + check_recv_pkts(rxfd, correct_payload, 1);
> + } else if (strcmp(testname, "big_tcp_tcp_seq") == 0) {
> + correct_payload[0] = (big_tcp_fill_cnt() + 1) * calc_mss();
> + correct_payload[1] = calc_mss();
> + printf("aliased seq past IP_MAXPACKET doesn't coalesce: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> + } else if (strcmp(testname, "big_tcp_large_max") == 0) {
> + correct_payload[0] = big_tcp_large_cnt() * calc_mss();
> + correct_payload[1] = calc_mss();
> + printf("shouldn't coalesce past gro_max_size: ");
> + check_recv_pkts(rxfd, correct_payload, 2);
> +
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260913052419.77910-1-narcisav.kernel%40gmail.com
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH net-next v3] selftests: drv-net: add BIG TCP test cases
2026-09-14 5:26 ` netdev-bot+sashiko
@ 2026-09-16 1:07 ` Narcisa Vasile
0 siblings, 0 replies; 3+ messages in thread
From: Narcisa Vasile @ 2026-09-16 1:07 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: narcisav.kernel, netdev, kuba, daniel.zahka, andrew+netdev,
davem, edumazet, pabeni, shuah, horms, willemb, petrm,
anubhavsinggh, richardbgobert, linux-kselftest, linux-kernel
On Mon, Sep 14, 2026 at 05:26:37AM +0000, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 1 (1) · Low: 1
>
> - [Medium] The four new `big_tcp_*` variants are yielded by
> `_gro_variants()` in tools/testing/selftests/drivers/net/gro_lib.py…
> - [Low] The patch changes the failure-tolerance policy in `run_test()`
> from `test_name.startswith("large_")` to…
>
The tests can still be run for all 3 variants and the MTU setup is
useful for all modes. The commit seems clear enough.
> Pre-existing issues:
> - [Medium] `check_recv_pkts()` (tools/testing/selftests/net/lib/gro.c)
> reads `correct_payload[num_pkt]` once per received non-FIN frame with…
This is a valid pre-existing issue. I've sent a separate patch to net
with the fix, according to the guidance on fixes. That patch doesn't
conflict with this one.
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260913052419.77910-1-narcisav.kernel%40gmail.com
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-16 1:08 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-13 5:24 [PATCH net-next v3] selftests: drv-net: add BIG TCP test cases Narcisa Vasile
2026-09-14 5:26 ` netdev-bot+sashiko
2026-09-16 1:07 ` Narcisa Vasile
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®