Fix NewReno.

Reviewed by: Tom Henderson <thomas.r.henderson@boeing.com>
This commit is contained in:
Jeffrey Hsu 2003-01-13 11:01:20 +00:00
parent d8b504e33c
commit cb942153c8
5 changed files with 94 additions and 89 deletions

View File

@ -975,7 +975,10 @@ after_listen:
if (SEQ_GT(th->th_ack, tp->snd_una) && if (SEQ_GT(th->th_ack, tp->snd_una) &&
SEQ_LEQ(th->th_ack, tp->snd_max) && SEQ_LEQ(th->th_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd && tp->snd_cwnd >= tp->snd_wnd &&
tp->t_dupacks < tcprexmtthresh) { ((!tcp_do_newreno &&
tp->t_dupacks < tcprexmtthresh) ||
(tcp_do_newreno &&
!SEQ_LT(tp->snd_una, tp->snd_recover)))) {
KASSERT(headlocked, ("headlocked")); KASSERT(headlocked, ("headlocked"));
INP_INFO_WUNLOCK(&tcbinfo); INP_INFO_WUNLOCK(&tcbinfo);
headlocked = 0; headlocked = 0;
@ -988,9 +991,11 @@ after_listen:
*/ */
if (tp->t_rxtshift == 1 && if (tp->t_rxtshift == 1 &&
ticks < tp->t_badrxtwin) { ticks < tp->t_badrxtwin) {
++tcpstat.tcps_sndrexmitbad;
tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_cwnd = tp->snd_cwnd_prev;
tp->snd_ssthresh = tp->snd_ssthresh =
tp->snd_ssthresh_prev; tp->snd_ssthresh_prev;
tp->snd_high = tp->snd_high_prev;
tp->snd_nxt = tp->snd_max; tp->snd_nxt = tp->snd_max;
tp->t_badrxtwin = 0; tp->t_badrxtwin = 0;
} }
@ -1017,11 +1022,15 @@ after_listen:
tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked; tcpstat.tcps_rcvackbyte += acked;
sbdrop(&so->so_snd, acked); sbdrop(&so->so_snd, acked);
if (SEQ_GT(tp->snd_una, tp->snd_high) &&
SEQ_LEQ(th->th_ack, tp->snd_high))
tp->snd_high = th->th_ack - 1;
tp->snd_una = tp->snd_recover = th->th_ack;
/* /*
* pull snd_wl2 up to prevent seq wrap relative * pull snd_wl2 up to prevent seq wrap relative
* to th_ack. * to th_ack.
*/ */
tp->snd_wl2 = tp->snd_una = th->th_ack; tp->snd_wl2 = th->th_ack;
tp->t_dupacks = 0; tp->t_dupacks = 0;
m_freem(m); m_freem(m);
ND6_HINT(tp); /* some progress has been done */ ND6_HINT(tp); /* some progress has been done */
@ -1714,22 +1723,23 @@ trimthenstep6:
if (!callout_active(tp->tt_rexmt) || if (!callout_active(tp->tt_rexmt) ||
th->th_ack != tp->snd_una) th->th_ack != tp->snd_una)
tp->t_dupacks = 0; tp->t_dupacks = 0;
else if (++tp->t_dupacks == tcprexmtthresh) { else if (++tp->t_dupacks > tcprexmtthresh ||
tcp_seq onxt = tp->snd_nxt; (tcp_do_newreno &&
u_int win = SEQ_LT(tp->snd_una,
min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->snd_recover))) {
tp->t_maxseg;
if (tcp_do_newreno &&
SEQ_LT(th->th_ack,
tp->snd_recover)) {
/* False retransmit, should not
* cut window
*/
tp->snd_cwnd += tp->t_maxseg; tp->snd_cwnd += tp->t_maxseg;
tp->t_dupacks = 0;
(void) tcp_output(tp); (void) tcp_output(tp);
goto drop; goto drop;
} else if (tp->t_dupacks == tcprexmtthresh) {
tcp_seq onxt = tp->snd_nxt;
u_int win;
if (tcp_do_newreno &&
SEQ_LEQ(th->th_ack, tp->snd_high)) {
tp->t_dupacks = 0;
break;
} }
win = min(tp->snd_wnd, tp->snd_cwnd) /
2 / tp->t_maxseg;
if (win < 2) if (win < 2)
win = 2; win = 2;
tp->snd_ssthresh = win * tp->t_maxseg; tp->snd_ssthresh = win * tp->t_maxseg;
@ -1744,23 +1754,21 @@ trimthenstep6:
if (SEQ_GT(onxt, tp->snd_nxt)) if (SEQ_GT(onxt, tp->snd_nxt))
tp->snd_nxt = onxt; tp->snd_nxt = onxt;
goto drop; goto drop;
} else if (tp->t_dupacks > tcprexmtthresh) {
tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
} }
} else } else
tp->t_dupacks = 0; tp->t_dupacks = 0;
break; break;
} }
KASSERT(SEQ_GT(th->th_ack, tp->snd_una), ("th_ack <= snd_una"));
/* /*
* If the congestion window was inflated to account * If the congestion window was inflated to account
* for the other side's cached packets, retract it. * for the other side's cached packets, retract it.
*/ */
if (tcp_do_newreno) { if (tcp_do_newreno) {
int is_partialack = SEQ_LT(th->th_ack, tp->snd_recover); if (SEQ_LT(tp->snd_una, tp->snd_recover)) {
if (tp->t_dupacks >= tcprexmtthresh) { if (SEQ_LT(th->th_ack, tp->snd_recover)) {
if (is_partialack) {
tcp_newreno_partial_ack(tp, th); tcp_newreno_partial_ack(tp, th);
} else { } else {
/* /*
@ -1781,18 +1789,12 @@ trimthenstep6:
tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd = tp->snd_ssthresh;
} }
} }
/*
* Reset dupacks, except on partial acks in
* fast recovery.
*/
if (!(tp->t_dupacks >= tcprexmtthresh && is_partialack))
tp->t_dupacks = 0;
} else { } else {
if (tp->t_dupacks >= tcprexmtthresh && if (tp->t_dupacks >= tcprexmtthresh &&
tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd > tp->snd_ssthresh)
tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd = tp->snd_ssthresh;
tp->t_dupacks = 0;
} }
tp->t_dupacks = 0;
if (SEQ_GT(th->th_ack, tp->snd_max)) { if (SEQ_GT(th->th_ack, tp->snd_max)) {
tcpstat.tcps_rcvacktoomuch++; tcpstat.tcps_rcvacktoomuch++;
goto dropafterack; goto dropafterack;
@ -1835,6 +1837,7 @@ process_ACK:
++tcpstat.tcps_sndrexmitbad; ++tcpstat.tcps_sndrexmitbad;
tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_cwnd = tp->snd_cwnd_prev;
tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev;
tp->snd_high = tp->snd_high_prev;
tp->snd_nxt = tp->snd_max; tp->snd_nxt = tp->snd_max;
tp->t_badrxtwin = 0; /* XXX probably not required */ tp->t_badrxtwin = 0; /* XXX probably not required */
} }
@ -1888,18 +1891,11 @@ process_ACK:
* Otherwise open linearly: maxseg per window * Otherwise open linearly: maxseg per window
* (maxseg^2 / cwnd per packet). * (maxseg^2 / cwnd per packet).
*/ */
{ if (!tcp_do_newreno || SEQ_GEQ(tp->snd_una, tp->snd_recover)) {
register u_int cw = tp->snd_cwnd; register u_int cw = tp->snd_cwnd;
register u_int incr = tp->t_maxseg; register u_int incr = tp->t_maxseg;
if (cw > tp->snd_ssthresh) if (cw > tp->snd_ssthresh)
incr = incr * incr / cw; incr = incr * incr / cw;
/*
* If t_dupacks != 0 here, it indicates that we are still
* in NewReno fast recovery mode, so we leave the congestion
* window alone.
*/
if (!tcp_do_newreno || tp->t_dupacks == 0)
tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale); tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale);
} }
if (acked > so->so_snd.sb_cc) { if (acked > so->so_snd.sb_cc) {
@ -1912,6 +1908,13 @@ process_ACK:
ourfinisacked = 0; ourfinisacked = 0;
} }
sowwakeup(so); sowwakeup(so);
/* detect una wraparound */
if (SEQ_GEQ(tp->snd_una, tp->snd_recover) &&
SEQ_LT(th->th_ack, tp->snd_recover))
tp->snd_recover = th->th_ack;
if (SEQ_GT(tp->snd_una, tp->snd_high) &&
SEQ_LEQ(th->th_ack, tp->snd_high))
tp->snd_high = th->th_ack - 1;
tp->snd_una = th->th_ack; tp->snd_una = th->th_ack;
if (SEQ_LT(tp->snd_nxt, tp->snd_una)) if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una; tp->snd_nxt = tp->snd_una;

View File

@ -975,7 +975,10 @@ after_listen:
if (SEQ_GT(th->th_ack, tp->snd_una) && if (SEQ_GT(th->th_ack, tp->snd_una) &&
SEQ_LEQ(th->th_ack, tp->snd_max) && SEQ_LEQ(th->th_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd && tp->snd_cwnd >= tp->snd_wnd &&
tp->t_dupacks < tcprexmtthresh) { ((!tcp_do_newreno &&
tp->t_dupacks < tcprexmtthresh) ||
(tcp_do_newreno &&
!SEQ_LT(tp->snd_una, tp->snd_recover)))) {
KASSERT(headlocked, ("headlocked")); KASSERT(headlocked, ("headlocked"));
INP_INFO_WUNLOCK(&tcbinfo); INP_INFO_WUNLOCK(&tcbinfo);
headlocked = 0; headlocked = 0;
@ -988,9 +991,11 @@ after_listen:
*/ */
if (tp->t_rxtshift == 1 && if (tp->t_rxtshift == 1 &&
ticks < tp->t_badrxtwin) { ticks < tp->t_badrxtwin) {
++tcpstat.tcps_sndrexmitbad;
tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_cwnd = tp->snd_cwnd_prev;
tp->snd_ssthresh = tp->snd_ssthresh =
tp->snd_ssthresh_prev; tp->snd_ssthresh_prev;
tp->snd_high = tp->snd_high_prev;
tp->snd_nxt = tp->snd_max; tp->snd_nxt = tp->snd_max;
tp->t_badrxtwin = 0; tp->t_badrxtwin = 0;
} }
@ -1017,11 +1022,15 @@ after_listen:
tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked; tcpstat.tcps_rcvackbyte += acked;
sbdrop(&so->so_snd, acked); sbdrop(&so->so_snd, acked);
if (SEQ_GT(tp->snd_una, tp->snd_high) &&
SEQ_LEQ(th->th_ack, tp->snd_high))
tp->snd_high = th->th_ack - 1;
tp->snd_una = tp->snd_recover = th->th_ack;
/* /*
* pull snd_wl2 up to prevent seq wrap relative * pull snd_wl2 up to prevent seq wrap relative
* to th_ack. * to th_ack.
*/ */
tp->snd_wl2 = tp->snd_una = th->th_ack; tp->snd_wl2 = th->th_ack;
tp->t_dupacks = 0; tp->t_dupacks = 0;
m_freem(m); m_freem(m);
ND6_HINT(tp); /* some progress has been done */ ND6_HINT(tp); /* some progress has been done */
@ -1714,22 +1723,23 @@ trimthenstep6:
if (!callout_active(tp->tt_rexmt) || if (!callout_active(tp->tt_rexmt) ||
th->th_ack != tp->snd_una) th->th_ack != tp->snd_una)
tp->t_dupacks = 0; tp->t_dupacks = 0;
else if (++tp->t_dupacks == tcprexmtthresh) { else if (++tp->t_dupacks > tcprexmtthresh ||
tcp_seq onxt = tp->snd_nxt; (tcp_do_newreno &&
u_int win = SEQ_LT(tp->snd_una,
min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->snd_recover))) {
tp->t_maxseg;
if (tcp_do_newreno &&
SEQ_LT(th->th_ack,
tp->snd_recover)) {
/* False retransmit, should not
* cut window
*/
tp->snd_cwnd += tp->t_maxseg; tp->snd_cwnd += tp->t_maxseg;
tp->t_dupacks = 0;
(void) tcp_output(tp); (void) tcp_output(tp);
goto drop; goto drop;
} else if (tp->t_dupacks == tcprexmtthresh) {
tcp_seq onxt = tp->snd_nxt;
u_int win;
if (tcp_do_newreno &&
SEQ_LEQ(th->th_ack, tp->snd_high)) {
tp->t_dupacks = 0;
break;
} }
win = min(tp->snd_wnd, tp->snd_cwnd) /
2 / tp->t_maxseg;
if (win < 2) if (win < 2)
win = 2; win = 2;
tp->snd_ssthresh = win * tp->t_maxseg; tp->snd_ssthresh = win * tp->t_maxseg;
@ -1744,23 +1754,21 @@ trimthenstep6:
if (SEQ_GT(onxt, tp->snd_nxt)) if (SEQ_GT(onxt, tp->snd_nxt))
tp->snd_nxt = onxt; tp->snd_nxt = onxt;
goto drop; goto drop;
} else if (tp->t_dupacks > tcprexmtthresh) {
tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
} }
} else } else
tp->t_dupacks = 0; tp->t_dupacks = 0;
break; break;
} }
KASSERT(SEQ_GT(th->th_ack, tp->snd_una), ("th_ack <= snd_una"));
/* /*
* If the congestion window was inflated to account * If the congestion window was inflated to account
* for the other side's cached packets, retract it. * for the other side's cached packets, retract it.
*/ */
if (tcp_do_newreno) { if (tcp_do_newreno) {
int is_partialack = SEQ_LT(th->th_ack, tp->snd_recover); if (SEQ_LT(tp->snd_una, tp->snd_recover)) {
if (tp->t_dupacks >= tcprexmtthresh) { if (SEQ_LT(th->th_ack, tp->snd_recover)) {
if (is_partialack) {
tcp_newreno_partial_ack(tp, th); tcp_newreno_partial_ack(tp, th);
} else { } else {
/* /*
@ -1781,18 +1789,12 @@ trimthenstep6:
tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd = tp->snd_ssthresh;
} }
} }
/*
* Reset dupacks, except on partial acks in
* fast recovery.
*/
if (!(tp->t_dupacks >= tcprexmtthresh && is_partialack))
tp->t_dupacks = 0;
} else { } else {
if (tp->t_dupacks >= tcprexmtthresh && if (tp->t_dupacks >= tcprexmtthresh &&
tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd > tp->snd_ssthresh)
tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd = tp->snd_ssthresh;
tp->t_dupacks = 0;
} }
tp->t_dupacks = 0;
if (SEQ_GT(th->th_ack, tp->snd_max)) { if (SEQ_GT(th->th_ack, tp->snd_max)) {
tcpstat.tcps_rcvacktoomuch++; tcpstat.tcps_rcvacktoomuch++;
goto dropafterack; goto dropafterack;
@ -1835,6 +1837,7 @@ process_ACK:
++tcpstat.tcps_sndrexmitbad; ++tcpstat.tcps_sndrexmitbad;
tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_cwnd = tp->snd_cwnd_prev;
tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev;
tp->snd_high = tp->snd_high_prev;
tp->snd_nxt = tp->snd_max; tp->snd_nxt = tp->snd_max;
tp->t_badrxtwin = 0; /* XXX probably not required */ tp->t_badrxtwin = 0; /* XXX probably not required */
} }
@ -1888,18 +1891,11 @@ process_ACK:
* Otherwise open linearly: maxseg per window * Otherwise open linearly: maxseg per window
* (maxseg^2 / cwnd per packet). * (maxseg^2 / cwnd per packet).
*/ */
{ if (!tcp_do_newreno || SEQ_GEQ(tp->snd_una, tp->snd_recover)) {
register u_int cw = tp->snd_cwnd; register u_int cw = tp->snd_cwnd;
register u_int incr = tp->t_maxseg; register u_int incr = tp->t_maxseg;
if (cw > tp->snd_ssthresh) if (cw > tp->snd_ssthresh)
incr = incr * incr / cw; incr = incr * incr / cw;
/*
* If t_dupacks != 0 here, it indicates that we are still
* in NewReno fast recovery mode, so we leave the congestion
* window alone.
*/
if (!tcp_do_newreno || tp->t_dupacks == 0)
tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale); tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale);
} }
if (acked > so->so_snd.sb_cc) { if (acked > so->so_snd.sb_cc) {
@ -1912,6 +1908,13 @@ process_ACK:
ourfinisacked = 0; ourfinisacked = 0;
} }
sowwakeup(so); sowwakeup(so);
/* detect una wraparound */
if (SEQ_GEQ(tp->snd_una, tp->snd_recover) &&
SEQ_LT(th->th_ack, tp->snd_recover))
tp->snd_recover = th->th_ack;
if (SEQ_GT(tp->snd_una, tp->snd_high) &&
SEQ_LEQ(th->th_ack, tp->snd_high))
tp->snd_high = th->th_ack - 1;
tp->snd_una = th->th_ack; tp->snd_una = th->th_ack;
if (SEQ_LT(tp->snd_nxt, tp->snd_una)) if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una; tp->snd_nxt = tp->snd_una;

View File

@ -73,7 +73,7 @@
#define tcp_sendseqinit(tp) \ #define tcp_sendseqinit(tp) \
(tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
(tp)->snd_recover = (tp)->iss (tp)->snd_recover = (tp)->snd_high = (tp)->iss
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * hz) #define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * hz)
/* timestamp wrap-around time */ /* timestamp wrap-around time */

View File

@ -462,6 +462,7 @@ tcp_timer_rexmt(xtp)
*/ */
tp->snd_cwnd_prev = tp->snd_cwnd; tp->snd_cwnd_prev = tp->snd_cwnd;
tp->snd_ssthresh_prev = tp->snd_ssthresh; tp->snd_ssthresh_prev = tp->snd_ssthresh;
tp->snd_high_prev = tp->snd_high;
tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1));
} }
tcpstat.tcps_rexmttimeo++; tcpstat.tcps_rexmttimeo++;
@ -499,11 +500,7 @@ tcp_timer_rexmt(xtp)
tp->t_srtt = 0; tp->t_srtt = 0;
} }
tp->snd_nxt = tp->snd_una; tp->snd_nxt = tp->snd_una;
/* tp->snd_high = tp->snd_max;
* Note: We overload snd_recover to function also as the
* snd_last variable described in RFC 2582
*/
tp->snd_recover = tp->snd_max;
/* /*
* Force a segment to be sent. * Force a segment to be sent.
*/ */

View File

@ -130,7 +130,8 @@ struct tcpcb {
* linear switch * linear switch
*/ */
u_long snd_bandwidth; /* calculated bandwidth or 0 */ u_long snd_bandwidth; /* calculated bandwidth or 0 */
tcp_seq snd_recover; /* for use in fast recovery */ tcp_seq snd_recover; /* for use in NewReno Fast Recovery */
tcp_seq snd_high; /* for use in NewReno Fast Recovery */
u_int t_maxopd; /* mss plus options */ u_int t_maxopd; /* mss plus options */
@ -174,6 +175,7 @@ struct tcpcb {
/* experimental */ /* experimental */
u_long snd_cwnd_prev; /* cwnd prior to retransmit */ u_long snd_cwnd_prev; /* cwnd prior to retransmit */
u_long snd_ssthresh_prev; /* ssthresh prior to retransmit */ u_long snd_ssthresh_prev; /* ssthresh prior to retransmit */
tcp_seq snd_high_prev; /* snd_high prior to retransmit */
u_long t_badrxtwin; /* window for retransmit recovery */ u_long t_badrxtwin; /* window for retransmit recovery */
}; };