Commit bcc843bb authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tcp-fix-receive-autotune-again'

Matthieu Baerts says:

====================
tcp: fix receive autotune again

Neal Cardwell found that recent kernels were having RWIN limited
issues, even when net.ipv4.tcp_rmem[2] was set to a very big value like
512MB.

He suspected that tcp_stream default buffer size (64KB) was triggering
heuristic added in ea33537d ("tcp: add receive queue awareness
in tcp_rcv_space_adjust()").

After more testing, it turns out the bug was added earlier
with commit 65c52878 ("tcp: fix sk_rcvbuf overshoot").

I forgot once again that DRS has one RTT latency.

MPTCP also got the same issue.

This series :
 - Prevents calling tcp_rcvbuf_grow() on some MPTCP subflows.
 - adds rcv_ssthresh, window_clamp and rcv_wnd to trace_tcp_rcvbuf_grow().
 - Refactors code in a patch with no functional changes.
 - Fixes the issue in the final patch.
====================

Link: https://patch.msgid.link/20251028-net-tcp-recv-autotune-v3-0-74b43ba4c84c@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents f99c5792 aa251c84
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -370,7 +370,7 @@ void tcp_delack_timer_handler(struct sock *sk);
int tcp_ioctl(struct sock *sk, int cmd, int *karg);
enum skb_drop_reason tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb);
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb);
void tcp_rcvbuf_grow(struct sock *sk);
void tcp_rcvbuf_grow(struct sock *sk, u32 newval);
void tcp_rcv_space_adjust(struct sock *sk);
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
void tcp_twsk_destructor(struct sock *sk);
+9 −0
Original line number Diff line number Diff line
@@ -218,6 +218,9 @@ TRACE_EVENT(tcp_rcvbuf_grow,
		__field(__u32, space)
		__field(__u32, ooo_space)
		__field(__u32, rcvbuf)
		__field(__u32, rcv_ssthresh)
		__field(__u32, window_clamp)
		__field(__u32, rcv_wnd)
		__field(__u8, scaling_ratio)
		__field(__u16, sport)
		__field(__u16, dport)
@@ -245,6 +248,9 @@ TRACE_EVENT(tcp_rcvbuf_grow,
				     tp->rcv_nxt;

		__entry->rcvbuf = sk->sk_rcvbuf;
		__entry->rcv_ssthresh = tp->rcv_ssthresh;
		__entry->window_clamp = tp->window_clamp;
		__entry->rcv_wnd = tp->rcv_wnd;
		__entry->scaling_ratio = tp->scaling_ratio;
		__entry->sport = ntohs(inet->inet_sport);
		__entry->dport = ntohs(inet->inet_dport);
@@ -264,11 +270,14 @@ TRACE_EVENT(tcp_rcvbuf_grow,
	),

	TP_printk("time=%u rtt_us=%u copied=%u inq=%u space=%u ooo=%u scaling_ratio=%u rcvbuf=%u "
		  "rcv_ssthresh=%u window_clamp=%u rcv_wnd=%u "
		  "family=%s sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 "
		  "saddrv6=%pI6c daddrv6=%pI6c skaddr=%p sock_cookie=%llx",
		  __entry->time, __entry->rtt_us, __entry->copied,
		  __entry->inq, __entry->space, __entry->ooo_space,
		  __entry->scaling_ratio, __entry->rcvbuf,
		  __entry->rcv_ssthresh, __entry->window_clamp,
		  __entry->rcv_wnd,
		  show_family_name(__entry->family),
		  __entry->sport, __entry->dport,
		  __entry->saddr, __entry->daddr,
+14 −7
Original line number Diff line number Diff line
@@ -891,18 +891,27 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
	}
}

void tcp_rcvbuf_grow(struct sock *sk)
void tcp_rcvbuf_grow(struct sock *sk, u32 newval)
{
	const struct net *net = sock_net(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	int rcvwin, rcvbuf, cap;
	u32 rcvwin, rcvbuf, cap, oldval;
	u64 grow;

	oldval = tp->rcvq_space.space;
	tp->rcvq_space.space = newval;

	if (!READ_ONCE(net->ipv4.sysctl_tcp_moderate_rcvbuf) ||
	    (sk->sk_userlocks & SOCK_RCVBUF_LOCK))
		return;

	/* DRS is always one RTT late. */
	rcvwin = newval << 1;

	/* slow start: allow the sender to double its rate. */
	rcvwin = tp->rcvq_space.space << 1;
	grow = (u64)rcvwin * (newval - oldval);
	do_div(grow, oldval);
	rcvwin += grow << 1;

	if (!RB_EMPTY_ROOT(&tp->out_of_order_queue))
		rcvwin += TCP_SKB_CB(tp->ooo_last_skb)->end_seq - tp->rcv_nxt;
@@ -943,9 +952,7 @@ void tcp_rcv_space_adjust(struct sock *sk)

	trace_tcp_rcvbuf_grow(sk, time);

	tp->rcvq_space.space = copied;

	tcp_rcvbuf_grow(sk);
	tcp_rcvbuf_grow(sk, copied);

new_measure:
	tp->rcvq_space.seq = tp->copied_seq;
@@ -5270,7 +5277,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
	}
	/* do not grow rcvbuf for not-yet-accepted or orphaned sockets. */
	if (sk->sk_socket)
		tcp_rcvbuf_grow(sk);
		tcp_rcvbuf_grow(sk, tp->rcvq_space.space);
}

static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb,
+17 −9
Original line number Diff line number Diff line
@@ -194,17 +194,26 @@ static bool mptcp_ooo_try_coalesce(struct mptcp_sock *msk, struct sk_buff *to,
 * - mptcp does not maintain a msk-level window clamp
 * - returns true when  the receive buffer is actually updated
 */
static bool mptcp_rcvbuf_grow(struct sock *sk)
static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)
{
	struct mptcp_sock *msk = mptcp_sk(sk);
	const struct net *net = sock_net(sk);
	int rcvwin, rcvbuf, cap;
	u32 rcvwin, rcvbuf, cap, oldval;
	u64 grow;

	oldval = msk->rcvq_space.space;
	msk->rcvq_space.space = newval;
	if (!READ_ONCE(net->ipv4.sysctl_tcp_moderate_rcvbuf) ||
	    (sk->sk_userlocks & SOCK_RCVBUF_LOCK))
		return false;

	rcvwin = msk->rcvq_space.space << 1;
	/* DRS is always one RTT late. */
	rcvwin = newval << 1;

	/* slow start: allow the sender to double its rate. */
	grow = (u64)rcvwin * (newval - oldval);
	do_div(grow, oldval);
	rcvwin += grow << 1;

	if (!RB_EMPTY_ROOT(&msk->out_of_order_queue))
		rcvwin += MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq - msk->ack_seq;
@@ -334,7 +343,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
	skb_set_owner_r(skb, sk);
	/* do not grow rcvbuf for not-yet-accepted or orphaned sockets. */
	if (sk->sk_socket)
		mptcp_rcvbuf_grow(sk);
		mptcp_rcvbuf_grow(sk, msk->rcvq_space.space);
}

static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
@@ -2049,9 +2058,7 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied)
	if (msk->rcvq_space.copied <= msk->rcvq_space.space)
		goto new_measure;

	msk->rcvq_space.space = msk->rcvq_space.copied;
	if (mptcp_rcvbuf_grow(sk)) {

	if (mptcp_rcvbuf_grow(sk, msk->rcvq_space.copied)) {
		/* Make subflows follow along.  If we do not do this, we
		 * get drops at subflow level if skbs can't be moved to
		 * the mptcp rx queue fast enough (announced rcv_win can
@@ -2063,8 +2070,9 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied)

			ssk = mptcp_subflow_tcp_sock(subflow);
			slow = lock_sock_fast(ssk);
			tcp_sk(ssk)->rcvq_space.space = msk->rcvq_space.copied;
			tcp_rcvbuf_grow(ssk);
			/* subflows can be added before tcp_init_transfer() */
			if (tcp_sk(ssk)->rcvq_space.space)
				tcp_rcvbuf_grow(ssk, msk->rcvq_space.copied);
			unlock_sock_fast(ssk, slow);
		}
	}