aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuchung Cheng <ycheng@google.com>2014-02-28 19:42:26 -0500
committerDavid S. Miller <davem@davemloft.net>2014-03-03 15:33:02 -0500
commitc84a57113f59486e6688be1cd443b96e3118efa0 (patch)
tree5177aed13e8ff6c94ceed13239d824a92e672f71
parentde39d7a4f3693c4247135cbce42716bf2a113577 (diff)
tcp: fix bogus RTT on special retransmission
RTT may be bogus with tall loss probe (TLP) when a packet is retransmitted and latter (s)acked without TCPCB_SACKED_RETRANS flag. For example, TLP calls __tcp_retransmit_skb() instead of tcp_retransmit_skb(). The skb timestamps are updated but the sacked flag is not marked with TCPCB_SACKED_RETRANS. As a result we'll get bogus RTT in tcp_clean_rtx_queue() or in tcp_sacktag_one() on spurious retransmission. The fix is to apply the sticky flag TCP_EVER_RETRANS to enforce Karn's check on RTT sampling. However this will disable F-RTO if timeout occurs after TLP, by resetting undo_marker in tcp_enter_loss(). We relax this check to only if any pending retransmists are still in-flight. Signed-off-by: Yuchung Cheng <ycheng@google.com> Acked-by: Eric Dumazet <edumazet@google.com> Acked-by: Neal Cardwell <ncardwell@google.com> Acked-by: Nandita Dukkipati <nanditad@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/tcp_input.c3
-rw-r--r--net/ipv4/tcp_output.c11
2 files changed, 10 insertions, 4 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 227cba79fa6b..eeaac399420d 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1945,8 +1945,9 @@ void tcp_enter_loss(struct sock *sk, int how)
1945 if (skb == tcp_send_head(sk)) 1945 if (skb == tcp_send_head(sk))
1946 break; 1946 break;
1947 1947
1948 if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS) 1948 if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)
1949 tp->undo_marker = 0; 1949 tp->undo_marker = 0;
1950
1950 TCP_SKB_CB(skb)->sacked &= (~TCPCB_TAGBITS)|TCPCB_SACKED_ACKED; 1951 TCP_SKB_CB(skb)->sacked &= (~TCPCB_TAGBITS)|TCPCB_SACKED_ACKED;
1951 if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED) || how) { 1952 if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED) || how) {
1952 TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED; 1953 TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index d718482fd11c..f0eb4e337ec8 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2337,6 +2337,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
2337 struct tcp_sock *tp = tcp_sk(sk); 2337 struct tcp_sock *tp = tcp_sk(sk);
2338 struct inet_connection_sock *icsk = inet_csk(sk); 2338 struct inet_connection_sock *icsk = inet_csk(sk);
2339 unsigned int cur_mss; 2339 unsigned int cur_mss;
2340 int err;
2340 2341
2341 /* Inconslusive MTU probe */ 2342 /* Inconslusive MTU probe */
2342 if (icsk->icsk_mtup.probe_size) { 2343 if (icsk->icsk_mtup.probe_size) {
@@ -2400,11 +2401,15 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
2400 skb_headroom(skb) >= 0xFFFF)) { 2401 skb_headroom(skb) >= 0xFFFF)) {
2401 struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER, 2402 struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER,
2402 GFP_ATOMIC); 2403 GFP_ATOMIC);
2403 return nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : 2404 err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
2404 -ENOBUFS; 2405 -ENOBUFS;
2405 } else { 2406 } else {
2406 return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); 2407 err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
2407 } 2408 }
2409
2410 if (likely(!err))
2411 TCP_SKB_CB(skb)->sacked |= TCPCB_EVER_RETRANS;
2412 return err;
2408} 2413}
2409 2414
2410int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) 2415int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)