tcp: fix mid stream window clamp.
authorPaolo Abeni <pabeni@redhat.com>
Mon, 4 Dec 2023 16:08:05 +0000 (17:08 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 Dec 2023 17:45:07 +0000 (18:45 +0100)
[ Upstream commit 58d3aade20cdddbac6c9707ac0f3f5f8c1278b74 ]

After the blamed commit below, if the user-space application performs
window clamping when tp->rcv_wnd is 0, the TCP socket will never be
able to announce a non 0 receive window, even after completely emptying
the receive buffer and re-setting the window clamp to higher values.

Refactor tcp_set_window_clamp() to address the issue: when the user
decreases the current clamp value, set rcv_ssthresh according to the
same logic used at buffer initialization, but ensuring reserved mem
provisioning.

To avoid code duplication factor-out the relevant bits from
tcp_adjust_rcv_ssthresh() in a new helper and reuse it in the above
scenario.

When increasing the clamp value, give the rcv_ssthresh a chance to grow
according to previously implemented heuristic.

Fixes: 3aa7857fe1d7 ("tcp: enable mid stream window clamp")
Reported-by: David Gibson <david@gibson.dropbear.id.au>
Reported-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://lore.kernel.org/r/705dad54e6e6e9a010e571bf58e0b35a8ae70503.1701706073.git.pabeni@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/tcp.h
net/ipv4/tcp.c

index 0239e81..a88bf8f 100644 (file)
@@ -1480,17 +1480,22 @@ static inline int tcp_full_space(const struct sock *sk)
        return tcp_win_from_space(sk, READ_ONCE(sk->sk_rcvbuf));
 }
 
-static inline void tcp_adjust_rcv_ssthresh(struct sock *sk)
+static inline void __tcp_adjust_rcv_ssthresh(struct sock *sk, u32 new_ssthresh)
 {
        int unused_mem = sk_unused_reserved_mem(sk);
        struct tcp_sock *tp = tcp_sk(sk);
 
-       tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
+       tp->rcv_ssthresh = min(tp->rcv_ssthresh, new_ssthresh);
        if (unused_mem)
                tp->rcv_ssthresh = max_t(u32, tp->rcv_ssthresh,
                                         tcp_win_from_space(sk, unused_mem));
 }
 
+static inline void tcp_adjust_rcv_ssthresh(struct sock *sk)
+{
+       __tcp_adjust_rcv_ssthresh(sk, 4U * tcp_sk(sk)->advmss);
+}
+
 void tcp_cleanup_rbuf(struct sock *sk, int copied);
 void __tcp_cleanup_rbuf(struct sock *sk, int copied);
 
index 3d3a24f..ec46d74 100644 (file)
@@ -3368,9 +3368,25 @@ int tcp_set_window_clamp(struct sock *sk, int val)
                        return -EINVAL;
                tp->window_clamp = 0;
        } else {
-               tp->window_clamp = val < SOCK_MIN_RCVBUF / 2 ?
-                       SOCK_MIN_RCVBUF / 2 : val;
-               tp->rcv_ssthresh = min(tp->rcv_wnd, tp->window_clamp);
+               u32 new_rcv_ssthresh, old_window_clamp = tp->window_clamp;
+               u32 new_window_clamp = val < SOCK_MIN_RCVBUF / 2 ?
+                                               SOCK_MIN_RCVBUF / 2 : val;
+
+               if (new_window_clamp == old_window_clamp)
+                       return 0;
+
+               tp->window_clamp = new_window_clamp;
+               if (new_window_clamp < old_window_clamp) {
+                       /* need to apply the reserved mem provisioning only
+                        * when shrinking the window clamp
+                        */
+                       __tcp_adjust_rcv_ssthresh(sk, tp->window_clamp);
+
+               } else {
+                       new_rcv_ssthresh = min(tp->rcv_wnd, tp->window_clamp);
+                       tp->rcv_ssthresh = max(new_rcv_ssthresh,
+                                              tp->rcv_ssthresh);
+               }
        }
        return 0;
 }