diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -439,7 +439,10 @@ 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; diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c --- a/sys/netinet/tcp_sack.c +++ b/sys/netinet/tcp_sack.c @@ -908,6 +908,25 @@ ("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 diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -559,7 +559,6 @@ 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 @@ * 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)) diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1499,6 +1499,7 @@ 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 *);