aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/tcp_input.c5
-rw-r--r--sys/netinet/tcp_sack.c19
-rw-r--r--sys/netinet/tcp_timer.c6
-rw-r--r--sys/netinet/tcp_var.h1
4 files changed, 28 insertions, 3 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 1d6430f6ef20..4a6100fc969b 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -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;
diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c
index e8ed3c52fd67..48efe855f689 100644
--- a/sys/netinet/tcp_sack.c
+++ b/sys/netinet/tcp_sack.c
@@ -909,6 +909,25 @@ tcp_free_sackholes(struct tcpcb *tp)
}
/*
+ * 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
* that will allow the sender to transmit no more than 2 segments. If
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c
index 952fa53e0275..125e28134c01 100644
--- a/sys/netinet/tcp_timer.c
+++ b/sys/netinet/tcp_timer.c
@@ -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))
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index c2b15526c15b..5f064ead7f64 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -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 *);