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
This commit is contained in:
Jonathan Lemon 1999-08-30 21:17:07 +00:00
parent e0eed6c8af
commit 9b8b58e033
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=50673
13 changed files with 706 additions and 425 deletions

View File

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

View File

@ -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,

View File

@ -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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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);
@ -1452,6 +1478,20 @@ tcp_input(m, iphlen)
tcpstat.tcps_rcvackpack++;
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
@ -1462,9 +1502,9 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
* 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) {
/*

View File

@ -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 @@ tcp_output(tp)
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 @@ tcp_output(tp)
*/
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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
/* 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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
* 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 @@ tcp_output(tp)
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++;
}

View File

@ -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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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);
@ -1452,6 +1478,20 @@ tcp_input(m, iphlen)
tcpstat.tcps_rcvackpack++;
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
@ -1462,9 +1502,9 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
*/
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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
* 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) {
/*

View File

@ -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

View File

@ -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 */
@ -366,6 +388,16 @@ tcp_close(tp)
register struct rtentry *rt;
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.
@ -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);
}

View File

@ -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;
switch (timer) {
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);
}
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);
/*
* Keep-alive timer went off; send something
* or drop connection if idle for too long.
*/
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;
/*
* 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
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.
*/
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.
*/
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 (++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) {
/*
* 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.
* 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 / 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;
/*
* If timing a segment in this window, stop the timer.
*/
tp->t_rtt = 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.)
*/
{
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);
/*
* 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;
/*
* 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);
break;
/*
* Persistance timer into zero window.
* Force a byte to be output, if possible.
*/
case TCPT_PERSIST:
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 &&
(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;
/*
* Keep-alive timer went off; send something
* or drop connection if idle for too long.
*/
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;
}
return (tp);
(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);
}

View File

@ -37,17 +37,6 @@
#ifndef _NETINET_TCP_TIMER_H_
#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
@ -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_ */

View File

@ -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 */
@ -366,6 +388,16 @@ tcp_close(tp)
register struct rtentry *rt;
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.
@ -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);
}

View File

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

View File

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

View File

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