summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/tcp_input.c22
-rw-r--r--sys/netinet/tcp_reass.c22
-rw-r--r--sys/netinet/tcp_sack.c34
-rw-r--r--sys/netinet/tcp_var.h1
4 files changed, 76 insertions, 3 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index d89bc2b..93e45e1 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1840,7 +1840,27 @@ trimthenstep6:
else if (++tp->t_dupacks > tcprexmtthresh ||
((tcp_do_newreno || tp->sack_enable) &&
IN_FASTRECOVERY(tp))) {
- tp->snd_cwnd += tp->t_maxseg;
+
+ if (tp->sack_enable && IN_FASTRECOVERY(tp)) {
+ int data_in_pipe;
+ int sacked, lost_not_rexmitted;
+
+ /*
+ * Compute the amount of data in flight first.
+ * We can inject new data into the pipe iff
+ * we have less than 1/2 the original window's
+ * worth of data in flight.
+ */
+ sacked = tcp_sacked_bytes(tp, &lost_not_rexmitted);
+ data_in_pipe = (tp->snd_nxt - tp->snd_una) -
+ (sacked + lost_not_rexmitted);
+ if (data_in_pipe < tp->snd_ssthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ if (tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ }
+ } else
+ tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
} else if (tp->t_dupacks == tcprexmtthresh) {
diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c
index d89bc2b..93e45e1 100644
--- a/sys/netinet/tcp_reass.c
+++ b/sys/netinet/tcp_reass.c
@@ -1840,7 +1840,27 @@ trimthenstep6:
else if (++tp->t_dupacks > tcprexmtthresh ||
((tcp_do_newreno || tp->sack_enable) &&
IN_FASTRECOVERY(tp))) {
- tp->snd_cwnd += tp->t_maxseg;
+
+ if (tp->sack_enable && IN_FASTRECOVERY(tp)) {
+ int data_in_pipe;
+ int sacked, lost_not_rexmitted;
+
+ /*
+ * Compute the amount of data in flight first.
+ * We can inject new data into the pipe iff
+ * we have less than 1/2 the original window's
+ * worth of data in flight.
+ */
+ sacked = tcp_sacked_bytes(tp, &lost_not_rexmitted);
+ data_in_pipe = (tp->snd_nxt - tp->snd_una) -
+ (sacked + lost_not_rexmitted);
+ if (data_in_pipe < tp->snd_ssthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ if (tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ }
+ } else
+ tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
} else if (tp->t_dupacks == tcprexmtthresh) {
diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c
index efe3bb6..a8e226c 100644
--- a/sys/netinet/tcp_sack.c
+++ b/sys/netinet/tcp_sack.c
@@ -536,10 +536,10 @@ tcp_sack_partialack(tp, th)
struct tcpcb *tp;
struct tcphdr *th;
{
- INP_LOCK_ASSERT(tp->t_inpcb);
int num_segs = 1;
int sack_bytes_rxmt = 0;
+ INP_LOCK_ASSERT(tp->t_inpcb);
callout_stop(tp->tt_rexmt);
tp->t_rtttime = 0;
/* send one or 2 segments based on how much new data was acked */
@@ -548,6 +548,8 @@ tcp_sack_partialack(tp, th)
(void)tcp_sack_output(tp, &sack_bytes_rxmt);
tp->snd_cwnd = sack_bytes_rxmt + (tp->snd_nxt - tp->sack_newdata) +
num_segs * tp->t_maxseg;
+ if (tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
}
@@ -632,3 +634,33 @@ tcp_sack_adjust(struct tcpcb *tp)
tp->snd_nxt = tp->rcv_lastsack;
return;
}
+
+/*
+ * Calculate the number of SACKed bytes in the scoreboard by
+ * subtracting the amount of data accounted for in sackholes
+ * from the total span of the scoreboard. Also returns the
+ * amount of data that is "lost" and has not yet been retransmitted.
+ */
+int
+tcp_sacked_bytes(struct tcpcb *tp, int *lost_not_rexmitted)
+{
+ INP_LOCK_ASSERT(tp->t_inpcb);
+ struct sackhole *cur = tp->snd_holes;
+ int sacked = 0;
+ u_long lost = 0;
+
+ if (cur == NULL) /* Scoreboard empty. */
+ goto out;
+ if (SEQ_GEQ(tp->snd_una, tp->rcv_lastsack)) /* Scoreboard is stale. */
+ goto out;
+ sacked = tp->rcv_lastsack - cur->start;
+ while (cur) {
+ lost += (cur->end - cur->rxmit);
+ sacked -= (cur->end - cur->start);
+ cur = cur->next;
+ }
+out:
+ if (lost_not_rexmitted)
+ *lost_not_rexmitted = lost;
+ return (sacked);
+}
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 252d3b0..26e410e 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -579,6 +579,7 @@ void tcp_del_sackholes(struct tcpcb *, struct tcphdr *);
void tcp_clean_sackreport(struct tcpcb *tp);
void tcp_sack_adjust(struct tcpcb *tp);
struct sackhole *tcp_sack_output(struct tcpcb *tp, int *sack_bytes_rexmt);
+int tcp_sacked_bytes(struct tcpcb *tp, int *lost_not_rexmitted);
void tcp_sack_partialack(struct tcpcb *, struct tcphdr *);
void tcp_free_sackholes(struct tcpcb *tp);
int tcp_newreno(struct tcpcb *, struct tcphdr *);
OpenPOWER on IntegriCloud