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:
parent
a87ba6e923
commit
25e6f9ed4b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=145087
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user