* [PATCH net] tcp: fix false undo corner cases
@ 2014-07-02 19:07 Yuchung Cheng
2014-07-08 4:41 ` David Miller
0 siblings, 1 reply; 3+ messages in thread
From: Yuchung Cheng @ 2014-07-02 19:07 UTC (permalink / raw)
To: davem; +Cc: ncardwell, netdev, Yuchung Cheng
The undo code assumes that, upon entering loss recovery, TCP
1) always retransmit something
2) the retransmission never fails locally (e.g., qdisc drop)
so undo_marker is set in tcp_enter_recovery() and undo_retrans is
incremented only when tcp_retransmit_skb() is successful.
When the assumption is broken because TCP's cwnd is too small to
retransmit or the retransmit fails locally. The next (DUP)ACK
would incorrectly revert the cwnd and the congestion state in
tcp_try_undo_dsack() or tcp_may_undo(). Subsequent (DUP)ACKs
may enter the recovery state. The sender repeatedly enter and
(incorrectly) exit recovery states if the retransmits continue to
fail locally while receiving (DUP)ACKs.
The fix is to initialize undo_retrans to -1 and start counting on
the first retransmission. Always increment undo_retrans even if the
retransmissions fail locally because they couldn't cause DSACKs to
undo the cwnd reduction.
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
---
net/ipv4/tcp_input.c | 8 ++++----
net/ipv4/tcp_output.c | 6 ++++--
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b5c2375..40639c2 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1106,7 +1106,7 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb,
}
/* D-SACK for already forgotten data... Do dumb counting. */
- if (dup_sack && tp->undo_marker && tp->undo_retrans &&
+ if (dup_sack && tp->undo_marker && tp->undo_retrans > 0 &&
!after(end_seq_0, prior_snd_una) &&
after(end_seq_0, tp->undo_marker))
tp->undo_retrans--;
@@ -1187,7 +1187,7 @@ static u8 tcp_sacktag_one(struct sock *sk,
/* Account D-SACK for retransmitted packet. */
if (dup_sack && (sacked & TCPCB_RETRANS)) {
- if (tp->undo_marker && tp->undo_retrans &&
+ if (tp->undo_marker && tp->undo_retrans > 0 &&
after(end_seq, tp->undo_marker))
tp->undo_retrans--;
if (sacked & TCPCB_SACKED_ACKED)
@@ -1893,7 +1893,7 @@ static void tcp_clear_retrans_partial(struct tcp_sock *tp)
tp->lost_out = 0;
tp->undo_marker = 0;
- tp->undo_retrans = 0;
+ tp->undo_retrans = -1;
}
void tcp_clear_retrans(struct tcp_sock *tp)
@@ -2665,7 +2665,7 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack)
tp->prior_ssthresh = 0;
tp->undo_marker = tp->snd_una;
- tp->undo_retrans = tp->retrans_out;
+ tp->undo_retrans = tp->retrans_out ? : -1;
if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) {
if (!ece_ack)
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index d92bce0..179b51e 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2525,8 +2525,6 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
if (!tp->retrans_stamp)
tp->retrans_stamp = TCP_SKB_CB(skb)->when;
- tp->undo_retrans += tcp_skb_pcount(skb);
-
/* snd_nxt is stored to detect loss of retransmitted segment,
* see tcp_input.c tcp_sacktag_write_queue().
*/
@@ -2534,6 +2532,10 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
} else if (err != -EBUSY) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
}
+
+ if (tp->undo_retrans < 0)
+ tp->undo_retrans = 0;
+ tp->undo_retrans += tcp_skb_pcount(skb);
return err;
}
--
2.0.0.526.g5318336
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH net] tcp: fix false undo corner cases
2014-07-02 19:07 [PATCH net] tcp: fix false undo corner cases Yuchung Cheng
@ 2014-07-08 4:41 ` David Miller
2014-07-08 17:33 ` Yuchung Cheng
0 siblings, 1 reply; 3+ messages in thread
From: David Miller @ 2014-07-08 4:41 UTC (permalink / raw)
To: ycheng; +Cc: ncardwell, netdev
From: Yuchung Cheng <ycheng@google.com>
Date: Wed, 2 Jul 2014 12:07:16 -0700
> The undo code assumes that, upon entering loss recovery, TCP
> 1) always retransmit something
> 2) the retransmission never fails locally (e.g., qdisc drop)
>
> so undo_marker is set in tcp_enter_recovery() and undo_retrans is
> incremented only when tcp_retransmit_skb() is successful.
>
> When the assumption is broken because TCP's cwnd is too small to
> retransmit or the retransmit fails locally. The next (DUP)ACK
> would incorrectly revert the cwnd and the congestion state in
> tcp_try_undo_dsack() or tcp_may_undo(). Subsequent (DUP)ACKs
> may enter the recovery state. The sender repeatedly enter and
> (incorrectly) exit recovery states if the retransmits continue to
> fail locally while receiving (DUP)ACKs.
>
> The fix is to initialize undo_retrans to -1 and start counting on
> the first retransmission. Always increment undo_retrans even if the
> retransmissions fail locally because they couldn't cause DSACKs to
> undo the cwnd reduction.
>
> Signed-off-by: Yuchung Cheng <ycheng@google.com>
> Signed-off-by: Neal Cardwell <ncardwell@google.com>
Applied, thanks.
Do you guys think this is -stable material?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] tcp: fix false undo corner cases
2014-07-08 4:41 ` David Miller
@ 2014-07-08 17:33 ` Yuchung Cheng
0 siblings, 0 replies; 3+ messages in thread
From: Yuchung Cheng @ 2014-07-08 17:33 UTC (permalink / raw)
To: David Miller; +Cc: Neal Cardwell, netdev
On Mon, Jul 7, 2014 at 9:41 PM, David Miller <davem@davemloft.net> wrote:
> From: Yuchung Cheng <ycheng@google.com>
> Date: Wed, 2 Jul 2014 12:07:16 -0700
>
>> The undo code assumes that, upon entering loss recovery, TCP
>> 1) always retransmit something
>> 2) the retransmission never fails locally (e.g., qdisc drop)
>>
>> so undo_marker is set in tcp_enter_recovery() and undo_retrans is
>> incremented only when tcp_retransmit_skb() is successful.
>>
>> When the assumption is broken because TCP's cwnd is too small to
>> retransmit or the retransmit fails locally. The next (DUP)ACK
>> would incorrectly revert the cwnd and the congestion state in
>> tcp_try_undo_dsack() or tcp_may_undo(). Subsequent (DUP)ACKs
>> may enter the recovery state. The sender repeatedly enter and
>> (incorrectly) exit recovery states if the retransmits continue to
>> fail locally while receiving (DUP)ACKs.
>>
>> The fix is to initialize undo_retrans to -1 and start counting on
>> the first retransmission. Always increment undo_retrans even if the
>> retransmissions fail locally because they couldn't cause DSACKs to
>> undo the cwnd reduction.
>>
>> Signed-off-by: Yuchung Cheng <ycheng@google.com>
>> Signed-off-by: Neal Cardwell <ncardwell@google.com>
>
> Applied, thanks.
>
> Do you guys think this is -stable material?
Yes this bugfix fits -stable. Thanks.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2014-07-08 17:34 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-02 19:07 [PATCH net] tcp: fix false undo corner cases Yuchung Cheng
2014-07-08 4:41 ` David Miller
2014-07-08 17:33 ` Yuchung Cheng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).