All of lore.kernel.org
 help / color / mirror / Atom feed
From: Shashank Mohan Jain <jain.sm@gmail.com>
To: Eric Dumazet <edumazet@kernel.org>,
	Neal Cardwell <ncardwell@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	"David S. Miller" <davem@davemloft.net>
Cc: Kuniyuki Iwashima <kuniyu@google.com>,
	Simon Horman <horms@kernel.org>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net-next v3 1/2] tcp: keep the route window metric when SO_RCVBUF is set before connect
Date: Mon, 12 Oct 2026 18:56:21 +0530	[thread overview]
Message-ID: <20261012132622.48550-2-jain.sm@gmail.com> (raw)
In-Reply-To: <20261012132622.48550-1-jain.sm@gmail.com>

Since commit b025461303d8 ("tcp: update window_clamp when SO_RCVBUF is
set"), setsockopt(SO_RCVBUF) writes tp->window_clamp in every socket
state through tcp_set_window_clamp().

tcp_connect_init() and tcp_openreq_init_rwin() treat any non-zero
window_clamp as a clamp the user asked for and, in that case, skip the
route window metric (RTAX_WINDOW, "ip route ... window N"). A clamp left
behind by an SO_RCVBUF call therefore has two observable effects:

 (1) on a route that carries an RTAX_WINDOW metric, SO_RCVBUF set before
     connect() (or on a listener) changes the window and window scale
     advertised in the SYN / SYN-ACK, because the route metric is no
     longer applied;

 (2) a window clamp the application set with setsockopt(TCP_WINDOW_CLAMP)
     is overwritten by a later SO_RCVBUF on the still unconnected socket
     or on a listener.

Without an RTAX_WINDOW metric there is no change: both before and after
this patch the advertised window and scale are derived from sk_rcvbuf
(tcp_full_space()), so only (1) and (2) are affected.

Keying the decision off the socket state is not enough, because the
socket is already in TCP_SYN_SENT before tcp_connect_init() runs.
SO_RCVBUF applied in that gap - through TCP_FASTOPEN_CONNECT before the
first write(), or through bpf_setsockopt() in a
BPF_SOCK_OPS_TCP_CONNECT_CB program (as samples/bpf/tcp_clamp_kern.c and
friends do) - still lands before tcp_connect_init() and still overrides
the metric.

Record instead whether window_clamp was set explicitly by the user via
setsockopt(TCP_WINDOW_CLAMP), in a new tp->window_clamp_user bit, and key
tcp_connect_init() and tcp_openreq_init_rwin() off that bit rather than
off a non-zero window_clamp. The route metric is then applied on all
pre-connection paths, regardless of socket state, whenever the clamp was
not set by the user; a passive socket inherits the decision from its
listener. SO_RCVBUF no longer overwrites a genuine TCP_WINDOW_CLAMP
either. When SOCK_RCVBUF_LOCK is set both functions still clamp
window_clamp to tcp_full_space(), and without a user clamp SO_RCVBUF
keeps updating window_clamp, so SO_RCVBUF on an established socket still
grows the advertised window as b025461303d8 intended.

Example: two netns joined by a veth pair, "ip route ... window 4096" on
the route to the peer, SO_RCVBUF set to 1 MiB before connect():

  before:  SYN win 65535, wscale 5
  after:   SYN win 2920,  wscale 0

(the window scale option is still sent, with a shift of 0.)

Fixes: b025461303d8 ("tcp: update window_clamp when SO_RCVBUF is set")
Assisted-by: LLM
Signed-off-by: Shashank Mohan Jain <jain.sm@gmail.com>
---
 include/linux/tcp.h      |  3 ++-
 net/ipv4/tcp.c           | 12 ++++++++++++
 net/ipv4/tcp_minisocks.c |  7 +++++--
 net/ipv4/tcp_output.c    |  2 +-
 4 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index e4d1720f4..2324841e5 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -301,7 +301,8 @@ struct tcp_sock {
  *	0x5?10 << 16 + snd_wnd in net byte order
  */
 	u8	nonagle     : 4,/* Disable Nagle algorithm?             */
-		rate_app_limited:1;  /* rate_{delivered,interval_us} limited? */
+		rate_app_limited:1,  /* rate_{delivered,interval_us} limited? */
+		window_clamp_user:1; /* TCP_WINDOW_CLAMP set by the user */
 	u8	received_ce_pending:4, /* Not yet transmit cnt of received_ce */
 		accecn_opt_sent_w_dsack:1,/* Sent ACCECN opt in previous ACK w/ D-SACK */
 		unused2:3;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index f4c9101e5..5f364928b 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1858,6 +1858,15 @@ int tcp_set_rcvlowat(struct sock *sk, int val)
 
 void tcp_set_rcvbuf(struct sock *sk, int val)
 {
+	/* A window clamp set explicitly via setsockopt(TCP_WINDOW_CLAMP)
+	 * takes precedence: SO_RCVBUF must not overwrite it.  Without such
+	 * a user clamp, keep updating window_clamp from the buffer size so
+	 * that SO_RCVBUF on an established socket still grows the advertised
+	 * window (commit b025461303d8).
+	 */
+	if (tcp_sk(sk)->window_clamp_user)
+		return;
+
 	tcp_set_window_clamp(sk, tcp_win_from_space(sk, val));
 }
 
@@ -3427,6 +3436,7 @@ int tcp_disconnect(struct sock *sk, int flags)
 	tp->is_cwnd_limited = 0;
 	tp->max_packets_out = 0;
 	tp->window_clamp = 0;
+	tp->window_clamp_user = 0;
 	tp->delivered = 0;
 	tp->delivered_ce = 0;
 	tp->accecn_fail_mode = 0;
@@ -4056,6 +4066,8 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
 
 	case TCP_WINDOW_CLAMP:
 		err = tcp_set_window_clamp(sk, val);
+		if (!err)
+			tp->window_clamp_user = !!val;
 		break;
 
 	case TCP_QUICKACK:
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 0ddfd5af6..c24dc975a 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -442,8 +442,11 @@ void tcp_openreq_init_rwin(struct request_sock *req,
 
 	mss = tcp_mss_clamp(tp, tcp_dst_advmss(dst));
 	window_clamp = READ_ONCE(tp->window_clamp);
-	/* Set this up on the first call only */
-	req->rsk_window_clamp = window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+	/* Set this up on the first call only.  Honour a window clamp the
+	 * listener set via TCP_WINDOW_CLAMP; otherwise use the route metric.
+	 */
+	req->rsk_window_clamp = tp->window_clamp_user ? window_clamp :
+				dst_metric(dst, RTAX_WINDOW);
 
 	/* limit the window selection if the user enforce a smaller rx buffer */
 	if (sk_listener->sk_userlocks & SOCK_RCVBUF_LOCK &&
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index c8865205c..21b4ae303 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -4137,7 +4137,7 @@ static void tcp_connect_init(struct sock *sk)
 
 	tcp_ca_dst_init(sk, dst);
 
-	if (!tp->window_clamp)
+	if (!tp->window_clamp_user)
 		WRITE_ONCE(tp->window_clamp, dst_metric(dst, RTAX_WINDOW));
 	tp->advmss = tcp_mss_clamp(tp, tcp_dst_advmss(dst));
 
-- 
2.43.0


  reply	other threads:[~2026-10-12 13:26 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-12 13:26 [PATCH net-next v3 0/2] tcp: keep the route window metric when SO_RCVBUF is set before connect Shashank Mohan Jain
2026-10-12 13:26 ` Shashank Mohan Jain [this message]
2026-10-13 13:52   ` [PATCH net-next v3 1/2] " netdev-bot+sashiko
2026-10-12 13:26 ` [PATCH net-next v3 2/2] selftests/net/packetdrill: cover SO_RCVBUF vs the route window clamp Shashank Mohan Jain
2026-10-13 13:52   ` netdev-bot+sashiko
2026-10-12 13:30 ` [PATCH net-next v3 0/2] tcp: keep the route window metric when SO_RCVBUF is set before connect netdev-bot+sinfo
2026-10-12 13:34   ` shashank Jain
2026-10-20  2:13     ` Jakub Kicinski
2026-10-20  2:46       ` shashank Jain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261012132622.48550-2-jain.sm@gmail.com \
    --to=jain.sm@gmail.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ncardwell@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.