[PATCH] tcp:provide 2 options for MSS/TSO window size: half the window or full window.

From: He.kai Zhang.zihan

Date: Wed Nov 19 2025 - 11:30:26 EST


From d65f0b89c2cb8685a1ebfb728dc45736bda038ed Mon Sep 17 00:00:00 2001
From: "He.kai Zhang.zihan" <hk517j@xxxxxxxxxxx>
Date: Wed, 19 Nov 2025 21:11:26 +0800
Subject: [PATCH] tcp:provide 2 options for MSS/TSO window size: half the
window or full window.

In high-latency scenarios, limiting MSS/TSO to half the receive
window size is appropriate.In low-latency scenarios,utilizing
the full window size is optional. For example, in low-latency
environments, when an embedded device has a receive window of
one MSS size (e.g., 1460 bytes),Linux can transmit a full MSS
to the device in a single segment,rather than splitting it into
two separate transmissions. In fact, Linux can't but Win10 can.
So provide a configuration optionto fix it.

Signed-off-by: He.kai Zhang.zihan <hk517j@xxxxxxxxxxx>
---
include/net/tcp.h | 31 +++++++++++++++++++++++++++++++
net/ipv4/Kconfig | 9 +++++++++
net/ipv4/tcp.c | 2 +-
net/ipv4/tcp_output.c | 2 +-
4 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 526a26e7a..9da7f5287 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -777,6 +777,37 @@ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
return pktsize;
}

+
+/* Bound MSS / TSO packet size with the max of the window */
+static inline int tcp_bound_to_max_wnd(struct tcp_sock *tp, int pktsize)
+{
+ int tmp;
+
+ tmp = tp->max_window;
+
+ if (tmp && pktsize > tmp)
+ return max_t(int, tmp, 68U - tp->tcp_header_len);
+ else
+ return pktsize;
+}
+
+
+#ifdef CONFIG_TCP_BOUND_HALF_WINDOW
+
+static inline int tcp_bound_to_wnd(struct tcp_sock *tp, int pktsize)
+{
+ return tcp_bound_to_half_wnd(tp, pktsize);
+}
+
+#else
+
+static inline int tcp_bound_to_wnd(struct tcp_sock *tp, int pktsize)
+{
+ return tcp_bound_to_max_wnd(tp, pktsize);
+}
+
+#endif
+
/* tcp.c */
void tcp_get_info(struct sock *, struct tcp_info *);

diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 12850a277..4365e31e8 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -679,6 +679,15 @@ config TCP_CONG_BBR
AQM schemes that do not provide a delay signal. It requires the fq
("Fair Queue") pacing packet scheduler.

+config TCP_BOUND_HALF_WINDOW
+ bool "Bound MSS / TSO packet size with the half of the window"
+ default y
+ help
+ Use the default half-window bounding for MSS / TSO packet.
+ In high-latency scenarios, limiting MSS/TSO to half the receive
+ window size is appropriate.In low-latency scenarios,utilizing
+ the full window size is optional.
+
choice
prompt "Default TCP congestion control"
default DEFAULT_CUBIC
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index ba36f558f..bec8501c4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -924,7 +924,7 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
return mss_now;

/* Note : tcp_tso_autosize() will eventually split this later */
- new_size_goal = tcp_bound_to_half_wnd(tp, sk->sk_gso_max_size);
+ new_size_goal = tcp_bound_to_wnd(tp, sk->sk_gso_max_size);

/* We try hard to avoid divides here */
size_goal = tp->gso_segs * mss_now;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 16251d8e1..1773075b9 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1849,7 +1849,7 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
icsk->icsk_mtup.search_high = pmtu;

mss_now = tcp_mtu_to_mss(sk, pmtu);
- mss_now = tcp_bound_to_half_wnd(tp, mss_now);
+ mss_now = tcp_bound_to_wnd(tp, mss_now);

/* And store cached results */
icsk->icsk_pmtu_cookie = pmtu;
--
2.34.1