diff options
| author | 2019-01-16 15:05:30 -0800 | |
|---|---|---|
| committer | 2019-01-17 15:12:26 -0800 | |
| commit | 7ae189759cc48cf8b54beebff566e9fd2d4e7d7c (patch) | |
| tree | 352db05710fad943494d48107a6fb4d7555d0a70 | |
| parent | tcp: always timestamp on every skb transmission (diff) | |
| download | linux-dev-7ae189759cc48cf8b54beebff566e9fd2d4e7d7c.tar.xz linux-dev-7ae189759cc48cf8b54beebff566e9fd2d4e7d7c.zip  | |
tcp: always set retrans_stamp on recovery
Previously TCP socket's retrans_stamp is not set if the
retransmission has failed to send. As a result if a socket is
experiencing local issues to retransmit packets, determining when
to abort a socket is complicated w/o knowning the starting time of
the recovery since retrans_stamp may remain zero.
This complication causes sub-optimal behavior that TCP may use the
latest, instead of the first, retransmission time to compute the
elapsed time of a stalling connection due to local issues. Then TCP
may disrecard TCP retries settings and keep retrying until it finally
succeed: not a good idea when the local host is already strained.
The simple fix is to always timestamp the start of a recovery.
It's worth noting that retrans_stamp is also used to compare echo
timestamp values to detect spurious recovery. This patch does
not break that because retrans_stamp is still later than when the
original packet was sent.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Neal Cardwell <ncardwell@google.com>
Reviewed-by: Soheil Hassas Yeganeh <soheil@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/ipv4/tcp_output.c | 9 | ||||
| -rw-r--r-- | net/ipv4/tcp_timer.c | 23 | 
2 files changed, 7 insertions, 25 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 57a56e205070..d2d494c74811 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2963,13 +2963,12 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)  #endif  		TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;  		tp->retrans_out += tcp_skb_pcount(skb); - -		/* Save stamp of the first retransmit. */ -		if (!tp->retrans_stamp) -			tp->retrans_stamp = tcp_skb_timestamp(skb); -  	} +	/* Save stamp of the first (attempted) retransmit. */ +	if (!tp->retrans_stamp) +		tp->retrans_stamp = tcp_skb_timestamp(skb); +  	if (tp->undo_retrans < 0)  		tp->undo_retrans = 0;  	tp->undo_retrans += tcp_skb_pcount(skb); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index e7d09e3705b8..1e61f0bd6e24 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -22,28 +22,14 @@  #include <linux/gfp.h>  #include <net/tcp.h> -static u32 tcp_retransmit_stamp(const struct sock *sk) -{ -	u32 start_ts = tcp_sk(sk)->retrans_stamp; - -	if (unlikely(!start_ts)) { -		struct sk_buff *head = tcp_rtx_queue_head(sk); - -		if (!head) -			return 0; -		start_ts = tcp_skb_timestamp(head); -	} -	return start_ts; -} -  static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk)  {  	struct inet_connection_sock *icsk = inet_csk(sk);  	u32 elapsed, start_ts;  	s32 remaining; -	start_ts = tcp_retransmit_stamp(sk); -	if (!icsk->icsk_user_timeout || !start_ts) +	start_ts = tcp_sk(sk)->retrans_stamp; +	if (!icsk->icsk_user_timeout)  		return icsk->icsk_rto;  	elapsed = tcp_time_stamp(tcp_sk(sk)) - start_ts;  	remaining = icsk->icsk_user_timeout - elapsed; @@ -197,10 +183,7 @@ static bool retransmits_timed_out(struct sock *sk,  	if (!inet_csk(sk)->icsk_retransmits)  		return false; -	start_ts = tcp_retransmit_stamp(sk); -	if (!start_ts) -		return false; - +	start_ts = tcp_sk(sk)->retrans_stamp;  	if (likely(timeout == 0)) {  		linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);  | 
