diff options
author | jlemon <jlemon@FreeBSD.org> | 2000-05-06 03:31:09 +0000 |
---|---|---|
committer | jlemon <jlemon@FreeBSD.org> | 2000-05-06 03:31:09 +0000 |
commit | 8a3c72bb35758d483f05567e3ff8926bee1a8813 (patch) | |
tree | 22b719c35e23206090023b078760d76bcd4627e7 /sys/netinet/tcp_input.c | |
parent | 796be5b52651cb1727e79565d25b60e69d1f738a (diff) | |
download | FreeBSD-src-8a3c72bb35758d483f05567e3ff8926bee1a8813.zip FreeBSD-src-8a3c72bb35758d483f05567e3ff8926bee1a8813.tar.gz |
Implement TCP NewReno, as documented in RFC 2582. This allows
better recovery for multiple packet losses in a single window.
The algorithm can be toggled via the sysctl net.inet.tcp.newreno,
which defaults to "on".
Submitted by: Jayanth Vijayaraghavan <jayanth@yahoo-inc.com>
Diffstat (limited to 'sys/netinet/tcp_input.c')
-rw-r--r-- | sys/netinet/tcp_input.c | 79 |
1 files changed, 73 insertions, 6 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 7922253..82ab08f 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -148,6 +148,7 @@ static void tcp_pulloutofband __P((struct socket *, static int tcp_reass __P((struct tcpcb *, struct tcphdr *, int *, struct mbuf *)); static void tcp_xmit_timer __P((struct tcpcb *, int)); +static int tcp_newreno __P((struct tcpcb *, struct tcphdr *)); /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ #ifdef INET6 @@ -1104,6 +1105,7 @@ findpcb: tp->irs = th->th_seq; tcp_sendseqinit(tp); tcp_rcvseqinit(tp); + tp->snd_recover = tp->snd_una; /* * Initialization of the tcpcb for transaction; * set SND.WND = SEG.WND, @@ -1780,10 +1782,20 @@ trimthenstep6: u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / 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->t_dupacks = 0; + (void) tcp_output(tp); + goto drop; + } if (win < 2) win = 2; tp->snd_ssthresh = win * tp->t_maxseg; + tp->snd_recover = tp->snd_max; callout_stop(tp->tt_rexmt); tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; @@ -1807,10 +1819,26 @@ trimthenstep6: * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ - if (tp->t_dupacks >= tcprexmtthresh && - tp->snd_cwnd > tp->snd_ssthresh) - tp->snd_cwnd = tp->snd_ssthresh; - tp->t_dupacks = 0; + if (tcp_do_newreno == 0) { + if (tp->t_dupacks >= tcprexmtthresh && + tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + tp->t_dupacks = 0; + } else if (tp->t_dupacks >= tcprexmtthresh && + !tcp_newreno(tp, th)) { + /* + * Window inflation should have left us with approx. + * snd_ssthresh outstanding data. But in case we + * would be inclined to send a burst, better to do + * it via the slow start mechanism. + */ + if (SEQ_GT(th->th_ack + tp->snd_ssthresh, tp->snd_max)) + tp->snd_cwnd = + tp->snd_max - th->th_ack + tp->t_maxseg; + else + tp->snd_cwnd = tp->snd_ssthresh; + tp->t_dupacks = 0; + } if (SEQ_GT(th->th_ack, tp->snd_max)) { tcpstat.tcps_rcvacktoomuch++; goto dropafterack; @@ -1903,7 +1931,8 @@ process_ACK: if (cw > tp->snd_ssthresh) incr = incr * incr / cw; - tp->snd_cwnd = min(cw + incr, TCP_MAXWIN << tp->snd_scale); + if (tcp_do_newreno == 0 || SEQ_GEQ(th->th_ack, tp->snd_recover)) + tp->snd_cwnd = min(cw + incr,TCP_MAXWIN<<tp->snd_scale); } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; @@ -2791,3 +2820,41 @@ tcp_mssopt(tp) return rt->rt_ifp->if_mtu - min_protoh; } + + +/* + * Checks for partial ack. If partial ack arrives, force the retransmission + * of the next unacknowledged segment, do not clear tp->t_dupacks, and return + * 1. By setting snd_nxt to ti_ack, this forces retransmission timer to + * be started again. If the ack advances at least to tp->snd_recover, return 0. + */ +static int +tcp_newreno(tp, th) + struct tcpcb *tp; + struct tcphdr *th; +{ + if (SEQ_LT(th->th_ack, tp->snd_recover)) { + tcp_seq onxt = tp->snd_nxt; + tcp_seq ouna = tp->snd_una; /* Haven't updated snd_una yet*/ + u_long ocwnd = tp->snd_cwnd; + + callout_stop(tp->tt_rexmt); + tp->t_rtttime = 0; + tp->snd_nxt = th->th_ack; + tp->snd_cwnd = tp->t_maxseg; + tp->snd_una = th->th_ack; + (void) tcp_output(tp); + + tp->snd_cwnd = ocwnd; + tp->snd_una = ouna; + if (SEQ_GT(onxt, tp->snd_nxt)) + tp->snd_nxt = onxt; + /* + * Partial window deflation. Relies on fact that tp->snd_una + * not updated yet. + */ + tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); + return (1); + } + return (0); +} |