diff options
author | ps <ps@FreeBSD.org> | 2005-04-14 20:09:52 +0000 |
---|---|---|
committer | ps <ps@FreeBSD.org> | 2005-04-14 20:09:52 +0000 |
commit | 2bf55008502e9691ab3ace062d854f1b10ba3667 (patch) | |
tree | 192aa6db528a920e2c180a45540eef55e2e4379f /sys | |
parent | 73d2098d76ba5faed40c4fbc11258fb04f02aa56 (diff) | |
download | FreeBSD-src-2bf55008502e9691ab3ace062d854f1b10ba3667.zip FreeBSD-src-2bf55008502e9691ab3ace062d854f1b10ba3667.tar.gz |
Fix for a TCP SACK bug where more than (win/2) bytes could have been
in flight in SACK recovery.
Found by: Noritoshi Demizu
Submitted by: Mohan Srinivasan <mohans at yahoo-inc dot com>
Noritoshi Demizu <demizu at dd dot ij4u dot or dot jp>
Raja Mukerji <raja at moselle dot com>
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet/tcp_input.c | 22 | ||||
-rw-r--r-- | sys/netinet/tcp_reass.c | 22 | ||||
-rw-r--r-- | sys/netinet/tcp_sack.c | 34 | ||||
-rw-r--r-- | sys/netinet/tcp_var.h | 1 |
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 *); |