Patch "tcp: fix tcp_enter_recovery() to zero retrans_stamp when it's safe" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    tcp: fix tcp_enter_recovery() to zero retrans_stamp when it's safe

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     tcp-fix-tcp_enter_recovery-to-zero-retrans_stamp-whe.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit b99c230af04bb507ce4dce1c33394aaf1956197f
Author: Neal Cardwell <ncardwell@xxxxxxxxxx>
Date:   Tue Oct 1 20:05:16 2024 +0000

    tcp: fix tcp_enter_recovery() to zero retrans_stamp when it's safe
    
    [ Upstream commit b41b4cbd9655bcebcce941bef3601db8110335be ]
    
    Fix tcp_enter_recovery() so that if there are no retransmits out then
    we zero retrans_stamp when entering fast recovery. This is necessary
    to fix two buggy behaviors.
    
    Currently a non-zero retrans_stamp value can persist across multiple
    back-to-back loss recovery episodes. This is because we generally only
    clears retrans_stamp if we are completely done with loss recoveries,
    and get to tcp_try_to_open() and find !tcp_any_retrans_done(sk). This
    behavior causes two bugs:
    
    (1) When a loss recovery episode (CA_Loss or CA_Recovery) is followed
    immediately by a new CA_Recovery, the retrans_stamp value can persist
    and can be a time before this new CA_Recovery episode starts. That
    means that timestamp-based undo will be using the wrong retrans_stamp
    (a value that is too old) when comparing incoming TS ecr values to
    retrans_stamp to see if the current fast recovery episode can be
    undone.
    
    (2) If there is a roughly minutes-long sequence of back-to-back fast
    recovery episodes, one after another (e.g. in a shallow-buffered or
    policed bottleneck), where each fast recovery successfully makes
    forward progress and recovers one window of sequence space (but leaves
    at least one retransmit in flight at the end of the recovery),
    followed by several RTOs, then the ETIMEDOUT check may be using the
    wrong retrans_stamp (a value set at the start of the first fast
    recovery in the sequence). This can cause a very premature ETIMEDOUT,
    killing the connection prematurely.
    
    This commit changes the code to zero retrans_stamp when entering fast
    recovery, when this is known to be safe (no retransmits are out in the
    network). That ensures that when starting a fast recovery episode, and
    it is safe to do so, retrans_stamp is set when we send the fast
    retransmit packet. That addresses both bug (1) and bug (2) by ensuring
    that (if no retransmits are out when we start a fast recovery) we use
    the initial fast retransmit of this fast recovery as the time value
    for undo and ETIMEDOUT calculations.
    
    This makes intuitive sense, since the start of a new fast recovery
    episode (in a scenario where no lost packets are out in the network)
    means that the connection has made forward progress since the last RTO
    or fast recovery, and we should thus "restart the clock" used for both
    undo and ETIMEDOUT logic.
    
    Note that if when we start fast recovery there *are* retransmits out
    in the network, there can still be undesirable (1)/(2) issues. For
    example, after this patch we can still have the (1) and (2) problems
    in cases like this:
    
    + round 1: sender sends flight 1
    
    + round 2: sender receives SACKs and enters fast recovery 1,
      retransmits some packets in flight 1 and then sends some new data as
      flight 2
    
    + round 3: sender receives some SACKs for flight 2, notes losses, and
      retransmits some packets to fill the holes in flight 2
    
    + fast recovery has some lost retransmits in flight 1 and continues
      for one or more rounds sending retransmits for flight 1 and flight 2
    
    + fast recovery 1 completes when snd_una reaches high_seq at end of
      flight 1
    
    + there are still holes in the SACK scoreboard in flight 2, so we
      enter fast recovery 2, but some retransmits in the flight 2 sequence
      range are still in flight (retrans_out > 0), so we can't execute the
      new retrans_stamp=0 added here to clear retrans_stamp
    
    It's not yet clear how to fix these remaining (1)/(2) issues in an
    efficient way without breaking undo behavior, given that retrans_stamp
    is currently used for undo and ETIMEDOUT. Perhaps the optimal (but
    expensive) strategy would be to set retrans_stamp to the timestamp of
    the earliest outstanding retransmit when entering fast recovery. But
    at least this commit makes things better.
    
    Note that this does not change the semantics of retrans_stamp; it
    simply makes retrans_stamp accurate in some cases where it was not
    before:
    
    (1) Some loss recovery, followed by an immediate entry into a fast
    recovery, where there are no retransmits out when entering the fast
    recovery.
    
    (2) When a TFO server has a SYNACK retransmit that sets retrans_stamp,
    and then the ACK that completes the 3-way handshake has SACK blocks
    that trigger a fast recovery. In this case when entering fast recovery
    we want to zero out the retrans_stamp from the TFO SYNACK retransmit,
    and set the retrans_stamp based on the timestamp of the fast recovery.
    
    We introduce a tcp_retrans_stamp_cleanup() helper, because this
    two-line sequence already appears in 3 places and is about to appear
    in 2 more as a result of this bug fix patch series. Once this bug fix
    patches series in the net branch makes it into the net-next branch
    we'll update the 3 other call sites to use the new helper.
    
    This is a long-standing issue. The Fixes tag below is chosen to be the
    oldest commit at which the patch will apply cleanly, which is from
    Linux v3.5 in 2012.
    
    Fixes: 1fbc340514fc ("tcp: early retransmit: tcp_enter_recovery()")
    Signed-off-by: Neal Cardwell <ncardwell@xxxxxxxxxx>
    Signed-off-by: Yuchung Cheng <ycheng@xxxxxxxxxx>
    Signed-off-by: Eric Dumazet <edumazet@xxxxxxxxxx>
    Link: https://patch.msgid.link/20241001200517.2756803-3-ncardwell.sw@xxxxxxxxx
    Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 699c48745cdd9..3e7533f645121 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2482,6 +2482,16 @@ static bool tcp_any_retrans_done(const struct sock *sk)
 	return false;
 }
 
+/* If loss recovery is finished and there are no retransmits out in the
+ * network, then we clear retrans_stamp so that upon the next loss recovery
+ * retransmits_timed_out() and timestamp-undo are using the correct value.
+ */
+static void tcp_retrans_stamp_cleanup(struct sock *sk)
+{
+	if (!tcp_any_retrans_done(sk))
+		tcp_sk(sk)->retrans_stamp = 0;
+}
+
 static void DBGUNDO(struct sock *sk, const char *msg)
 {
 #if FASTRETRANS_DEBUG > 1
@@ -2849,6 +2859,9 @@ void tcp_enter_recovery(struct sock *sk, bool ece_ack)
 	struct tcp_sock *tp = tcp_sk(sk);
 	int mib_idx;
 
+	/* Start the clock with our fast retransmit, for undo and ETIMEDOUT. */
+	tcp_retrans_stamp_cleanup(sk);
+
 	if (tcp_is_reno(tp))
 		mib_idx = LINUX_MIB_TCPRENORECOVERY;
 	else




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux