summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjlemon <jlemon@FreeBSD.org>1999-08-30 21:17:07 +0000
committerjlemon <jlemon@FreeBSD.org>1999-08-30 21:17:07 +0000
commit628be0515eace470ecc5e0055dbc9cee71e2adb9 (patch)
tree541ccb366454973a67b93f62b3164fc1a98eced0
parent6aee941745b6eecfd2d3e23b412969b4ddf18102 (diff)
downloadFreeBSD-src-628be0515eace470ecc5e0055dbc9cee71e2adb9.zip
FreeBSD-src-628be0515eace470ecc5e0055dbc9cee71e2adb9.tar.gz
Restructure TCP timeout handling:
- eliminate the fast/slow timeout lists for TCP and instead use a callout entry for each timer. - increase the TCP timer granularity to HZ - implement "bad retransmit" recovery, as presented in "On Estimating End-to-End Network Path Properties", by Allman and Paxson. Submitted by: jlemon, wollmann
-rw-r--r--sys/kern/kern_timeout.c18
-rw-r--r--sys/netinet/in_proto.c2
-rw-r--r--sys/netinet/tcp_input.c172
-rw-r--r--sys/netinet/tcp_output.c73
-rw-r--r--sys/netinet/tcp_reass.c172
-rw-r--r--sys/netinet/tcp_seq.h2
-rw-r--r--sys/netinet/tcp_subr.c38
-rw-r--r--sys/netinet/tcp_timer.c510
-rw-r--r--sys/netinet/tcp_timer.h60
-rw-r--r--sys/netinet/tcp_timewait.c38
-rw-r--r--sys/netinet/tcp_usrreq.c7
-rw-r--r--sys/netinet/tcp_var.h25
-rw-r--r--sys/sys/callout.h10
13 files changed, 704 insertions, 423 deletions
diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c
index 45bc266..e73d371 100644
--- a/sys/kern/kern_timeout.c
+++ b/sys/kern/kern_timeout.c
@@ -125,8 +125,7 @@ softclock()
c_links.sle);
} else {
c->c_flags =
- (c->c_flags & ~CALLOUT_PENDING)
- | CALLOUT_FIRED;
+ (c->c_flags & ~CALLOUT_PENDING);
}
splx(s);
c_func(c_arg);
@@ -218,11 +217,11 @@ callout_handle_init(struct callout_handle *handle)
* callout_init() - initialize a callout structure so that it can
* safely be passed to callout_reset() and callout_stop()
*
- * <sys/callout.h> defines two convenience macros:
+ * <sys/callout.h> defines three convenience macros:
*
- * callout_pending() - returns number of ticks until callout fires, or 0
- * if not scheduled
- * callout_fired() - returns truth if callout has already been fired
+ * callout_active() - returns truth if callout has not been serviced
+ * callout_pending() - returns truth if callout is still waiting for timeout
+ * callout_deactivate() - marks the callout as having been serviced
*/
void
callout_reset(c, to_ticks, ftn, arg)
@@ -240,13 +239,13 @@ callout_reset(c, to_ticks, ftn, arg)
/*
* We could spl down here and back up at the TAILQ_INSERT_TAIL,
* but there's no point since doing this setup doesn't take much
- ^ time.
+ * time.
*/
if (to_ticks <= 0)
to_ticks = 1;
c->c_arg = arg;
- c->c_flags = (c->c_flags & ~CALLOUT_FIRED) | CALLOUT_PENDING;
+ c->c_flags |= (CALLOUT_ACTIVE | CALLOUT_PENDING);
c->c_func = ftn;
c->c_time = ticks + to_ticks;
TAILQ_INSERT_TAIL(&callwheel[c->c_time & callwheelmask],
@@ -266,10 +265,11 @@ callout_stop(c)
* Don't attempt to delete a callout that's not on the queue.
*/
if (!(c->c_flags & CALLOUT_PENDING)) {
+ c->c_flags &= ~CALLOUT_ACTIVE;
splx(s);
return;
}
- c->c_flags &= ~CALLOUT_PENDING;
+ c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING);
if (nextsoftcheck == c) {
nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index 786b4e8..22647d9 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -92,7 +92,7 @@ struct protosw inetsw[] = {
PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD,
tcp_input, 0, tcp_ctlinput, tcp_ctloutput,
0,
- tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
+ tcp_init, 0, tcp_slowtimo, tcp_drain,
&tcp_usrreqs
},
{ SOCK_RAW, &inetdomain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index c371e77..53034ff 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -93,7 +93,6 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,
&tcp_delack_enabled, 0,
"Delay ACK to try and piggyback it onto a data packet");
-u_long tcp_now;
struct inpcbhead tcb;
struct inpcbinfo tcbinfo;
@@ -120,7 +119,8 @@ static void tcp_xmit_timer __P((struct tcpcb *, int));
(tp)->t_segq == NULL && \
(tp)->t_state == TCPS_ESTABLISHED) { \
if (tcp_delack_enabled) \
- tp->t_flags |= TF_DELACK; \
+ callout_reset(tp->tt_delack, tcp_delacktime, \
+ tcp_timer_delack, tp); \
else \
tp->t_flags |= TF_ACKNOW; \
(tp)->rcv_nxt += (ti)->ti_len; \
@@ -523,9 +523,9 @@ findpcb:
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
- tp->t_idle = 0;
+ tp->t_rcvtime = ticks;
if (TCPS_HAVEESTABLISHED(tp->t_state))
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
/*
* Process options if not in LISTEN state,
@@ -575,7 +575,7 @@ findpcb:
*/
if ((to.to_flag & TOF_TS) != 0 &&
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
tp->ts_recent = to.to_tsval;
}
@@ -588,12 +588,23 @@ findpcb:
* this is a pure ack for outstanding data.
*/
++tcpstat.tcps_predack;
+ /*
+ * "bad retransmit" recovery
+ */
+ if (tp->t_rxtshift == 1 &&
+ ticks < tp->t_badrxtwin) {
+ tp->snd_cwnd = tp->snd_cwnd_prev;
+ tp->snd_ssthresh =
+ tp->snd_ssthresh_prev;
+ tp->snd_nxt = tp->snd_max;
+ tp->t_badrxtwin = 0;
+ }
if ((to.to_flag & TOF_TS) != 0)
tcp_xmit_timer(tp,
- tcp_now - to.to_tsecr + 1);
- else if (tp->t_rtt &&
+ ticks - to.to_tsecr + 1);
+ else if (tp->t_rtttime &&
SEQ_GT(ti->ti_ack, tp->t_rtseq))
- tcp_xmit_timer(tp, tp->t_rtt);
+ tcp_xmit_timer(tp, ticks - tp->t_rtttime);
acked = ti->ti_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
@@ -611,9 +622,11 @@ findpcb:
* decide between more output or persist.
*/
if (tp->snd_una == tp->snd_max)
- tp->t_timer[TCPT_REXMT] = 0;
- else if (tp->t_timer[TCPT_PERSIST] == 0)
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ callout_stop(tp->tt_rexmt);
+ else if (!callout_active(tp->tt_persist))
+ callout_reset(tp->tt_rexmt,
+ tp->t_rxtcur,
+ tcp_timer_rexmt, tp);
sowwakeup(so);
if (so->so_snd.sb_cc)
@@ -638,7 +651,8 @@ findpcb:
sbappend(&so->so_rcv, m);
sorwakeup(so);
if (tcp_delack_enabled) {
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
} else {
tp->t_flags |= TF_ACKNOW;
tcp_output(tp);
@@ -760,6 +774,7 @@ findpcb:
taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) {
taop->tao_cc = to.to_cc;
+ tp->t_starttime = ticks;
tp->t_state = TCPS_ESTABLISHED;
/*
@@ -771,9 +786,11 @@ findpcb:
*/
if (tcp_delack_enabled && ((tiflags & TH_FIN) ||
(ti->ti_len != 0 &&
- in_localaddr(inp->inp_faddr))))
- tp->t_flags |= (TF_DELACK | TF_NEEDSYN);
- else
+ in_localaddr(inp->inp_faddr)))) {
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
+ tp->t_flags |= TF_NEEDSYN;
+ } else
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
/*
@@ -784,7 +801,8 @@ findpcb:
tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN);
tcpstat.tcps_connects++;
soisconnected(so);
- tp->t_timer[TCPT_KEEP] = tcp_keepinit;
+ callout_reset(tp->tt_keep, tcp_keepinit,
+ tcp_timer_keep, tp);
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
@@ -803,7 +821,7 @@ findpcb:
*/
tp->t_flags |= TF_ACKNOW;
tp->t_state = TCPS_SYN_RECEIVED;
- tp->t_timer[TCPT_KEEP] = tcp_keepinit;
+ callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
@@ -903,7 +921,8 @@ findpcb:
* ACKNOW will be turned on later.
*/
if (tcp_delack_enabled && ti->ti_len != 0)
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
else
tp->t_flags |= TF_ACKNOW;
/*
@@ -912,13 +931,15 @@ findpcb:
* SYN_SENT --> ESTABLISHED
* SYN_SENT* --> FIN_WAIT_1
*/
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
tiflags &= ~TH_SYN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle,
+ tcp_timer_keep, tp);
}
} else {
/*
@@ -931,7 +952,7 @@ findpcb:
* If there was no CC option, clear cached CC value.
*/
tp->t_flags |= TF_ACKNOW;
- tp->t_timer[TCPT_REXMT] = 0;
+ callout_stop(tp->tt_rexmt);
if (to.to_flag & TOF_CC) {
if (taop->tao_cc != 0 &&
CC_GT(to.to_cc, taop->tao_cc)) {
@@ -941,13 +962,16 @@ findpcb:
* SYN-SENT* -> FIN-WAIT-1*
*/
taop->tao_cc = to.to_cc;
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] =
- tcp_keepidle;
+ callout_reset(tp->tt_keep,
+ tcp_keepidle,
+ tcp_timer_keep,
+ tp);
}
tp->t_flags |= TF_NEEDSYN;
} else
@@ -1006,7 +1030,7 @@ trimthenstep6:
if ((tiflags & TH_SYN) &&
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
if (tp->t_state == TCPS_TIME_WAIT &&
- tp->t_duration > TCPTV_MSL)
+ (ticks - tp->t_starttime) > tcp_msl)
goto dropwithreset;
if (CC_GT(to.to_cc, tp->cc_recv)) {
tp = tcp_close(tp);
@@ -1115,7 +1139,7 @@ trimthenstep6:
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
/* Check to see if ts_recent is over 24 days old. */
- if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
/*
* Invalidate ts_recent. If this segment updates
* ts_recent, the age will be reset later and ts_recent
@@ -1262,7 +1286,7 @@ trimthenstep6:
*/
if ((to.to_flag & TOF_TS) != 0 &&
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
tp->ts_recent = to.to_tsval;
}
@@ -1322,12 +1346,14 @@ trimthenstep6:
* SYN-RECEIVED -> ESTABLISHED
* SYN-RECEIVED* -> FIN-WAIT-1
*/
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle,
+ tcp_timer_keep, tp);
}
/*
* If segment contains data or ACK, will call tcp_reass()
@@ -1382,7 +1408,7 @@ trimthenstep6:
* to keep a constant cwnd packets in the
* network.
*/
- if (tp->t_timer[TCPT_REXMT] == 0 ||
+ if (!callout_active(tp->tt_rexmt) ||
ti->ti_ack != tp->snd_una)
tp->t_dupacks = 0;
else if (++tp->t_dupacks == tcprexmtthresh) {
@@ -1394,8 +1420,8 @@ trimthenstep6:
if (win < 2)
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg;
- tp->t_timer[TCPT_REXMT] = 0;
- tp->t_rtt = 0;
+ callout_stop(tp->tt_rexmt);
+ tp->t_rtttime = 0;
tp->snd_nxt = ti->ti_ack;
tp->snd_cwnd = tp->t_maxseg;
(void) tcp_output(tp);
@@ -1453,6 +1479,20 @@ process_ACK:
tcpstat.tcps_rcvackbyte += acked;
/*
+ * If we just performed our first retransmit, and the ACK
+ * arrives within our recovery window, then it was a mistake
+ * to do the retransmit in the first place. Recover our
+ * original cwnd and ssthresh, and proceed to transmit where
+ * we left off.
+ */
+ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {
+ tp->snd_cwnd = tp->snd_cwnd_prev;
+ tp->snd_ssthresh = tp->snd_ssthresh_prev;
+ tp->snd_nxt = tp->snd_max;
+ tp->t_badrxtwin = 0; /* XXX probably not required */
+ }
+
+ /*
* If we have a timestamp reply, update smoothed
* round trip time. If no timestamp is present but
* transmit timer is running and timed sequence
@@ -1462,9 +1502,9 @@ process_ACK:
* Recompute the initial retransmit timer.
*/
if (to.to_flag & TOF_TS)
- tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1);
- else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
- tcp_xmit_timer(tp,tp->t_rtt);
+ tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
+ else if (tp->t_rtttime && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, ticks - tp->t_rtttime);
/*
* If all outstanding data is acked, stop retransmit
@@ -1473,10 +1513,11 @@ process_ACK:
* timer, using current (possibly backed-off) value.
*/
if (ti->ti_ack == tp->snd_max) {
- tp->t_timer[TCPT_REXMT] = 0;
+ callout_stop(tp->tt_rexmt);
needoutput = 1;
- } else if (tp->t_timer[TCPT_PERSIST] == 0)
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ } else if (!callout_active(tp->tt_persist))
+ callout_reset(tp->tt_rexmt, tp->t_rxtcur,
+ tcp_timer_rexmt, tp);
/*
* If no data (only SYN) was ACK'd,
@@ -1532,7 +1573,8 @@ process_ACK:
*/
if (so->so_state & SS_CANTRCVMORE) {
soisdisconnected(so);
- tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ callout_reset(tp->tt_2msl, tcp_maxidle,
+ tcp_timer_2msl, tp);
}
tp->t_state = TCPS_FIN_WAIT_2;
}
@@ -1550,11 +1592,14 @@ process_ACK:
tcp_canceltimers(tp);
/* Shorten TIME_WAIT [RFC-1644, p.28] */
if (tp->cc_recv != 0 &&
- tp->t_duration < TCPTV_MSL)
- tp->t_timer[TCPT_2MSL] =
- tp->t_rxtcur * TCPTV_TWTRUNC;
+ (ticks - tp->t_starttime) < tcp_msl)
+ callout_reset(tp->tt_2msl,
+ tp->t_rxtcur *
+ TCPTV_TWTRUNC,
+ tcp_timer_2msl, tp);
else
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
soisdisconnected(so);
}
break;
@@ -1578,7 +1623,8 @@ process_ACK:
* it and restart the finack timer.
*/
case TCPS_TIME_WAIT:
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
goto dropafterack;
}
}
@@ -1702,7 +1748,8 @@ dodata: /* XXX */
* more input can be expected, send ACK now.
*/
if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN))
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
else
tp->t_flags |= TF_ACKNOW;
tp->rcv_nxt++;
@@ -1714,6 +1761,8 @@ dodata: /* XXX */
* enter the CLOSE_WAIT state.
*/
case TCPS_SYN_RECEIVED:
+ tp->t_starttime = ticks;
+ /*FALLTHROUGH*/
case TCPS_ESTABLISHED:
tp->t_state = TCPS_CLOSE_WAIT;
break;
@@ -1736,14 +1785,16 @@ dodata: /* XXX */
tcp_canceltimers(tp);
/* Shorten TIME_WAIT [RFC-1644, p.28] */
if (tp->cc_recv != 0 &&
- tp->t_duration < TCPTV_MSL) {
- tp->t_timer[TCPT_2MSL] =
- tp->t_rxtcur * TCPTV_TWTRUNC;
+ (ticks - tp->t_starttime) < tcp_msl) {
+ callout_reset(tp->tt_2msl,
+ tp->t_rxtcur * TCPTV_TWTRUNC,
+ tcp_timer_2msl, tp);
/* For transaction client, force ACK now. */
tp->t_flags |= TF_ACKNOW;
}
else
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
soisdisconnected(so);
break;
@@ -1751,7 +1802,8 @@ dodata: /* XXX */
* In TIME_WAIT state restart the 2 MSL time_wait timer.
*/
case TCPS_TIME_WAIT:
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
break;
}
}
@@ -1900,7 +1952,7 @@ tcp_dooptions(tp, cp, cnt, ti, to)
if (ti->ti_flags & TH_SYN) {
tp->t_flags |= TF_RCVD_TSTMP;
tp->ts_recent = to->to_tsval;
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
}
break;
case TCPOPT_CC:
@@ -1988,7 +2040,7 @@ tcp_pulloutofband(so, ti, m)
static void
tcp_xmit_timer(tp, rtt)
register struct tcpcb *tp;
- short rtt;
+ int rtt;
{
register int delta;
@@ -2032,7 +2084,7 @@ tcp_xmit_timer(tp, rtt)
tp->t_srtt = rtt << TCP_RTT_SHIFT;
tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
}
- tp->t_rtt = 0;
+ tp->t_rtttime = 0;
tp->t_rxtshift = 0;
/*
@@ -2143,12 +2195,12 @@ tcp_mss(tp, offer)
* is also a minimum value; this is subject to time.
*/
if (rt->rt_rmx.rmx_locks & RTV_RTT)
- tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
- tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ tp->t_rttmin = rtt / (RTM_RTTUNIT / hz);
+ tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
tcpstat.tcps_usedrtt++;
if (rt->rt_rmx.rmx_rttvar) {
tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
tcpstat.tcps_usedrttvar++;
} else {
/* default variation is +- 1 rtt */
@@ -2156,8 +2208,8 @@ tcp_mss(tp, offer)
tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
}
TCPT_RANGESET(tp->t_rxtcur,
- ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
- tp->t_rttmin, TCPTV_REXMTMAX);
+ ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+ tp->t_rttmin, TCPTV_REXMTMAX);
}
/*
* if there's an mtu associated with the route, use it
@@ -2231,11 +2283,15 @@ tcp_mss(tp, offer)
bufsize = sb_max;
(void)sbreserve(&so->so_rcv, bufsize);
}
+
/*
- * Don't force slow-start on local network.
+ * Set the slow-start flight size depending on whether this
+ * is a local network or not.
*/
- if (!in_localaddr(inp->inp_faddr))
- tp->snd_cwnd = mss;
+ if (in_localaddr(inp->inp_faddr))
+ tp->snd_cwnd = mss * ss_fltsz_local;
+ else
+ tp->snd_cwnd = mss * ss_fltsz;
if (rt->rt_rmx.rmx_ssthresh) {
/*
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 0fbceb8..d029f07 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -73,6 +73,13 @@ static int path_mtu_discovery = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, path_mtu_discovery, CTLFLAG_RW,
&path_mtu_discovery, 1, "Enable Path MTU Discovery");
+int ss_fltsz = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowstart_flightsize, CTLFLAG_RW,
+ &ss_fltsz, 1, "Slow start flight size");
+
+int ss_fltsz_local = TCP_MAXWIN; /* something large */
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW,
+ &ss_fltsz_local, 1, "Slow start flight size for local networks");
/*
* Tcp output routine: figure out what should be sent and send it.
@@ -99,13 +106,20 @@ tcp_output(tp)
* to send, then transmit; otherwise, investigate further.
*/
idle = (tp->snd_max == tp->snd_una);
- if (idle && tp->t_idle >= tp->t_rxtcur)
+ if (idle && (ticks - tp->t_rcvtime) >= tp->t_rxtcur) {
/*
* We have been idle for "a while" and no acks are
* expected to clock out any data we send --
* slow start to get ack "clock" running again.
- */
- tp->snd_cwnd = tp->t_maxseg;
+ *
+ * Set the slow-start flight size depending on whether
+ * this is a local network or not.
+ */
+ if (in_localaddr(tp->t_inpcb->inp_faddr))
+ tp->snd_cwnd = tp->t_maxseg * ss_fltsz_local;
+ else
+ tp->snd_cwnd = tp->t_maxseg * ss_fltsz;
+ }
again:
sendalot = 0;
off = tp->snd_nxt - tp->snd_una;
@@ -149,7 +163,7 @@ again:
flags &= ~TH_FIN;
win = 1;
} else {
- tp->t_timer[TCPT_PERSIST] = 0;
+ callout_stop(tp->tt_persist);
tp->t_rxtshift = 0;
}
}
@@ -200,10 +214,10 @@ again:
*/
len = 0;
if (win == 0) {
- tp->t_timer[TCPT_REXMT] = 0;
+ callout_stop(tp->tt_rexmt);
tp->t_rxtshift = 0;
tp->snd_nxt = tp->snd_una;
- if (tp->t_timer[TCPT_PERSIST] == 0)
+ if (!callout_active(tp->tt_persist))
tcp_setpersist(tp);
}
}
@@ -291,11 +305,11 @@ again:
* persisting to move a small or zero window
* (re)transmitting and thereby not persisting
*
- * tp->t_timer[TCPT_PERSIST]
- * is set when we are in persist state.
+ * callout_active(tp->tt_persist)
+ * is true when we are in persist state.
* tp->t_force
* is set when we are called to send a persist packet.
- * tp->t_timer[TCPT_REXMT]
+ * callout_active(tp->tt_rexmt)
* is set when we are retransmitting
* The output side is idle when both timers are zero.
*
@@ -305,8 +319,8 @@ again:
* if window is nonzero, transmit what we can,
* otherwise force out a byte.
*/
- if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
- tp->t_timer[TCPT_PERSIST] == 0) {
+ if (so->so_snd.sb_cc && !callout_active(tp->tt_rexmt) &&
+ !callout_active(tp->tt_persist)) {
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
@@ -364,7 +378,7 @@ send:
/* Form timestamp option as shown in appendix A of RFC 1323. */
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
- *lp++ = htonl(tcp_now);
+ *lp++ = htonl(ticks);
*lp = htonl(tp->ts_recent);
optlen += TCPOLEN_TSTAMP_APPA;
}
@@ -569,7 +583,8 @@ send:
* case, since we know we aren't doing a retransmission.
* (retransmit and persist are mutually exclusive...)
*/
- if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ if (len || (flags & (TH_SYN|TH_FIN))
+ || callout_active(tp->tt_persist))
ti->ti_seq = htonl(tp->snd_nxt);
else
ti->ti_seq = htonl(tp->snd_max);
@@ -615,7 +630,7 @@ send:
* In transmit state, time the transmission and arrange for
* the retransmit. In persist state, just set snd_max.
*/
- if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ if (tp->t_force == 0 || !callout_active(tp->tt_persist)) {
tcp_seq startseq = tp->snd_nxt;
/*
@@ -636,8 +651,8 @@ send:
* Time this transmission if not a retransmission and
* not currently timing anything.
*/
- if (tp->t_rtt == 0) {
- tp->t_rtt = 1;
+ if (tp->t_rtttime == 0) {
+ tp->t_rtttime = ticks;
tp->t_rtseq = startseq;
tcpstat.tcps_segstimed++;
}
@@ -651,11 +666,12 @@ send:
* Initialize shift counter which is used for backoff
* of retransmit time.
*/
- if (tp->t_timer[TCPT_REXMT] == 0 &&
+ if (!callout_active(tp->tt_rexmt) &&
tp->snd_nxt != tp->snd_una) {
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
- if (tp->t_timer[TCPT_PERSIST]) {
- tp->t_timer[TCPT_PERSIST] = 0;
+ callout_reset(tp->tt_rexmt, tp->t_rxtcur,
+ tcp_timer_rexmt, tp);
+ if (callout_active(tp->tt_persist)) {
+ callout_stop(tp->tt_persist);
tp->t_rxtshift = 0;
}
}
@@ -733,7 +749,9 @@ out:
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
- tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ tp->t_flags &= ~TF_ACKNOW;
+ if (tcp_delack_enabled)
+ callout_stop(tp->tt_delack);
if (sendalot)
goto again;
return (0);
@@ -743,16 +761,17 @@ void
tcp_setpersist(tp)
register struct tcpcb *tp;
{
- register int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+ int tt;
- if (tp->t_timer[TCPT_REXMT])
- panic("tcp_output REXMT");
+ if (callout_active(tp->tt_rexmt))
+ panic("tcp_setpersist: retransmit pending");
/*
* Start/restart persistance timer.
*/
- TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
- t * tcp_backoff[tp->t_rxtshift],
- TCPTV_PERSMIN, TCPTV_PERSMAX);
+ TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ callout_reset(tp->tt_persist, tt, tcp_timer_persist, tp);
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
tp->t_rxtshift++;
}
diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c
index c371e77..53034ff 100644
--- a/sys/netinet/tcp_reass.c
+++ b/sys/netinet/tcp_reass.c
@@ -93,7 +93,6 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,
&tcp_delack_enabled, 0,
"Delay ACK to try and piggyback it onto a data packet");
-u_long tcp_now;
struct inpcbhead tcb;
struct inpcbinfo tcbinfo;
@@ -120,7 +119,8 @@ static void tcp_xmit_timer __P((struct tcpcb *, int));
(tp)->t_segq == NULL && \
(tp)->t_state == TCPS_ESTABLISHED) { \
if (tcp_delack_enabled) \
- tp->t_flags |= TF_DELACK; \
+ callout_reset(tp->tt_delack, tcp_delacktime, \
+ tcp_timer_delack, tp); \
else \
tp->t_flags |= TF_ACKNOW; \
(tp)->rcv_nxt += (ti)->ti_len; \
@@ -523,9 +523,9 @@ findpcb:
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
- tp->t_idle = 0;
+ tp->t_rcvtime = ticks;
if (TCPS_HAVEESTABLISHED(tp->t_state))
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
/*
* Process options if not in LISTEN state,
@@ -575,7 +575,7 @@ findpcb:
*/
if ((to.to_flag & TOF_TS) != 0 &&
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
tp->ts_recent = to.to_tsval;
}
@@ -588,12 +588,23 @@ findpcb:
* this is a pure ack for outstanding data.
*/
++tcpstat.tcps_predack;
+ /*
+ * "bad retransmit" recovery
+ */
+ if (tp->t_rxtshift == 1 &&
+ ticks < tp->t_badrxtwin) {
+ tp->snd_cwnd = tp->snd_cwnd_prev;
+ tp->snd_ssthresh =
+ tp->snd_ssthresh_prev;
+ tp->snd_nxt = tp->snd_max;
+ tp->t_badrxtwin = 0;
+ }
if ((to.to_flag & TOF_TS) != 0)
tcp_xmit_timer(tp,
- tcp_now - to.to_tsecr + 1);
- else if (tp->t_rtt &&
+ ticks - to.to_tsecr + 1);
+ else if (tp->t_rtttime &&
SEQ_GT(ti->ti_ack, tp->t_rtseq))
- tcp_xmit_timer(tp, tp->t_rtt);
+ tcp_xmit_timer(tp, ticks - tp->t_rtttime);
acked = ti->ti_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
@@ -611,9 +622,11 @@ findpcb:
* decide between more output or persist.
*/
if (tp->snd_una == tp->snd_max)
- tp->t_timer[TCPT_REXMT] = 0;
- else if (tp->t_timer[TCPT_PERSIST] == 0)
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ callout_stop(tp->tt_rexmt);
+ else if (!callout_active(tp->tt_persist))
+ callout_reset(tp->tt_rexmt,
+ tp->t_rxtcur,
+ tcp_timer_rexmt, tp);
sowwakeup(so);
if (so->so_snd.sb_cc)
@@ -638,7 +651,8 @@ findpcb:
sbappend(&so->so_rcv, m);
sorwakeup(so);
if (tcp_delack_enabled) {
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
} else {
tp->t_flags |= TF_ACKNOW;
tcp_output(tp);
@@ -760,6 +774,7 @@ findpcb:
taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) {
taop->tao_cc = to.to_cc;
+ tp->t_starttime = ticks;
tp->t_state = TCPS_ESTABLISHED;
/*
@@ -771,9 +786,11 @@ findpcb:
*/
if (tcp_delack_enabled && ((tiflags & TH_FIN) ||
(ti->ti_len != 0 &&
- in_localaddr(inp->inp_faddr))))
- tp->t_flags |= (TF_DELACK | TF_NEEDSYN);
- else
+ in_localaddr(inp->inp_faddr)))) {
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
+ tp->t_flags |= TF_NEEDSYN;
+ } else
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
/*
@@ -784,7 +801,8 @@ findpcb:
tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN);
tcpstat.tcps_connects++;
soisconnected(so);
- tp->t_timer[TCPT_KEEP] = tcp_keepinit;
+ callout_reset(tp->tt_keep, tcp_keepinit,
+ tcp_timer_keep, tp);
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
@@ -803,7 +821,7 @@ findpcb:
*/
tp->t_flags |= TF_ACKNOW;
tp->t_state = TCPS_SYN_RECEIVED;
- tp->t_timer[TCPT_KEEP] = tcp_keepinit;
+ callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
dropsocket = 0; /* committed to socket */
tcpstat.tcps_accepts++;
goto trimthenstep6;
@@ -903,7 +921,8 @@ findpcb:
* ACKNOW will be turned on later.
*/
if (tcp_delack_enabled && ti->ti_len != 0)
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
else
tp->t_flags |= TF_ACKNOW;
/*
@@ -912,13 +931,15 @@ findpcb:
* SYN_SENT --> ESTABLISHED
* SYN_SENT* --> FIN_WAIT_1
*/
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
tiflags &= ~TH_SYN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle,
+ tcp_timer_keep, tp);
}
} else {
/*
@@ -931,7 +952,7 @@ findpcb:
* If there was no CC option, clear cached CC value.
*/
tp->t_flags |= TF_ACKNOW;
- tp->t_timer[TCPT_REXMT] = 0;
+ callout_stop(tp->tt_rexmt);
if (to.to_flag & TOF_CC) {
if (taop->tao_cc != 0 &&
CC_GT(to.to_cc, taop->tao_cc)) {
@@ -941,13 +962,16 @@ findpcb:
* SYN-SENT* -> FIN-WAIT-1*
*/
taop->tao_cc = to.to_cc;
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] =
- tcp_keepidle;
+ callout_reset(tp->tt_keep,
+ tcp_keepidle,
+ tcp_timer_keep,
+ tp);
}
tp->t_flags |= TF_NEEDSYN;
} else
@@ -1006,7 +1030,7 @@ trimthenstep6:
if ((tiflags & TH_SYN) &&
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
if (tp->t_state == TCPS_TIME_WAIT &&
- tp->t_duration > TCPTV_MSL)
+ (ticks - tp->t_starttime) > tcp_msl)
goto dropwithreset;
if (CC_GT(to.to_cc, tp->cc_recv)) {
tp = tcp_close(tp);
@@ -1115,7 +1139,7 @@ trimthenstep6:
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
/* Check to see if ts_recent is over 24 days old. */
- if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
/*
* Invalidate ts_recent. If this segment updates
* ts_recent, the age will be reset later and ts_recent
@@ -1262,7 +1286,7 @@ trimthenstep6:
*/
if ((to.to_flag & TOF_TS) != 0 &&
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
tp->ts_recent = to.to_tsval;
}
@@ -1322,12 +1346,14 @@ trimthenstep6:
* SYN-RECEIVED -> ESTABLISHED
* SYN-RECEIVED* -> FIN-WAIT-1
*/
+ tp->t_starttime = ticks;
if (tp->t_flags & TF_NEEDFIN) {
tp->t_state = TCPS_FIN_WAIT_1;
tp->t_flags &= ~TF_NEEDFIN;
} else {
tp->t_state = TCPS_ESTABLISHED;
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ callout_reset(tp->tt_keep, tcp_keepidle,
+ tcp_timer_keep, tp);
}
/*
* If segment contains data or ACK, will call tcp_reass()
@@ -1382,7 +1408,7 @@ trimthenstep6:
* to keep a constant cwnd packets in the
* network.
*/
- if (tp->t_timer[TCPT_REXMT] == 0 ||
+ if (!callout_active(tp->tt_rexmt) ||
ti->ti_ack != tp->snd_una)
tp->t_dupacks = 0;
else if (++tp->t_dupacks == tcprexmtthresh) {
@@ -1394,8 +1420,8 @@ trimthenstep6:
if (win < 2)
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg;
- tp->t_timer[TCPT_REXMT] = 0;
- tp->t_rtt = 0;
+ callout_stop(tp->tt_rexmt);
+ tp->t_rtttime = 0;
tp->snd_nxt = ti->ti_ack;
tp->snd_cwnd = tp->t_maxseg;
(void) tcp_output(tp);
@@ -1453,6 +1479,20 @@ process_ACK:
tcpstat.tcps_rcvackbyte += acked;
/*
+ * If we just performed our first retransmit, and the ACK
+ * arrives within our recovery window, then it was a mistake
+ * to do the retransmit in the first place. Recover our
+ * original cwnd and ssthresh, and proceed to transmit where
+ * we left off.
+ */
+ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {
+ tp->snd_cwnd = tp->snd_cwnd_prev;
+ tp->snd_ssthresh = tp->snd_ssthresh_prev;
+ tp->snd_nxt = tp->snd_max;
+ tp->t_badrxtwin = 0; /* XXX probably not required */
+ }
+
+ /*
* If we have a timestamp reply, update smoothed
* round trip time. If no timestamp is present but
* transmit timer is running and timed sequence
@@ -1462,9 +1502,9 @@ process_ACK:
* Recompute the initial retransmit timer.
*/
if (to.to_flag & TOF_TS)
- tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1);
- else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
- tcp_xmit_timer(tp,tp->t_rtt);
+ tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
+ else if (tp->t_rtttime && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, ticks - tp->t_rtttime);
/*
* If all outstanding data is acked, stop retransmit
@@ -1473,10 +1513,11 @@ process_ACK:
* timer, using current (possibly backed-off) value.
*/
if (ti->ti_ack == tp->snd_max) {
- tp->t_timer[TCPT_REXMT] = 0;
+ callout_stop(tp->tt_rexmt);
needoutput = 1;
- } else if (tp->t_timer[TCPT_PERSIST] == 0)
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ } else if (!callout_active(tp->tt_persist))
+ callout_reset(tp->tt_rexmt, tp->t_rxtcur,
+ tcp_timer_rexmt, tp);
/*
* If no data (only SYN) was ACK'd,
@@ -1532,7 +1573,8 @@ process_ACK:
*/
if (so->so_state & SS_CANTRCVMORE) {
soisdisconnected(so);
- tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ callout_reset(tp->tt_2msl, tcp_maxidle,
+ tcp_timer_2msl, tp);
}
tp->t_state = TCPS_FIN_WAIT_2;
}
@@ -1550,11 +1592,14 @@ process_ACK:
tcp_canceltimers(tp);
/* Shorten TIME_WAIT [RFC-1644, p.28] */
if (tp->cc_recv != 0 &&
- tp->t_duration < TCPTV_MSL)
- tp->t_timer[TCPT_2MSL] =
- tp->t_rxtcur * TCPTV_TWTRUNC;
+ (ticks - tp->t_starttime) < tcp_msl)
+ callout_reset(tp->tt_2msl,
+ tp->t_rxtcur *
+ TCPTV_TWTRUNC,
+ tcp_timer_2msl, tp);
else
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
soisdisconnected(so);
}
break;
@@ -1578,7 +1623,8 @@ process_ACK:
* it and restart the finack timer.
*/
case TCPS_TIME_WAIT:
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
goto dropafterack;
}
}
@@ -1702,7 +1748,8 @@ dodata: /* XXX */
* more input can be expected, send ACK now.
*/
if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN))
- tp->t_flags |= TF_DELACK;
+ callout_reset(tp->tt_delack, tcp_delacktime,
+ tcp_timer_delack, tp);
else
tp->t_flags |= TF_ACKNOW;
tp->rcv_nxt++;
@@ -1714,6 +1761,8 @@ dodata: /* XXX */
* enter the CLOSE_WAIT state.
*/
case TCPS_SYN_RECEIVED:
+ tp->t_starttime = ticks;
+ /*FALLTHROUGH*/
case TCPS_ESTABLISHED:
tp->t_state = TCPS_CLOSE_WAIT;
break;
@@ -1736,14 +1785,16 @@ dodata: /* XXX */
tcp_canceltimers(tp);
/* Shorten TIME_WAIT [RFC-1644, p.28] */
if (tp->cc_recv != 0 &&
- tp->t_duration < TCPTV_MSL) {
- tp->t_timer[TCPT_2MSL] =
- tp->t_rxtcur * TCPTV_TWTRUNC;
+ (ticks - tp->t_starttime) < tcp_msl) {
+ callout_reset(tp->tt_2msl,
+ tp->t_rxtcur * TCPTV_TWTRUNC,
+ tcp_timer_2msl, tp);
/* For transaction client, force ACK now. */
tp->t_flags |= TF_ACKNOW;
}
else
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
soisdisconnected(so);
break;
@@ -1751,7 +1802,8 @@ dodata: /* XXX */
* In TIME_WAIT state restart the 2 MSL time_wait timer.
*/
case TCPS_TIME_WAIT:
- tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ callout_reset(tp->tt_2msl, 2 * tcp_msl,
+ tcp_timer_2msl, tp);
break;
}
}
@@ -1900,7 +1952,7 @@ tcp_dooptions(tp, cp, cnt, ti, to)
if (ti->ti_flags & TH_SYN) {
tp->t_flags |= TF_RCVD_TSTMP;
tp->ts_recent = to->to_tsval;
- tp->ts_recent_age = tcp_now;
+ tp->ts_recent_age = ticks;
}
break;
case TCPOPT_CC:
@@ -1988,7 +2040,7 @@ tcp_pulloutofband(so, ti, m)
static void
tcp_xmit_timer(tp, rtt)
register struct tcpcb *tp;
- short rtt;
+ int rtt;
{
register int delta;
@@ -2032,7 +2084,7 @@ tcp_xmit_timer(tp, rtt)
tp->t_srtt = rtt << TCP_RTT_SHIFT;
tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
}
- tp->t_rtt = 0;
+ tp->t_rtttime = 0;
tp->t_rxtshift = 0;
/*
@@ -2143,12 +2195,12 @@ tcp_mss(tp, offer)
* is also a minimum value; this is subject to time.
*/
if (rt->rt_rmx.rmx_locks & RTV_RTT)
- tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
- tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ tp->t_rttmin = rtt / (RTM_RTTUNIT / hz);
+ tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
tcpstat.tcps_usedrtt++;
if (rt->rt_rmx.rmx_rttvar) {
tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
tcpstat.tcps_usedrttvar++;
} else {
/* default variation is +- 1 rtt */
@@ -2156,8 +2208,8 @@ tcp_mss(tp, offer)
tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
}
TCPT_RANGESET(tp->t_rxtcur,
- ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
- tp->t_rttmin, TCPTV_REXMTMAX);
+ ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+ tp->t_rttmin, TCPTV_REXMTMAX);
}
/*
* if there's an mtu associated with the route, use it
@@ -2231,11 +2283,15 @@ tcp_mss(tp, offer)
bufsize = sb_max;
(void)sbreserve(&so->so_rcv, bufsize);
}
+
/*
- * Don't force slow-start on local network.
+ * Set the slow-start flight size depending on whether this
+ * is a local network or not.
*/
- if (!in_localaddr(inp->inp_faddr))
- tp->snd_cwnd = mss;
+ if (in_localaddr(inp->inp_faddr))
+ tp->snd_cwnd = mss * ss_fltsz_local;
+ else
+ tp->snd_cwnd = mss * ss_fltsz;
if (rt->rt_rmx.rmx_ssthresh) {
/*
diff --git a/sys/netinet/tcp_seq.h b/sys/netinet/tcp_seq.h
index 0ad0fb8..de50dd5 100644
--- a/sys/netinet/tcp_seq.h
+++ b/sys/netinet/tcp_seq.h
@@ -75,7 +75,7 @@
(tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
(tp)->iss
-#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * hz)
/* timestamp wrap-around time */
#ifdef KERNEL
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 3320b07..a6a5270 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -39,6 +39,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/callout.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
@@ -74,9 +75,11 @@ int tcp_mssdflt = TCP_MSS;
SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
&tcp_mssdflt , 0, "Default TCP Maximum Segment Size");
+#if 0
static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW,
&tcp_rttdflt , 0, "Default maximum TCP Round Trip Time");
+#endif
static int tcp_do_rfc1323 = 1;
SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW,
@@ -122,6 +125,8 @@ struct inp_tp {
char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1];
} inp_tp_u;
struct tcpcb tcb;
+ struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl;
+ struct callout inp_tp_delack;
};
#undef ALIGNMENT
#undef ALIGNM1
@@ -137,6 +142,14 @@ tcp_init()
tcp_iss = random(); /* wrong, but better than a constant */
tcp_ccgen = 1;
tcp_cleartaocache();
+
+ tcp_delacktime = TCPTV_DELACK;
+ tcp_keepinit = TCPTV_KEEP_INIT;
+ tcp_keepidle = TCPTV_KEEP_IDLE;
+ tcp_keepintvl = TCPTV_KEEPINTVL;
+ tcp_maxpersistidle = TCPTV_KEEP_IDLE;
+ tcp_msl = TCPTV_MSL;
+
LIST_INIT(&tcb);
tcbinfo.listhead = &tcb;
TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize);
@@ -150,6 +163,7 @@ tcp_init()
&tcbinfo.porthashmask);
tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets,
ZONE_INTERRUPT, 0);
+
if (max_protohdr < sizeof(struct tcpiphdr))
max_protohdr = sizeof(struct tcpiphdr);
if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
@@ -304,6 +318,13 @@ tcp_newtcpcb(inp)
tp->t_segq = NULL;
tp->t_maxseg = tp->t_maxopd = tcp_mssdflt;
+ /* Set up our timeouts. */
+ callout_init(tp->tt_rexmt = &it->inp_tp_rexmt);
+ callout_init(tp->tt_persist = &it->inp_tp_persist);
+ callout_init(tp->tt_keep = &it->inp_tp_keep);
+ callout_init(tp->tt_2msl = &it->inp_tp_2msl);
+ callout_init(tp->tt_delack = &it->inp_tp_delack);
+
if (tcp_do_rfc1323)
tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);
if (tcp_do_rfc1644)
@@ -320,6 +341,7 @@ tcp_newtcpcb(inp)
tp->t_rxtcur = TCPTV_RTOBASE;
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->t_rcvtime = ticks;
inp->inp_ip_ttl = ip_defttl;
inp->inp_ppcb = (caddr_t)tp;
return (tp); /* XXX */
@@ -367,6 +389,16 @@ tcp_close(tp)
int dosavessthresh;
/*
+ * Make sure that all of our timers are stopped before we
+ * delete the PCB.
+ */
+ callout_stop(tp->tt_rexmt);
+ callout_stop(tp->tt_persist);
+ callout_stop(tp->tt_keep);
+ callout_stop(tp->tt_2msl);
+ callout_stop(tp->tt_delack);
+
+ /*
* If we got enough samples through the srtt filter,
* save the rtt and rttvar in the routing entry.
* 'Enough' is arbitrarily defined as the 16 samples.
@@ -384,7 +416,7 @@ tcp_close(tp)
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
i = tp->t_srtt *
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
if (rt->rt_rmx.rmx_rtt && i)
/*
* filter this update to half the old & half
@@ -400,7 +432,7 @@ tcp_close(tp)
}
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
i = tp->t_rttvar *
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
if (rt->rt_rmx.rmx_rttvar && i)
rt->rt_rmx.rmx_rttvar =
(rt->rt_rmx.rmx_rttvar + i) / 2;
@@ -734,7 +766,7 @@ tcp_mtudisc(inp, errno)
tp->t_maxseg = mss;
tcpstat.tcps_mturesent++;
- tp->t_rtt = 0;
+ tp->t_rtttime = 0;
tp->snd_nxt = tp->snd_una;
tcp_output(tp);
}
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c
index de7ecc9..a4f3aad 100644
--- a/sys/netinet/tcp_timer.c
+++ b/sys/netinet/tcp_timer.c
@@ -63,116 +63,55 @@
#include <netinet/tcp_debug.h>
#endif
-int tcp_keepinit = TCPTV_KEEP_INIT;
+int tcp_keepinit;
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit,
CTLFLAG_RW, &tcp_keepinit , 0, "");
-int tcp_keepidle = TCPTV_KEEP_IDLE;
+int tcp_keepidle;
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle,
CTLFLAG_RW, &tcp_keepidle , 0, "");
-static int tcp_keepintvl = TCPTV_KEEPINTVL;
+int tcp_keepintvl;
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl,
CTLFLAG_RW, &tcp_keepintvl , 0, "");
+int tcp_delacktime;
+SYSCTL_INT(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime, CTLFLAG_RW,
+ &tcp_delacktime, 0, "Time before a delayed ACK is sent");
+
+int tcp_msl;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, msl, CTLFLAG_RW,
+ &tcp_msl, 0, "Maximum segment lifetime");
+
static int always_keepalive = 0;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW,
&always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections");
static int tcp_keepcnt = TCPTV_KEEPCNT;
/* max idle probes */
-static int tcp_maxpersistidle = TCPTV_KEEP_IDLE;
+int tcp_maxpersistidle;
/* max idle time in persist */
int tcp_maxidle;
/*
- * Fast timeout routine for processing delayed acks
- */
-void
-tcp_fasttimo()
-{
- register struct inpcb *inp;
- register struct tcpcb *tp;
- int s;
-
- if (tcp_delack_enabled) {
- s = splnet();
- for (inp = tcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
- if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
- (tp->t_flags & TF_DELACK)) {
- tp->t_flags &= ~TF_DELACK;
- tp->t_flags |= TF_ACKNOW;
- tcpstat.tcps_delack++;
- (void) tcp_output(tp);
- }
- }
- splx(s);
- }
-}
-
-/*
* Tcp protocol timeout routine called every 500 ms.
- * Updates the timers in all active tcb's and
+ * Updates timestamps used for TCP
* causes finite state machine actions if timers expire.
*/
void
tcp_slowtimo()
{
- register struct inpcb *ip, *ipnxt;
- register struct tcpcb *tp;
- register int i;
int s;
-#ifdef TCPDEBUG
- int ostate;
-#endif
s = splnet();
tcp_maxidle = tcp_keepcnt * tcp_keepintvl;
- ip = tcb.lh_first;
- if (ip == NULL) {
- splx(s);
- return;
- }
- /*
- * Search through tcb's and update active timers.
- */
- for (; ip != NULL; ip = ipnxt) {
- ipnxt = ip->inp_list.le_next;
- tp = intotcpcb(ip);
- if (tp == 0 || tp->t_state == TCPS_LISTEN)
- continue;
- for (i = 0; i < TCPT_NTIMERS; i++) {
- if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
-#ifdef TCPDEBUG
- ostate = tp->t_state;
-#endif
- tp = tcp_timers(tp, i);
- if (tp == NULL)
- goto tpgone;
-#ifdef TCPDEBUG
- if (tp->t_inpcb->inp_socket->so_options
- & SO_DEBUG)
- tcp_trace(TA_USER, ostate, tp,
- (struct tcpiphdr *)0,
- PRU_SLOWTIMO);
-#endif
- }
- }
- tp->t_idle++;
- tp->t_duration++;
- if (tp->t_rtt)
- tp->t_rtt++;
-tpgone:
- ;
- }
tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
#ifdef TCP_COMPAT_42
if ((int)tcp_iss < 0)
tcp_iss = TCP_ISSINCR; /* XXX */
#endif
- tcp_now++; /* for timestamps */
splx(s);
}
@@ -183,10 +122,10 @@ void
tcp_canceltimers(tp)
struct tcpcb *tp;
{
- register int i;
-
- for (i = 0; i < TCPT_NTIMERS; i++)
- tp->t_timer[i] = 0;
+ callout_stop(tp->tt_2msl);
+ callout_stop(tp->tt_persist);
+ callout_stop(tp->tt_keep);
+ callout_stop(tp->tt_rexmt);
}
int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
@@ -197,175 +136,300 @@ static int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
/*
* TCP timer processing.
*/
-struct tcpcb *
-tcp_timers(tp, timer)
- register struct tcpcb *tp;
- int timer;
+void
+tcp_timer_delack(xtp)
+ void *xtp;
{
- register int rexmt;
+ struct tcpcb *tp = xtp;
+ int s;
+
+ s = splnet();
+ if (callout_pending(tp->tt_delack)) {
+ splx(s);
+ return;
+ }
+ callout_deactivate(tp->tt_delack);
+
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_delack++;
+ (void) tcp_output(tp);
+ splx(s);
+}
- switch (timer) {
+void
+tcp_timer_2msl(xtp)
+ void *xtp;
+{
+ struct tcpcb *tp = xtp;
+ int s;
+#ifdef TCPDEBUG
+ int ostate;
+ ostate = tp->t_state;
+#endif
+ s = splnet();
+ if (callout_pending(tp->tt_2msl)) {
+ splx(s);
+ return;
+ }
+ callout_deactivate(tp->tt_2msl);
/*
* 2 MSL timeout in shutdown went off. If we're closed but
* still waiting for peer to close and connection has been idle
* too long, or if 2MSL time is up from TIME_WAIT, delete connection
* control block. Otherwise, check again in a bit.
*/
- case TCPT_2MSL:
- if (tp->t_state != TCPS_TIME_WAIT &&
- tp->t_idle <= tcp_maxidle)
- tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
- else
- tp = tcp_close(tp);
- break;
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ (ticks - tp->t_rcvtime) <= tcp_maxidle)
+ callout_reset(tp->tt_2msl, tcp_keepintvl,
+ tcp_timer_2msl, tp);
+ else
+ tp = tcp_close(tp);
+
+#ifdef TCPDEBUG
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
+ PRU_SLOWTIMO);
+#endif
+ splx(s);
+}
+void
+tcp_timer_keep(xtp)
+ void *xtp;
+{
+ struct tcpcb *tp = xtp;
+ int s;
+#ifdef TCPDEBUG
+ int ostate;
+
+ ostate = tp->t_state;
+#endif
+ s = splnet();
+ if (callout_pending(tp->tt_keep)) {
+ splx(s);
+ return;
+ }
+ callout_deactivate(tp->tt_keep);
/*
- * Retransmission timer went off. Message has not
- * been acked within retransmit interval. Back off
- * to a longer retransmit interval and retransmit one segment.
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
*/
- case TCPT_REXMT:
- if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
- tp->t_rxtshift = TCP_MAXRXTSHIFT;
- tcpstat.tcps_timeoutdrop++;
- tp = tcp_drop(tp, tp->t_softerror ?
- tp->t_softerror : ETIMEDOUT);
- break;
- }
- tcpstat.tcps_rexmttimeo++;
- rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
- TCPT_RANGESET(tp->t_rxtcur, rexmt,
- tp->t_rttmin, TCPTV_REXMTMAX);
- tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
- /*
- * If losing, let the lower level know and try for
- * a better route. Also, if we backed off this far,
- * our srtt estimate is probably bogus. Clobber it
- * so we'll take the next rtt measurement as our srtt;
- * move the current srtt into rttvar to keep the current
- * retransmit times until then.
- */
- if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
- in_losing(tp->t_inpcb);
- tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
- tp->t_srtt = 0;
- }
- tp->snd_nxt = tp->snd_una;
- /*
- * Force a segment to be sent.
- */
- tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_keeptimeo++;
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+ if ((always_keepalive ||
+ tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) &&
+ tp->t_state <= TCPS_CLOSING) {
+ if ((ticks - tp->t_rcvtime) >= tcp_keepidle + tcp_maxidle)
+ goto dropit;
/*
- * If timing a segment in this window, stop the timer.
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
*/
- tp->t_rtt = 0;
+ tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
/*
- * Close the congestion window down to one segment
- * (we'll open it by one segment for each ack we get).
- * Since we probably have a window's worth of unacked
- * data accumulated, this "slow start" keeps us from
- * dumping all that data as back-to-back packets (which
- * might overwhelm an intermediate gateway).
- *
- * There are two phases to the opening: Initially we
- * open by one mss on each ack. This makes the window
- * size increase exponentially with time. If the
- * window is larger than the path can handle, this
- * exponential growth results in dropped packet(s)
- * almost immediately. To get more time between
- * drops but still "push" the network to take advantage
- * of improving conditions, we switch from exponential
- * to linear window opening at some threshhold size.
- * For a threshhold, we use half the current window
- * size, truncated to a multiple of the mss.
- *
- * (the minimum cwnd that will give us exponential
- * growth is 2 mss. We don't allow the threshhold
- * to go below this.)
+ * The keepalive packet must have nonzero length
+ * to get a 4.2 host to respond.
*/
- {
- u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
- if (win < 2)
- win = 2;
- tp->snd_cwnd = tp->t_maxseg;
- tp->snd_ssthresh = win * tp->t_maxseg;
- tp->t_dupacks = 0;
- }
- (void) tcp_output(tp);
- break;
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+ callout_reset(tp->tt_keep, tcp_keepintvl, tcp_timer_keep, tp);
+ } else
+ callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
+
+#ifdef TCPDEBUG
+ if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
+ PRU_SLOWTIMO);
+#endif
+ splx(s);
+ return;
+
+dropit:
+ tcpstat.tcps_keepdrops++;
+ tp = tcp_drop(tp, ETIMEDOUT);
+
+#ifdef TCPDEBUG
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
+ PRU_SLOWTIMO);
+#endif
+ splx(s);
+}
+
+void
+tcp_timer_persist(xtp)
+ void *xtp;
+{
+ struct tcpcb *tp = xtp;
+ int s;
+#ifdef TCPDEBUG
+ int ostate;
+ ostate = tp->t_state;
+#endif
+ s = splnet();
+ if (callout_pending(tp->tt_persist)) {
+ splx(s);
+ return;
+ }
+ callout_deactivate(tp->tt_persist);
/*
* Persistance timer into zero window.
* Force a byte to be output, if possible.
*/
- case TCPT_PERSIST:
- tcpstat.tcps_persisttimeo++;
+ tcpstat.tcps_persisttimeo++;
+ /*
+ * Hack: if the peer is dead/unreachable, we do not
+ * time out if the window is closed. After a full
+ * backoff, drop the connection if the idle time
+ * (no responses to probes) reaches the maximum
+ * backoff that we would use if retransmitting.
+ */
+ if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
+ ((ticks - tp->t_rcvtime) >= tcp_maxpersistidle ||
+ (ticks - tp->t_rcvtime) >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
+ tcpstat.tcps_persistdrop++;
+ tp = tcp_drop(tp, ETIMEDOUT);
+ goto out;
+ }
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+
+out:
+#ifdef TCPDEBUG
+ if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
+ PRU_SLOWTIMO);
+#endif
+ splx(s);
+}
+
+void
+tcp_timer_rexmt(xtp)
+ void *xtp;
+{
+ struct tcpcb *tp = xtp;
+ int s;
+ int rexmt;
+#ifdef TCPDEBUG
+ int ostate;
+
+ ostate = tp->t_state;
+#endif
+ s = splnet();
+ if (callout_pending(tp->tt_rexmt)) {
+ splx(s);
+ return;
+ }
+ callout_deactivate(tp->tt_rexmt);
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tcpstat.tcps_timeoutdrop++;
+ tp = tcp_drop(tp, tp->t_softerror ?
+ tp->t_softerror : ETIMEDOUT);
+ goto out;
+ }
+ if (tp->t_rxtshift == 1) {
/*
- * Hack: if the peer is dead/unreachable, we do not
- * time out if the window is closed. After a full
- * backoff, drop the connection if the idle time
- * (no responses to probes) reaches the maximum
- * backoff that we would use if retransmitting.
+ * first retransmit; record ssthresh and cwnd so they can
+ * be recovered if this turns out to be a "bad" retransmit.
+ * A retransmit is considered "bad" if an ACK for this
+ * segment is received within RTT/2 interval; the assumption
+ * here is that the ACK was already in flight. See
+ * "On Estimating End-to-End Network Path Properties" by
+ * Allman and Paxson for more details.
*/
- if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
- (tp->t_idle >= tcp_maxpersistidle ||
- tp->t_idle >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
- tcpstat.tcps_persistdrop++;
- tp = tcp_drop(tp, ETIMEDOUT);
- break;
- }
- tcp_setpersist(tp);
- tp->t_force = 1;
- (void) tcp_output(tp);
- tp->t_force = 0;
- break;
-
+ tp->snd_cwnd_prev = tp->snd_cwnd;
+ tp->snd_ssthresh_prev = tp->snd_ssthresh;
+ tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1));
+ }
+ tcpstat.tcps_rexmttimeo++;
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ tp->t_rttmin, TCPTV_REXMTMAX);
/*
- * Keep-alive timer went off; send something
- * or drop connection if idle for too long.
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
*/
- case TCPT_KEEP:
- tcpstat.tcps_keeptimeo++;
- if (tp->t_state < TCPS_ESTABLISHED)
- goto dropit;
- if ((always_keepalive ||
- tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) &&
- tp->t_state <= TCPS_CLOSING) {
- if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
- goto dropit;
- /*
- * Send a packet designed to force a response
- * if the peer is up and reachable:
- * either an ACK if the connection is still alive,
- * or an RST if the peer has closed the connection
- * due to timeout or reboot.
- * Using sequence number tp->snd_una-1
- * causes the transmitted zero-length segment
- * to lie outside the receive window;
- * by the protocol spec, this requires the
- * correspondent TCP to respond.
- */
- tcpstat.tcps_keepprobe++;
-#ifdef TCP_COMPAT_42
- /*
- * The keepalive packet must have nonzero length
- * to get a 4.2 host to respond.
- */
- tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
- tp->rcv_nxt - 1, tp->snd_una - 1, 0);
-#else
- tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
- tp->rcv_nxt, tp->snd_una - 1, 0);
-#endif
- tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
- } else
- tp->t_timer[TCPT_KEEP] = tcp_keepidle;
- break;
- dropit:
- tcpstat.tcps_keepdrops++;
- tp = tcp_drop(tp, ETIMEDOUT);
- break;
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+ in_losing(tp->t_inpcb);
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
}
- return (tp);
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * Force a segment to be sent.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtttime = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshhold size.
+ * For a threshhold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshhold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+
+out:
+#ifdef TCPDEBUG
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
+ PRU_SLOWTIMO);
+#endif
+ splx(s);
}
diff --git a/sys/netinet/tcp_timer.h b/sys/netinet/tcp_timer.h
index 3c0f39e..c727559 100644
--- a/sys/netinet/tcp_timer.h
+++ b/sys/netinet/tcp_timer.h
@@ -38,17 +38,6 @@
#define _NETINET_TCP_TIMER_H_
/*
- * Definitions of the TCP timers. These timers are counted
- * down PR_SLOWHZ times a second.
- */
-#define TCPT_NTIMERS 4
-
-#define TCPT_REXMT 0 /* retransmit */
-#define TCPT_PERSIST 1 /* retransmit persistence */
-#define TCPT_KEEP 2 /* keep alive */
-#define TCPT_2MSL 3 /* 2*msl quiet time timer */
-
-/*
* The TCPT_REXMT timer is used to force retransmissions.
* The TCP has the TCPT_REXMT timer set whenever segments
* have been sent for which ACKs are expected but not yet
@@ -87,22 +76,22 @@
/*
* Time constants.
*/
-#define TCPTV_MSL ( 30*PR_SLOWHZ) /* max seg lifetime (hah!) */
+#define TCPTV_MSL ( 30*hz) /* max seg lifetime (hah!) */
#define TCPTV_SRTTBASE 0 /* base roundtrip time;
if 0, no idea yet */
-#define TCPTV_RTOBASE ( 3*PR_SLOWHZ) /* assumed RTO if no info */
-#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+#define TCPTV_RTOBASE ( 3*hz) /* assumed RTO if no info */
+#define TCPTV_SRTTDFLT ( 3*hz) /* assumed RTT if no info */
-#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
-#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+#define TCPTV_PERSMIN ( 5*hz) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*hz) /* maximum persist interval */
-#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
-#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
-#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEP_INIT ( 75*hz) /* initial connect keepalive */
+#define TCPTV_KEEP_IDLE (120*60*hz) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*hz) /* default probe interval */
#define TCPTV_KEEPCNT 8 /* max probes before drop */
-#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
-#define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) /* max allowable REXMT value */
+#define TCPTV_MIN ( 1*hz) /* minimum allowable value */
+#define TCPTV_REXMTMAX ( 64*hz) /* max allowable REXMT value */
#define TCPTV_TWTRUNC 8 /* RTO factor to truncate TW */
@@ -110,6 +99,8 @@
#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+#define TCPTV_DELACK (hz / PR_FASTHZ) /* 200mS timeout */
+
#ifdef TCPTIMERS
static char *tcptimers[] =
{ "REXMT", "PERSIST", "KEEP", "2MSL" };
@@ -118,20 +109,39 @@ static char *tcptimers[] =
/*
* Force a time value to be in a certain range.
*/
-#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) do { \
(tv) = (value); \
if ((u_long)(tv) < (u_long)(tvmin)) \
(tv) = (tvmin); \
else if ((u_long)(tv) > (u_long)(tvmax)) \
(tv) = (tvmax); \
-}
+} while(0)
+
+/*
+ * Convert slow-timeout ticks to timer ticks. We don't really want to do
+ * this as it is rather expensive, so this is only a transitional stage
+ * until we are able to update all the code which counts timer ticks.
+ */
+#define TCPT_TICKS(stt) ((stt) * hz / PR_SLOWHZ)
+#define TCPT_SLOWHZ(tt) (((tt) * PR_SLOWHZ) / hz)
#ifdef KERNEL
extern int tcp_keepinit; /* time to establish connection */
extern int tcp_keepidle; /* time before keepalive probes begin */
+extern int tcp_keepintvl; /* time between keepalive probes */
extern int tcp_maxidle; /* time to drop after starting probes */
+extern int tcp_delacktime; /* time before sending a delayed ACK */
+extern int tcp_maxpersistidle;
+extern int tcp_msl;
extern int tcp_ttl; /* time to live for TCP segs */
extern int tcp_backoff[];
-#endif
-#endif
+void tcp_timer_2msl __P((void *xtp));
+void tcp_timer_keep __P((void *xtp));
+void tcp_timer_persist __P((void *xtp));
+void tcp_timer_rexmt __P((void *xtp));
+void tcp_timer_delack __P((void *xtp));
+
+#endif /* KERNEL */
+
+#endif /* !_NETINET_TCP_TIMER_H_ */
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index 3320b07..a6a5270 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -39,6 +39,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/callout.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
@@ -74,9 +75,11 @@ int tcp_mssdflt = TCP_MSS;
SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
&tcp_mssdflt , 0, "Default TCP Maximum Segment Size");
+#if 0
static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW,
&tcp_rttdflt , 0, "Default maximum TCP Round Trip Time");
+#endif
static int tcp_do_rfc1323 = 1;
SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW,
@@ -122,6 +125,8 @@ struct inp_tp {
char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1];
} inp_tp_u;
struct tcpcb tcb;
+ struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl;
+ struct callout inp_tp_delack;
};
#undef ALIGNMENT
#undef ALIGNM1
@@ -137,6 +142,14 @@ tcp_init()
tcp_iss = random(); /* wrong, but better than a constant */
tcp_ccgen = 1;
tcp_cleartaocache();
+
+ tcp_delacktime = TCPTV_DELACK;
+ tcp_keepinit = TCPTV_KEEP_INIT;
+ tcp_keepidle = TCPTV_KEEP_IDLE;
+ tcp_keepintvl = TCPTV_KEEPINTVL;
+ tcp_maxpersistidle = TCPTV_KEEP_IDLE;
+ tcp_msl = TCPTV_MSL;
+
LIST_INIT(&tcb);
tcbinfo.listhead = &tcb;
TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize);
@@ -150,6 +163,7 @@ tcp_init()
&tcbinfo.porthashmask);
tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets,
ZONE_INTERRUPT, 0);
+
if (max_protohdr < sizeof(struct tcpiphdr))
max_protohdr = sizeof(struct tcpiphdr);
if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
@@ -304,6 +318,13 @@ tcp_newtcpcb(inp)
tp->t_segq = NULL;
tp->t_maxseg = tp->t_maxopd = tcp_mssdflt;
+ /* Set up our timeouts. */
+ callout_init(tp->tt_rexmt = &it->inp_tp_rexmt);
+ callout_init(tp->tt_persist = &it->inp_tp_persist);
+ callout_init(tp->tt_keep = &it->inp_tp_keep);
+ callout_init(tp->tt_2msl = &it->inp_tp_2msl);
+ callout_init(tp->tt_delack = &it->inp_tp_delack);
+
if (tcp_do_rfc1323)
tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);
if (tcp_do_rfc1644)
@@ -320,6 +341,7 @@ tcp_newtcpcb(inp)
tp->t_rxtcur = TCPTV_RTOBASE;
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->t_rcvtime = ticks;
inp->inp_ip_ttl = ip_defttl;
inp->inp_ppcb = (caddr_t)tp;
return (tp); /* XXX */
@@ -367,6 +389,16 @@ tcp_close(tp)
int dosavessthresh;
/*
+ * Make sure that all of our timers are stopped before we
+ * delete the PCB.
+ */
+ callout_stop(tp->tt_rexmt);
+ callout_stop(tp->tt_persist);
+ callout_stop(tp->tt_keep);
+ callout_stop(tp->tt_2msl);
+ callout_stop(tp->tt_delack);
+
+ /*
* If we got enough samples through the srtt filter,
* save the rtt and rttvar in the routing entry.
* 'Enough' is arbitrarily defined as the 16 samples.
@@ -384,7 +416,7 @@ tcp_close(tp)
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
i = tp->t_srtt *
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
if (rt->rt_rmx.rmx_rtt && i)
/*
* filter this update to half the old & half
@@ -400,7 +432,7 @@ tcp_close(tp)
}
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
i = tp->t_rttvar *
- (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
if (rt->rt_rmx.rmx_rttvar && i)
rt->rt_rmx.rmx_rttvar =
(rt->rt_rmx.rmx_rttvar + i) / 2;
@@ -734,7 +766,7 @@ tcp_mtudisc(inp, errno)
tp->t_maxseg = mss;
tcpstat.tcps_mturesent++;
- tp->t_rtt = 0;
+ tp->t_rtttime = 0;
tp->snd_nxt = tp->snd_una;
tcp_output(tp);
}
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 231da99..e50dcb0 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -537,7 +537,7 @@ tcp_connect(tp, nam, p)
if (oinp) {
if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
otp->t_state == TCPS_TIME_WAIT &&
- otp->t_duration < TCPTV_MSL &&
+ (ticks - otp->t_starttime) < tcp_msl &&
(otp->t_flags & TF_RCVD_CC))
otp = tcp_close(otp);
else
@@ -563,7 +563,7 @@ tcp_connect(tp, nam, p)
soisconnecting(so);
tcpstat.tcps_connattempt++;
tp->t_state = TCPS_SYN_SENT;
- tp->t_timer[TCPT_KEEP] = tcp_keepinit;
+ callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
tcp_sendseqinit(tp);
@@ -810,7 +810,8 @@ tcp_usrclosed(tp)
soisdisconnected(tp->t_inpcb->inp_socket);
/* To prevent the connection hanging in FIN_WAIT_2 forever. */
if (tp->t_state == TCPS_FIN_WAIT_2)
- tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ callout_reset(tp->tt_2msl, tcp_maxidle,
+ tcp_timer_2msl, tp);
}
return (tp);
}
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index eadd4f5..8d7c597 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -49,7 +49,11 @@ struct tcpcb {
int t_dupacks; /* consecutive dup acks recd */
struct tcpiphdr *t_template; /* skeletal packet for transmit */
- int t_timer[TCPT_NTIMERS]; /* tcp timers */
+ struct callout *tt_rexmt; /* retransmit timer */
+ struct callout *tt_persist; /* retransmit persistence */
+ struct callout *tt_keep; /* keepalive */
+ struct callout *tt_2msl; /* 2*msl TIME_WAIT timer */
+ struct callout *tt_delack; /* delayed ACK timer */
struct inpcb *t_inpcb; /* back pointer to internet pcb */
int t_state; /* state of this connection */
@@ -98,12 +102,12 @@ struct tcpcb {
*/
u_int t_maxopd; /* mss plus options */
- u_int t_idle; /* inactivity time */
- u_long t_duration; /* connection duration */
- int t_rtt; /* round trip time */
+ u_long t_rcvtime; /* inactivity time */
+ u_long t_starttime; /* time connection was established */
+ int t_rtttime; /* round trip time */
tcp_seq t_rtseq; /* sequence number being timed */
- int t_rxtcur; /* current retransmit value */
+ int t_rxtcur; /* current retransmit value (ticks) */
u_int t_maxseg; /* maximum segment size */
int t_srtt; /* smoothed round-trip time */
int t_rttvar; /* variance in round-trip time */
@@ -131,6 +135,10 @@ struct tcpcb {
/* RFC 1644 variables */
tcp_cc cc_send; /* send connection count */
tcp_cc cc_recv; /* receive connection count */
+/* experimental */
+ u_long snd_cwnd_prev; /* cwnd prior to retransmit */
+ u_long snd_ssthresh_prev; /* ssthresh prior to retransmit */
+ u_long t_badrxtwin; /* window for retransmit recovery */
};
/*
@@ -305,7 +313,8 @@ struct xtcpcb {
#define TCPCTL_RECVSPACE 9 /* receive buffer space */
#define TCPCTL_KEEPINIT 10 /* receive buffer space */
#define TCPCTL_PCBLIST 11 /* list of all outstanding PCBs */
-#define TCPCTL_MAXID 12
+#define TCPCTL_DELACKTIME 12 /* time before sending delayed ACK */
+#define TCPCTL_MAXID 13
#define TCPCTL_NAMES { \
{ 0, 0 }, \
@@ -320,6 +329,7 @@ struct xtcpcb {
{ "recvspace", CTLTYPE_INT }, \
{ "keepinit", CTLTYPE_INT }, \
{ "pcblist", CTLTYPE_STRUCT }, \
+ { "delacktime", CTLTYPE_INT }, \
}
@@ -332,8 +342,9 @@ extern struct inpcbhead tcb; /* head of queue of active tcpcb's */
extern struct inpcbinfo tcbinfo;
extern struct tcpstat tcpstat; /* tcp statistics */
extern int tcp_mssdflt; /* XXX */
-extern u_long tcp_now; /* for RFC 1323 timestamps */
extern int tcp_delack_enabled;
+extern int ss_fltsz;
+extern int ss_fltsz_local;
void tcp_canceltimers __P((struct tcpcb *));
struct tcpcb *
diff --git a/sys/sys/callout.h b/sys/sys/callout.h
index 971ac9f..a3cfcc2 100644
--- a/sys/sys/callout.h
+++ b/sys/sys/callout.h
@@ -59,8 +59,8 @@ struct callout {
};
#define CALLOUT_LOCAL_ALLOC 0x0001 /* was allocated from callfree */
-#define CALLOUT_PENDING 0x0002 /* callout is currently active */
-#define CALLOUT_FIRED 0x0004 /* callout has been fired */
+#define CALLOUT_ACTIVE 0x0002 /* callout is currently active */
+#define CALLOUT_PENDING 0x0004 /* callout is waiting for timeout */
struct callout_handle {
struct callout *callout;
@@ -73,10 +73,10 @@ extern int ncallout;
extern struct callout_tailq *callwheel;
extern int callwheelsize, callwheelbits, callwheelmask, softticks;
-#define callout_fired(c) ((c)->c_flags & CALLOUT_FIRED)
+#define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE)
+#define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE)
void callout_init __P((struct callout *));
-#define callout_pending(c) (((c)->c_flags & CALLOUT_PENDING) ? \
- ((c)->c_time - ticks) : 0)
+#define callout_pending(c) ((c)->c_flags & CALLOUT_PENDING)
void callout_reset __P((struct callout *, int, void (*)(void *), void *));
void callout_stop __P((struct callout *));
OpenPOWER on IntegriCloud