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>
This commit is contained in:
Paul Saab 2005-04-14 20:09:52 +00:00
parent a87ba6e923
commit 25e6f9ed4b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=145087
4 changed files with 76 additions and 3 deletions

View File

@ -1840,7 +1840,27 @@ tcp_input(m, off0)
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) {

View File

@ -1840,7 +1840,27 @@ tcp_input(m, off0)
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) {

View File

@ -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);
}

View File

@ -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 *);