From 628be0515eace470ecc5e0055dbc9cee71e2adb9 Mon Sep 17 00:00:00 2001 From: jlemon Date: Mon, 30 Aug 1999 21:17:07 +0000 Subject: 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 --- sys/kern/kern_timeout.c | 18 +- sys/netinet/in_proto.c | 2 +- sys/netinet/tcp_input.c | 172 +++++++++------ sys/netinet/tcp_output.c | 73 ++++--- sys/netinet/tcp_reass.c | 172 +++++++++------ sys/netinet/tcp_seq.h | 2 +- sys/netinet/tcp_subr.c | 38 +++- sys/netinet/tcp_timer.c | 510 +++++++++++++++++++++++++-------------------- sys/netinet/tcp_timer.h | 60 +++--- sys/netinet/tcp_timewait.c | 38 +++- sys/netinet/tcp_usrreq.c | 7 +- sys/netinet/tcp_var.h | 25 ++- sys/sys/callout.h | 10 +- 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() * - * defines two convenience macros: + * 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 #include +#include #include #include #include @@ -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 #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 #include +#include #include #include #include @@ -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 *)); -- cgit v1.1