tcp: do not purge SACK scoreboard on first RTO

Keeping the SACK scoreboard intact after the first RTO
and retransmitting all data anew only on subsequent RTOs
allows a more timely and efficient loss recovery under
many adverse cirumstances.

Reviewed By:           tuexen, #transport
MFC after:             10 weeks
Sponsored by:          NetApp, Inc.
Differential Revision: https://reviews.freebsd.org/D42906
This commit is contained in:
Richard Scheffenegger 2024-01-06 20:25:17 +01:00
parent 893ed42eca
commit 30409ecdb6
4 changed files with 28 additions and 3 deletions

View file

@ -439,7 +439,10 @@ cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type)
case CC_RTO:
tp->t_dupacks = 0;
tp->t_bytes_acked = 0;
EXIT_RECOVERY(tp->t_flags);
if ((tp->t_rxtshift > 1) ||
!((tp->t_flags & TF_SACK_PERMIT) &&
(!TAILQ_EMPTY(&tp->snd_holes))))
EXIT_RECOVERY(tp->t_flags);
if (tp->t_flags2 & TF2_ECN_PERMIT)
tp->t_flags2 |= TF2_ECN_SND_CWR;
break;

View file

@ -908,6 +908,25 @@ tcp_free_sackholes(struct tcpcb *tp)
("tp->sackhint.nexthole == NULL"));
}
/*
* Resend all the currently existing SACK holes of
* the scoreboard. This is in line with the Errata to
* RFC 2018, which allows the use of SACK data past
* an RTO to good effect typically.
*/
void
tcp_resend_sackholes(struct tcpcb *tp)
{
struct sackhole *p;
INP_WLOCK_ASSERT(tptoinpcb(tp));
TAILQ_FOREACH(p, &tp->snd_holes, scblink) {
p->rxmit = p->start;
}
tp->sackhint.nexthole = TAILQ_FIRST(&tp->snd_holes);
tp->sackhint.sack_bytes_rexmit = 0;
}
/*
* Partial ack handling within a sack recovery episode. Keeping this very
* simple for now. When a partial ack is received, force snd_cwnd to a value

View file

@ -559,7 +559,6 @@ tcp_timer_rexmt(struct tcpcb *tp)
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO);
CURVNET_SET(inp->inp_vnet);
tcp_free_sackholes(tp);
if (tp->t_fb->tfb_tcp_rexmit_tmr) {
/* The stack has a timer action too. */
(*tp->t_fb->tfb_tcp_rexmit_tmr)(tp);
@ -619,8 +618,11 @@ tcp_timer_rexmt(struct tcpcb *tp)
* the retransmitted packet's to_tsval to by tcp_output
*/
tp->t_flags |= TF_PREVVALID;
} else
tcp_resend_sackholes(tp);
} else {
tp->t_flags &= ~TF_PREVVALID;
tcp_free_sackholes(tp);
}
TCPSTAT_INC(tcps_rexmttimeo);
if ((tp->t_state == TCPS_SYN_SENT) ||
(tp->t_state == TCPS_SYN_RECEIVED))

View file

@ -1499,6 +1499,7 @@ struct sackhole *tcp_sack_output(struct tcpcb *tp, int *sack_bytes_rexmt);
void tcp_do_prr_ack(struct tcpcb *, struct tcphdr *, struct tcpopt *, sackstatus_t);
void tcp_lost_retransmission(struct tcpcb *, struct tcphdr *);
void tcp_sack_partialack(struct tcpcb *, struct tcphdr *);
void tcp_resend_sackholes(struct tcpcb *tp);
void tcp_free_sackholes(struct tcpcb *tp);
void tcp_sack_lost_retransmission(struct tcpcb *, struct tcphdr *);
int tcp_newreno(struct tcpcb *, struct tcphdr *);