mirror of https://github.com/torvalds/linux.git
tcp: add the ability to control max RTO
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>
This commit is contained in:
parent
48b69b4c7e
commit
54a378f434
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ int do_tcp_getsockopt(struct sock *sk, int level,
|
|||
case TCP_IS_MPTCP:
|
||||
val = 0;
|
||||
break;
|
||||
case TCP_RTO_MAX_MS:
|
||||
val = jiffies_to_msecs(tcp_rto_max(sk));
|
||||
break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 @@ void tcp_retransmit_timer(struct sock *sk)
|
|||
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 @@ void tcp_retransmit_timer(struct sock *sk)
|
|||
* 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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue