]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tcp: add the ability to control max RTO
authorEric Dumazet <edumazet@google.com>
Fri, 7 Feb 2025 15:28:29 +0000 (15:28 +0000)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 11 Feb 2025 12:08:00 +0000 (13:08 +0100)
Currently, TCP stack uses a constant (120 seconds)
to limit the RTO value exponential growth.

Some applications want to set a lower value.

Add TCP_RTO_MAX_MS socket option to set a value (in ms)
between 1 and 120 seconds.

It is discouraged to change the socket rto max on a live
socket, as it might lead to unexpected disconnects.

Following patch is adding a netns sysctl to control the
default value at socket creation time.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Reviewed-by: Neal Cardwell <ncardwell@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Documentation/networking/net_cachelines/inet_connection_sock.rst
include/net/inet_connection_sock.h
include/net/tcp.h
include/uapi/linux/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c

index 4a15627fc93b8438342165a47e58c2ff10ce13d2..b2401aa7c45090b07c2262ac31a3584725f92233 100644 (file)
@@ -17,6 +17,7 @@ struct timer_list                   icsk_retransmit_timer  read_mostly
 struct timer_list                   icsk_delack_timer      read_mostly                             inet_csk_reset_xmit_timer,tcp_connect
 u32                                 icsk_rto               read_write                              tcp_cwnd_validate,tcp_schedule_loss_probe,tcp_connect_init,tcp_connect,tcp_write_xmit,tcp_push_one
 u32                                 icsk_rto_min
+u32                                 icsk_rto_max           read_mostly                             tcp_reset_xmit_timer
 u32                                 icsk_delack_max
 u32                                 icsk_pmtu_cookie       read_write                              tcp_sync_mss,tcp_current_mss,tcp_send_syn_data,tcp_connect_init,tcp_connect
 struct tcp_congestion_ops           icsk_ca_ops            read_write                              tcp_cwnd_validate,tcp_tso_segs,tcp_ca_dst_init,tcp_connect_init,tcp_connect,tcp_write_xmit
index 055aa80b05c6da8c36b6acf2709ee116136918e6..d9978ffacc970efd308d0011a094aec41b561e65 100644 (file)
@@ -90,6 +90,7 @@ struct inet_connection_sock {
        struct timer_list         icsk_delack_timer;
        __u32                     icsk_rto;
        __u32                     icsk_rto_min;
+       u32                       icsk_rto_max;
        __u32                     icsk_delack_max;
        __u32                     icsk_pmtu_cookie;
        const struct tcp_congestion_ops *icsk_ca_ops;
index 56557b0104e3f0984125532332a73e067ef1a118..7fd2d7fa4532be29d0779354b9f555107c1790a5 100644 (file)
@@ -143,8 +143,9 @@ static_assert((1 << ATO_BITS) > TCP_DELACK_MAX);
 #define TCP_DELACK_MIN 4U
 #define TCP_ATO_MIN    4U
 #endif
-#define TCP_RTO_MAX    ((unsigned)(120*HZ))
-#define TCP_RTO_MIN    ((unsigned)(HZ/5))
+#define TCP_RTO_MAX_SEC 120
+#define TCP_RTO_MAX    ((unsigned)(TCP_RTO_MAX_SEC * HZ))
+#define TCP_RTO_MIN    ((unsigned)(HZ / 5))
 #define TCP_TIMEOUT_MIN        (2U) /* Min timeout for TCP timers in jiffies */
 
 #define TCP_TIMEOUT_MIN_US (2*USEC_PER_MSEC) /* Min TCP timeout in microsecs */
@@ -740,10 +741,14 @@ int tcp_mtu_to_mss(struct sock *sk, int pmtu);
 int tcp_mss_to_mtu(struct sock *sk, int mss);
 void tcp_mtup_init(struct sock *sk);
 
+static inline unsigned int tcp_rto_max(const struct sock *sk)
+{
+       return READ_ONCE(inet_csk(sk)->icsk_rto_max);
+}
+
 static inline void tcp_bound_rto(struct sock *sk)
 {
-       if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)
-               inet_csk(sk)->icsk_rto = TCP_RTO_MAX;
+       inet_csk(sk)->icsk_rto = min(inet_csk(sk)->icsk_rto, tcp_rto_max(sk));
 }
 
 static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
@@ -1428,7 +1433,8 @@ static inline void tcp_reset_xmit_timer(struct sock *sk,
 {
        if (pace_delay)
                when += tcp_pacing_delay(sk);
-       inet_csk_reset_xmit_timer(sk, what, when, TCP_RTO_MAX);
+       inet_csk_reset_xmit_timer(sk, what, when,
+                                 tcp_rto_max(sk));
 }
 
 /* Something is really bad, we could not queue an additional packet,
index dbf896f3146c5fb2a35d68f8633fda4e0b74a6fb..32a27b4a5020ad066d5e443b04a3a2e34fbf6178 100644 (file)
@@ -136,6 +136,7 @@ enum {
 #define TCP_AO_REPAIR          42      /* Get/Set SNEs and ISNs */
 
 #define TCP_IS_MPTCP           43      /* Is MPTCP being used? */
+#define TCP_RTO_MAX_MS         44      /* max rto time in ms */
 
 #define TCP_REPAIR_ON          1
 #define TCP_REPAIR_OFF         0
index 2021f2709ec32fc3a439540f288d181a6dab274a..3bb8fbbb01d98c1b0d0bb19e8b2d049d06704e46 100644 (file)
@@ -432,6 +432,10 @@ void tcp_init_sock(struct sock *sk)
        INIT_LIST_HEAD(&tp->tsorted_sent_queue);
 
        icsk->icsk_rto = TCP_TIMEOUT_INIT;
+
+       /* Use a sysctl ? */
+       icsk->icsk_rto_max = TCP_RTO_MAX;
+
        rto_min_us = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_rto_min_us);
        icsk->icsk_rto_min = usecs_to_jiffies(rto_min_us);
        icsk->icsk_delack_max = TCP_DELACK_MAX;
@@ -3807,6 +3811,11 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
                           secs_to_retrans(val, TCP_TIMEOUT_INIT / HZ,
                                           TCP_RTO_MAX / HZ));
                return 0;
+       case TCP_RTO_MAX_MS:
+               if (val < MSEC_PER_SEC || val > TCP_RTO_MAX_SEC * MSEC_PER_SEC)
+                       return -EINVAL;
+               WRITE_ONCE(inet_csk(sk)->icsk_rto_max, msecs_to_jiffies(val));
+               return 0;
        }
 
        sockopt_lock_sock(sk);
@@ -4643,6 +4652,9 @@ zerocopy_rcv_out:
        case TCP_IS_MPTCP:
                val = 0;
                break;
+       case TCP_RTO_MAX_MS:
+               val = jiffies_to_msecs(tcp_rto_max(sk));
+               break;
        default:
                return -ENOPROTOOPT;
        }
index 9153fae2ebd817d264386b2ee0ee87a83d7e2992..4686783b70defe0457571efd72d41ac88c528f7b 100644 (file)
@@ -3558,7 +3558,7 @@ static void tcp_ack_probe(struct sock *sk)
                 * This function is not for random using!
                 */
        } else {
-               unsigned long when = tcp_probe0_when(sk, TCP_RTO_MAX);
+               unsigned long when = tcp_probe0_when(sk, tcp_rto_max(sk));
 
                when = tcp_clamp_probe0_to_user_timeout(sk, when);
                tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, true);
index e065f7097611b70f41e75502d7d6f9248af1c85f..06fb0123d2d60e22f19ea48b73ac3668c51465a2 100644 (file)
@@ -458,7 +458,7 @@ void tcp_ld_RTO_revert(struct sock *sk, u32 seq)
 
        icsk->icsk_backoff--;
        icsk->icsk_rto = tp->srtt_us ? __tcp_set_rto(tp) : TCP_TIMEOUT_INIT;
-       icsk->icsk_rto = inet_csk_rto_backoff(icsk, TCP_RTO_MAX);
+       icsk->icsk_rto = inet_csk_rto_backoff(icsk, tcp_rto_max(sk));
 
        tcp_mstamp_refresh(tp);
        delta_us = (u32)(tp->tcp_mstamp - tcp_skb_timestamp_us(skb));
index 3623d19b7c6ed9d485bbc329737b03bcfc69a406..464232a0d63796d5ac7437dafb5f496d5c05bc1c 100644 (file)
@@ -4251,7 +4251,7 @@ void __tcp_send_ack(struct sock *sk, u32 rcv_nxt)
                unsigned long delay;
 
                delay = TCP_DELACK_MAX << icsk->icsk_ack.retry;
-               if (delay < TCP_RTO_MAX)
+               if (delay < tcp_rto_max(sk))
                        icsk->icsk_ack.retry++;
                inet_csk_schedule_ack(sk);
                icsk->icsk_ack.ato = TCP_ATO_MIN;
@@ -4391,7 +4391,7 @@ void tcp_send_probe0(struct sock *sk)
        if (err <= 0) {
                if (icsk->icsk_backoff < READ_ONCE(net->ipv4.sysctl_tcp_retries2))
                        icsk->icsk_backoff++;
-               timeout = tcp_probe0_when(sk, TCP_RTO_MAX);
+               timeout = tcp_probe0_when(sk, tcp_rto_max(sk));
        } else {
                /* If packet was not sent due to local congestion,
                 * Let senders fight for local resources conservatively.
index 6472f560f653909f37461ca7f4f3625476347ea6..c0e601e4f39c146d7859a59d4f2b2d07827e9d12 100644 (file)
@@ -109,7 +109,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
 
        /* If peer does not open window for long time, or did not transmit
         * anything for long time, penalize it. */
-       if ((s32)(tcp_jiffies32 - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+       if ((s32)(tcp_jiffies32 - tp->lsndtime) > 2*tcp_rto_max(sk) || !do_reset)
                shift++;
 
        /* If some dubious ICMP arrived, penalize even more. */
@@ -189,12 +189,12 @@ static unsigned int tcp_model_timeout(struct sock *sk,
 {
        unsigned int linear_backoff_thresh, timeout;
 
-       linear_backoff_thresh = ilog2(TCP_RTO_MAX / rto_base);
+       linear_backoff_thresh = ilog2(tcp_rto_max(sk) / rto_base);
        if (boundary <= linear_backoff_thresh)
                timeout = ((2 << boundary) - 1) * rto_base;
        else
                timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +
-                       (boundary - linear_backoff_thresh) * TCP_RTO_MAX;
+                       (boundary - linear_backoff_thresh) * tcp_rto_max(sk);
        return jiffies_to_msecs(timeout);
 }
 /**
@@ -268,7 +268,7 @@ static int tcp_write_timeout(struct sock *sk)
 
                retry_until = READ_ONCE(net->ipv4.sysctl_tcp_retries2);
                if (sock_flag(sk, SOCK_DEAD)) {
-                       const bool alive = icsk->icsk_rto < TCP_RTO_MAX;
+                       const bool alive = icsk->icsk_rto < tcp_rto_max(sk);
 
                        retry_until = tcp_orphan_retries(sk, alive);
                        do_reset = alive ||
@@ -416,7 +416,8 @@ static void tcp_probe_timer(struct sock *sk)
        }
        max_probes = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_retries2);
        if (sock_flag(sk, SOCK_DEAD)) {
-               const bool alive = inet_csk_rto_backoff(icsk, TCP_RTO_MAX) < TCP_RTO_MAX;
+               unsigned int rto_max = tcp_rto_max(sk);
+               const bool alive = inet_csk_rto_backoff(icsk, rto_max) < rto_max;
 
                max_probes = tcp_orphan_retries(sk, alive);
                if (!alive && icsk->icsk_backoff >= max_probes)
@@ -492,7 +493,7 @@ static bool tcp_rtx_probe0_timed_out(const struct sock *sk,
        const struct inet_connection_sock *icsk = inet_csk(sk);
        u32 user_timeout = READ_ONCE(icsk->icsk_user_timeout);
        const struct tcp_sock *tp = tcp_sk(sk);
-       int timeout = TCP_RTO_MAX * 2;
+       int timeout = tcp_rto_max(sk) * 2;
        s32 rcv_delta;
 
        if (user_timeout) {
@@ -665,7 +666,7 @@ out_reset_timer:
                icsk->icsk_backoff = 0;
                icsk->icsk_rto = clamp(__tcp_set_rto(tp),
                                       tcp_rto_min(sk),
-                                      TCP_RTO_MAX);
+                                      tcp_rto_max(sk));
        } else if (sk->sk_state != TCP_SYN_SENT ||
                   tp->total_rto >
                   READ_ONCE(net->ipv4.sysctl_tcp_syn_linear_timeouts)) {
@@ -673,7 +674,7 @@ out_reset_timer:
                 * activated.
                 */
                icsk->icsk_backoff++;
-               icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
+               icsk->icsk_rto = min(icsk->icsk_rto << 1, tcp_rto_max(sk));
        }
        tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                             tcp_clamp_rto_to_user_timeout(sk), false);