]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
tcp: fix to allow timestamp undo if no retransmits were sent
authorNeal Cardwell <ncardwell@google.com>
Tue, 1 Oct 2024 20:05:15 +0000 (20:05 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 3 Oct 2024 23:18:03 +0000 (16:18 -0700)
Fix the TCP loss recovery undo logic in tcp_packet_delayed() so that
it can trigger undo even if TSQ prevents a fast recovery episode from
reaching tcp_retransmit_skb().

Geumhwan Yu <geumhwan.yu@samsung.com> recently reported that after
this commit from 2019:

commit bc9f38c8328e ("tcp: avoid unconditional congestion window undo
on SYN retransmit")

...and before this fix we could have buggy scenarios like the
following:

+ Due to reordering, a TCP connection receives some SACKs and enters a
  spurious fast recovery.

+ TSQ prevents all invocations of tcp_retransmit_skb(), because many
  skbs are queued in lower layers of the sending machine's network
  stack; thus tp->retrans_stamp remains 0.

+ The connection receives a TCP timestamp ECR value echoing a
  timestamp before the fast recovery, indicating that the fast
  recovery was spurious.

+ The connection fails to undo the spurious fast recovery because
  tp->retrans_stamp is 0, and thus tcp_packet_delayed() returns false,
  due to the new logic in the 2019 commit: commit bc9f38c8328e ("tcp:
  avoid unconditional congestion window undo on SYN retransmit")

This fix tweaks the logic to be more similar to the
tcp_packet_delayed() logic before bc9f38c8328e, except that we take
care not to be fooled by the FLAG_SYN_ACKED code path zeroing out
tp->retrans_stamp (the bug noted and fixed by Yuchung in
bc9f38c8328e).

Note that this returns the high-level behavior of tcp_packet_delayed()
to again match the comment for the function, which says: "Nothing was
retransmitted or returned timestamp is less than timestamp of the
first retransmission." Note that this comment is in the original
2005-04-16 Linux git commit, so this is evidently long-standing
behavior.

Fixes: bc9f38c8328e ("tcp: avoid unconditional congestion window undo on SYN retransmit")
Reported-by: Geumhwan Yu <geumhwan.yu@samsung.com>
Diagnosed-by: Geumhwan Yu <geumhwan.yu@samsung.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20241001200517.2756803-2-ncardwell.sw@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp_input.c

index cc05ec1faac89e552b72b30d53a033145c0c8a89..233b77890795cfea5f6e42429b8c9ebffe6f592a 100644 (file)
@@ -2473,8 +2473,22 @@ static bool tcp_skb_spurious_retrans(const struct tcp_sock *tp,
  */
 static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
 {
-       return tp->retrans_stamp &&
-              tcp_tsopt_ecr_before(tp, tp->retrans_stamp);
+       const struct sock *sk = (const struct sock *)tp;
+
+       if (tp->retrans_stamp &&
+           tcp_tsopt_ecr_before(tp, tp->retrans_stamp))
+               return true;  /* got echoed TS before first retransmission */
+
+       /* Check if nothing was retransmitted (retrans_stamp==0), which may
+        * happen in fast recovery due to TSQ. But we ignore zero retrans_stamp
+        * in TCP_SYN_SENT, since when we set FLAG_SYN_ACKED we also clear
+        * retrans_stamp even if we had retransmitted the SYN.
+        */
+       if (!tp->retrans_stamp &&          /* no record of a retransmit/SYN? */
+           sk->sk_state != TCP_SYN_SENT)  /* not the FLAG_SYN_ACKED case? */
+               return true;  /* nothing was retransmitted */
+
+       return false;
 }
 
 /* Undo procedures. */