Skip to content

Commit 9af25dd

Browse files
committed
Merge branch 'tcp-3-fixes-for-retrans_stamp-and-undo-logic'
Neal Cardwell says: ==================== tcp: 3 fixes for retrans_stamp and undo logic Geumhwan Yu <[email protected]> recently reported and diagnosed a regression in TCP loss recovery undo logic in the case where a TCP connection enters fast recovery, is unable to retransmit anything due to TSQ, and then receives an ACK allowing forward progress. The sender should be able to undo the spurious loss recovery in this case, but was not doing so. The first patch fixes this regression. Running our suite of packetdrill tests with the first fix, the tests highlighted two other small bugs in the way retrans_stamp is updated in some rare corner cases. The second two patches fix those other two small bugs. Thanks to Geumhwan Yu for the bug report! ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents ec63670 + 27c80ef commit 9af25dd

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

net/ipv4/tcp_input.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,8 +2473,22 @@ static bool tcp_skb_spurious_retrans(const struct tcp_sock *tp,
24732473
*/
24742474
static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
24752475
{
2476-
return tp->retrans_stamp &&
2477-
tcp_tsopt_ecr_before(tp, tp->retrans_stamp);
2476+
const struct sock *sk = (const struct sock *)tp;
2477+
2478+
if (tp->retrans_stamp &&
2479+
tcp_tsopt_ecr_before(tp, tp->retrans_stamp))
2480+
return true; /* got echoed TS before first retransmission */
2481+
2482+
/* Check if nothing was retransmitted (retrans_stamp==0), which may
2483+
* happen in fast recovery due to TSQ. But we ignore zero retrans_stamp
2484+
* in TCP_SYN_SENT, since when we set FLAG_SYN_ACKED we also clear
2485+
* retrans_stamp even if we had retransmitted the SYN.
2486+
*/
2487+
if (!tp->retrans_stamp && /* no record of a retransmit/SYN? */
2488+
sk->sk_state != TCP_SYN_SENT) /* not the FLAG_SYN_ACKED case? */
2489+
return true; /* nothing was retransmitted */
2490+
2491+
return false;
24782492
}
24792493

24802494
/* Undo procedures. */
@@ -2508,6 +2522,16 @@ static bool tcp_any_retrans_done(const struct sock *sk)
25082522
return false;
25092523
}
25102524

2525+
/* If loss recovery is finished and there are no retransmits out in the
2526+
* network, then we clear retrans_stamp so that upon the next loss recovery
2527+
* retransmits_timed_out() and timestamp-undo are using the correct value.
2528+
*/
2529+
static void tcp_retrans_stamp_cleanup(struct sock *sk)
2530+
{
2531+
if (!tcp_any_retrans_done(sk))
2532+
tcp_sk(sk)->retrans_stamp = 0;
2533+
}
2534+
25112535
static void DBGUNDO(struct sock *sk, const char *msg)
25122536
{
25132537
#if FASTRETRANS_DEBUG > 1
@@ -2875,6 +2899,9 @@ void tcp_enter_recovery(struct sock *sk, bool ece_ack)
28752899
struct tcp_sock *tp = tcp_sk(sk);
28762900
int mib_idx;
28772901

2902+
/* Start the clock with our fast retransmit, for undo and ETIMEDOUT. */
2903+
tcp_retrans_stamp_cleanup(sk);
2904+
28782905
if (tcp_is_reno(tp))
28792906
mib_idx = LINUX_MIB_TCPRENORECOVERY;
28802907
else
@@ -6657,10 +6684,17 @@ static void tcp_rcv_synrecv_state_fastopen(struct sock *sk)
66576684
if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss && !tp->packets_out)
66586685
tcp_try_undo_recovery(sk);
66596686

6660-
/* Reset rtx states to prevent spurious retransmits_timed_out() */
66616687
tcp_update_rto_time(tp);
6662-
tp->retrans_stamp = 0;
66636688
inet_csk(sk)->icsk_retransmits = 0;
6689+
/* In tcp_fastopen_synack_timer() on the first SYNACK RTO we set
6690+
* retrans_stamp but don't enter CA_Loss, so in case that happened we
6691+
* need to zero retrans_stamp here to prevent spurious
6692+
* retransmits_timed_out(). However, if the ACK of our SYNACK caused us
6693+
* to enter CA_Recovery then we need to leave retrans_stamp as it was
6694+
* set entering CA_Recovery, for correct retransmits_timed_out() and
6695+
* undo behavior.
6696+
*/
6697+
tcp_retrans_stamp_cleanup(sk);
66646698

66656699
/* Once we leave TCP_SYN_RECV or TCP_FIN_WAIT_1,
66666700
* we no longer need req so release it.

0 commit comments

Comments
 (0)