lkml.org 
[lkml]   [2014]   [Jul]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.15 056/109] tcp: fix false undo corner cases
    Date
    3.15-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Yuchung Cheng <ycheng@google.com>

    [ Upstream commit 6e08d5e3c8236e7484229e46fdf92006e1dd4c49 ]

    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>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/ipv4/tcp_input.c | 8 ++++----
    net/ipv4/tcp_output.c | 6 ++++--
    2 files changed, 8 insertions(+), 6 deletions(-)

    --- a/net/ipv4/tcp_input.c
    +++ b/net/ipv4/tcp_input.c
    @@ -1106,7 +1106,7 @@ static bool tcp_check_dsack(struct sock
    }

    /* 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 *s

    /* 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(st
    tp->lost_out = 0;

    tp->undo_marker = 0;
    - tp->undo_retrans = 0;
    + tp->undo_retrans = -1;
    }

    void tcp_clear_retrans(struct tcp_sock *tp)
    @@ -2664,7 +2664,7 @@ static void tcp_enter_recovery(struct so

    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)
    --- a/net/ipv4/tcp_output.c
    +++ b/net/ipv4/tcp_output.c
    @@ -2472,8 +2472,6 @@ int tcp_retransmit_skb(struct sock *sk,
    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().
    */
    @@ -2481,6 +2479,10 @@ int tcp_retransmit_skb(struct sock *sk,
    } else {
    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;
    }




    \
     
     \ /
      Last update: 2014-07-26 22:01    [W:4.254 / U:1.400 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site