+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tp_timer.c 8.1 (Berkeley) 6/10/93
+ */
+
/***********************************************************
Copyright IBM Corporation 1987
*
* $Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $
* $Source: /usr/argo/sys/netiso/RCS/tp_timer.c,v $
- * @(#)tp_timer.c 7.3 (Berkeley) 8/29/89 *
- *
- * Contains all the timer code.
- * There are two sources of calls to these routines:
- * the clock, and tp.trans. (ok, and tp_pcb.c calls it at init time)
*
- * Timers come in two flavors - those that generally get
- * cancelled (tp_ctimeout, tp_cuntimeout)
- * and those that either usually expire (tp_etimeout,
- * tp_euntimeout, tp_slowtimo) or may require more than one instance
- * of the timer active at a time.
- *
- * The C timers are stored in the tp_ref structure. Their "going off"
- * is manifested by a driver event of the TM_xxx form.
- *
- * The E timers are handled like the generic kernel callouts.
- * Their "going off" is manifested by a function call w/ 3 arguments.
*/
-#ifndef lint
-static char *rcsid = "$Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $";
-#endif lint
-
-#include "param.h"
-#include "types.h"
-#include "time.h"
-#include "malloc.h"
-
-#include "tp_param.h"
-#include "tp_timer.h"
-#include "tp_stat.h"
-#include "tp_pcb.h"
-#include "tp_tpdu.h"
-#include "argo_debug.h"
-#include "tp_trace.h"
-#include "tp_seq.h"
-
-static struct Ecallout *TP_callfree;
-static struct Ecallout TP_callout[N_TPREF*2];
-
-extern int tp_maxrefopen; /* highest ref # of an open tp connection */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+
+#include <netiso/argo_debug.h>
+#include <netiso/tp_param.h>
+#include <netiso/tp_timer.h>
+#include <netiso/tp_stat.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_tpdu.h>
+#include <netiso/tp_trace.h>
+#include <netiso/tp_seq.h>
+
+struct tp_ref *tp_ref;
+int tp_rttdiv, tp_rttadd, N_TPREF = 127;
+struct tp_refinfo tp_refinfo;
+struct tp_pcb *tp_ftimeolist = (struct tp_pcb *)&tp_ftimeolist;
/*
* CALLED FROM:
void
tp_timerinit()
{
- register int i;
+ register int s;
/*
- * Initialize callouts
+ * Initialize storage
*/
- TP_callfree = TP_callout;
- for (i = 1; i < N_TPREF*2; i++)
- TP_callout[i-1].c_next = &TP_callout[i];
-
- bzero( (caddr_t)tp_ref, N_TPREF * sizeof(struct tp_ref) );
-
- /* hate to do this but we really don't want zero to be a legit ref */
- tp_maxrefopen = 1;
- tp_ref[0].tpr_state = REF_FROZEN; /* white lie -- no ref timer, don't
- * want this one to be allocated- ever
- * unless, of course, you make refs and address instead of an
- * index - then 0 can be allocated
- */
-
+ if (tp_refinfo.tpr_base)
+ return;
+ tp_refinfo.tpr_size = N_TPREF + 1; /* Need to start somewhere */
+ s = sizeof(*tp_ref) * tp_refinfo.tpr_size;
+ if ((tp_ref = (struct tp_ref *) malloc(s, M_PCB, M_NOWAIT)) == 0)
+ panic("tp_timerinit");
+ bzero((caddr_t)tp_ref, (unsigned) s);
+ tp_refinfo.tpr_base = tp_ref;
+ tp_rttdiv = hz / PR_SLOWHZ;
+ tp_rttadd = (2 * tp_rttdiv) - 1;
}
-
+#ifdef TP_DEBUG_TIMERS
/********************** e timers *************************/
-/*
- * CALLED FROM:
- * tp_slowtimo() every 1/2 second, for each open reference
- * FUNCTION and ARGUMENTS:
- * (refp) indicates a reference structure that is in use.
- * This ref structure may contain active E-type timers.
- * Update the timers and if any expire, create an event and
- * call the driver.
- */
-static void
-tp_Eclock(refp)
- struct tp_ref *refp; /* the reference structure */
-{
- register struct Ecallout *p1; /* to drift through the list of callouts */
- struct tp_event E; /* event to pass to tp_driver() */
- int tp_driver(); /* drives the FSM */
-
- /*
- * Update real-time timeout queue.
- * At front of queue are some number of events which are ``due''.
- * The time to these is <= 0 and if negative represents the
- * number of ticks which have passed since it was supposed to happen.
- * The rest of the q elements (times > 0) are events yet to happen,
- * where the time for each is given as a delta from the previous.
- * Decrementing just the first of these serves to decrement the time
- * to all events.
- *
- * This version, which calls the driver directly, doesn't pass
- * along the ticks - may want to add the ticks if there's any use
- * for them.
- */
- IncStat(ts_Eticks);
- p1 = refp->tpr_calltodo.c_next;
- while (p1) {
- if (--p1->c_time > 0)
- break;
- if (p1->c_time == 0)
- break;
- p1 = p1->c_next;
- }
-
- for (;;) {
- struct tp_pcb *tpcb;
- if ((p1 = refp->tpr_calltodo.c_next) == 0 || p1->c_time > 0) {
- break;
- }
- refp->tpr_calltodo.c_next = p1->c_next;
- p1->c_next = TP_callfree;
-
-#ifndef lint
- E.ev_number = p1->c_func;
- E.ATTR(TM_data_retrans).e_low = (SeqNum) p1->c_arg1;
- E.ATTR(TM_data_retrans).e_high = (SeqNum) p1->c_arg2;
- E.ATTR(TM_data_retrans).e_retrans = p1->c_arg3;
-#endif lint
- IFDEBUG(D_TIMER)
- printf("E expired! event 0x%x (0x%x,0x%x), pcb 0x%x ref %d\n",
- p1->c_func, p1->c_arg1, p1->c_arg2, refp->tpr_pcb,
- refp-tp_ref);
- ENDDEBUG
-
- TP_callfree = p1;
- IncStat(ts_Eexpired);
- (void) tp_driver( tpcb = refp->tpr_pcb, &E);
- if (p1->c_func == TM_reference && tpcb->tp_state == TP_CLOSED)
- free((caddr_t)tpcb, M_PCB); /* XXX wart; where else to do it? */
- }
-}
-
/*
* CALLED FROM:
* tp.trans all over
* FUNCTION and ARGUMENTS:
- * Set an E type timer. (refp) is the ref structure.
- * Causes fun(arg1,arg2,arg3) to be called after time t.
+ * Set an E type timer.
*/
void
-tp_etimeout(refp, fun, arg1, arg2, arg3, ticks)
- struct tp_ref *refp;
- int fun; /* function to be called */
- u_int arg1, arg2;
- int arg3;
- register int ticks;
+tp_etimeout(tpcb, fun, ticks)
+ register struct tp_pcb *tpcb;
+ int fun; /* function to be called */
+ int ticks;
{
- register struct Ecallout *p1, *p2, *pnew;
- /* p1 and p2 drift through the list of timeout callout structures,
- * pnew points to the newly created callout structure
- */
+ register u_int *callp;
IFDEBUG(D_TIMER)
- printf("etimeout pcb 0x%x state 0x%x\n", refp->tpr_pcb,
- refp->tpr_pcb->tp_state);
+ printf("etimeout pcb 0x%x state 0x%x\n", tpcb, tpcb->tp_state);
ENDDEBUG
IFTRACE(D_TIMER)
- tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", refp-tp_ref,
- refp->tpr_state, ticks, tp_stat.ts_Eticks);
+ tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", tpcb->tp_lref,
+ tpcb->tp_state, ticks, tp_stat.ts_Eticks);
ENDTRACE
-
+ if (tpcb == 0)
+ return;
IncStat(ts_Eset);
if (ticks == 0)
ticks = 1;
- pnew = TP_callfree;
- if (pnew == (struct Ecallout *)0)
- panic("tp timeout table overflow");
- TP_callfree = pnew->c_next;
- pnew->c_arg1 = arg1;
- pnew->c_arg2 = arg2;
- pnew->c_arg3 = arg3;
- pnew->c_func = fun;
- for (p1 = &(refp->tpr_calltodo);
- (p2 = p1->c_next) && p2->c_time < ticks; p1 = p2)
- if (p2->c_time > 0)
- ticks -= p2->c_time;
- p1->c_next = pnew;
- pnew->c_next = p2;
- pnew->c_time = ticks;
- if (p2)
- p2->c_time -= ticks;
+ callp = tpcb->tp_timer + fun;
+ if (*callp == 0 || *callp > ticks)
+ *callp = ticks;
}
/*
* Cancel all occurrences of E-timer function (fun) for reference (refp)
*/
void
-tp_euntimeout(refp, fun)
- struct tp_ref *refp;
- int fun;
-{
- register struct Ecallout *p1, *p2; /* ptrs to drift through the list */
-
- IFTRACE(D_TIMER)
- tptrace(TPPTmisc, "tp_euntimeout ref", refp-tp_ref, 0, 0, 0);
- ENDTRACE
-
- p1 = &refp->tpr_calltodo;
- while ( (p2 = p1->c_next) != 0) {
- if (p2->c_func == fun) {
- if (p2->c_next && p2->c_time > 0)
- p2->c_next->c_time += p2->c_time;
- p1->c_next = p2->c_next;
- p2->c_next = TP_callfree;
- TP_callfree = p2;
- IncStat(ts_Ecan_act);
- continue;
- }
- p1 = p2;
- }
-}
-
-/*
- * CALLED FROM:
- * tp.trans, when an incoming ACK causes things to be dropped
- * from the retransmission queue, and we want their associated
- * timers to be cancelled.
- * FUNCTION and ARGUMENTS:
- * cancel all occurrences of function (fun) where (arg2) < (seq)
- */
-void
-tp_euntimeout_lss(refp, fun, seq)
- struct tp_ref *refp;
+tp_euntimeout(tpcb, fun)
+ register struct tp_pcb *tpcb;
int fun;
- SeqNum seq;
{
- register struct Ecallout *p1, *p2;
-
IFTRACE(D_TIMER)
- tptrace(TPPTmisc, "tp_euntimeoutLSS ref", refp-tp_ref, seq, 0, 0);
+ tptrace(TPPTmisc, "tp_euntimeout ref", tpcb->tp_lref, 0, 0, 0);
ENDTRACE
- p1 = &refp->tpr_calltodo;
- while ( (p2 = p1->c_next) != 0) {
- if ((p2->c_func == fun) && SEQ_LT(refp->tpr_pcb, p2->c_arg2, seq)) {
- if (p2->c_next && p2->c_time > 0)
- p2->c_next->c_time += p2->c_time;
- p1->c_next = p2->c_next;
- p2->c_next = TP_callfree;
- TP_callfree = p2;
- IncStat(ts_Ecan_act);
- continue;
- }
- p1 = p2;
- }
+ if (tpcb)
+ tpcb->tp_timer[fun] = 0;
}
/**************** c timers **********************
* are typically cancelled so it's faster not to
* mess with the chains
*/
-
+#endif
/*
* CALLED FROM:
* the clock, every 500 ms
* Look for open references with active timers.
* If they exist, call the appropriate timer routines to update
* the timers and possibly generate events.
- * (The E timers are done in other procedures; the C timers are
- * updated here, and events for them are generated here.)
*/
ProtoHook
tp_slowtimo()
{
- register int r,t;
- struct Ccallout *cp;
- struct tp_ref *rp = tp_ref;
+ register u_int *cp;
+ register struct tp_ref *rp;
+ struct tp_pcb *tpcb;
struct tp_event E;
- int s = splnet();
+ int s = splnet(), t;
/* check only open reference structures */
IncStat(ts_Cticks);
- rp++; /* tp_ref[0] is never used */
- for( r=1 ; (r <= tp_maxrefopen) ; r++,rp++ ) {
- if (rp->tpr_state < REF_OPEN)
+ /* tp_ref[0] is never used */
+ for (rp = tp_ref + tp_refinfo.tpr_maxopen; rp > tp_ref; rp--) {
+ if ((tpcb = rp->tpr_pcb) == 0 || tpcb->tp_refstate < REF_OPEN)
continue;
-
- /* check the C-type timers */
- cp = rp->tpr_callout;
- for (t=0 ; t < N_CTIMERS; t++,cp++) {
- if( cp->c_active ) {
- if( --cp->c_time <= 0 ) {
- cp->c_active = FALSE;
- E.ev_number = t;
- IFDEBUG(D_TIMER)
- printf("C expired! type 0x%x\n", t);
- ENDDEBUG
- IncStat(ts_Cexpired);
- tp_driver( rp->tpr_pcb, &E);
+ /* check the timers */
+ for (t = 0; t < TM_NTIMERS; t++) {
+ cp = tpcb->tp_timer + t;
+ if (*cp && --(*cp) <= 0 ) {
+ *cp = 0;
+ E.ev_number = t;
+ IFDEBUG(D_TIMER)
+ printf("tp_slowtimo: pcb 0x%x t %d\n",
+ tpcb, t);
+ ENDDEBUG
+ IncStat(ts_Cexpired);
+ tp_driver(tpcb, &E);
+ if (t == TM_reference && tpcb->tp_state == TP_CLOSED) {
+ if (tpcb->tp_notdetached) {
+ IFDEBUG(D_CONN)
+ printf("PRU_DETACH: not detached\n");
+ ENDDEBUG
+ tp_detach(tpcb);
+ }
+ /* XXX wart; where else to do it? */
+ free((caddr_t)tpcb, M_PCB);
}
}
}
- /* now update the list */
- tp_Eclock(rp);
}
splx(s);
return 0;
}
+/*
+ * Called From: tp.trans from tp_slowtimo() -- retransmission timer went off.
+ */
+tp_data_retrans(tpcb)
+register struct tp_pcb *tpcb;
+{
+ int rexmt, win;
+ tpcb->tp_rttemit = 0; /* cancel current round trip time */
+ tpcb->tp_dupacks = 0;
+ tpcb->tp_sndnxt = tpcb->tp_snduna;
+ if (tpcb->tp_fcredit == 0) {
+ /*
+ * We transmitted new data, started timing it and the window
+ * got shrunk under us. This can only happen if all data
+ * that they wanted us to send got acked, so don't
+ * bother shrinking the congestion windows, et. al.
+ * The retransmission timer should have been reset in goodack()
+ */
+ IFDEBUG(D_ACKRECV)
+ printf("tp_data_retrans: 0 window tpcb 0x%x una 0x%x\n",
+ tpcb, tpcb->tp_snduna);
+ ENDDEBUG
+ tpcb->tp_rxtshift = 0;
+ tpcb->tp_timer[TM_data_retrans] = 0;
+ tpcb->tp_timer[TM_sendack] = tpcb->tp_dt_ticks;
+ return;
+ }
+ rexmt = tpcb->tp_dt_ticks << min(tpcb->tp_rxtshift, TP_MAXRXTSHIFT);
+ win = min(tpcb->tp_fcredit, (tpcb->tp_cong_win / tpcb->tp_l_tpdusize / 2));
+ win = max(win, 2);
+ tpcb->tp_cong_win = tpcb->tp_l_tpdusize; /* slow start again. */
+ tpcb->tp_ssthresh = win * tpcb->tp_l_tpdusize;
+ /* We're losing; our srtt estimate is probably bogus.
+ * Clobber it so we'll take the next rtt measurement as our srtt;
+ * Maintain current rxt times until then.
+ */
+ if (++tpcb->tp_rxtshift > TP_NRETRANS / 4) {
+ /* tpcb->tp_nlprotosw->nlp_losing(tpcb->tp_npcb) someday */
+ tpcb->tp_rtt = 0;
+ }
+ TP_RANGESET(tpcb->tp_rxtcur, rexmt, tpcb->tp_peer_acktime, 128);
+ tpcb->tp_timer[TM_data_retrans] = tpcb->tp_rxtcur;
+ tp_send(tpcb);
+}
+
+int
+tp_fasttimo()
+{
+ register struct tp_pcb *t;
+ int s = splnet();
+ struct tp_event E;
+
+ E.ev_number = TM_sendack;
+ while ((t = tp_ftimeolist) != (struct tp_pcb *)&tp_ftimeolist) {
+ if (t == 0) {
+ printf("tp_fasttimeo: should panic");
+ tp_ftimeolist = (struct tp_pcb *)&tp_ftimeolist;
+ } else {
+ if (t->tp_flags & TPF_DELACK) {
+ IncStat(ts_Fdelack);
+ tp_driver(t, &E);
+ t->tp_flags &= ~TPF_DELACK;
+ } else
+ IncStat(ts_Fpruned);
+ tp_ftimeolist = t->tp_fasttimeo;
+ t->tp_fasttimeo = 0;
+ }
+ }
+ splx(s);
+}
+
+#ifdef TP_DEBUG_TIMERS
/*
* CALLED FROM:
* tp.trans, tp_emit()
* Set a C type timer of type (which) to go off after (ticks) time.
*/
void
-tp_ctimeout(refp, which, ticks)
- register struct tp_ref *refp;
+tp_ctimeout(tpcb, which, ticks)
+ register struct tp_pcb *tpcb;
int which, ticks;
{
- register struct Ccallout *cp = &(refp->tpr_callout[which]);
IFTRACE(D_TIMER)
tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active",
- (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active);
+ tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
ENDTRACE
- if(cp->c_active)
+ if(tpcb->tp_timer[which])
IncStat(ts_Ccan_act);
IncStat(ts_Cset);
- cp->c_time = ticks;
- cp->c_active = TRUE;
+ if (ticks <= 0)
+ ticks = 1;
+ tpcb->tp_timer[which] = ticks;
}
/*
* parameter (ticks) is > the current value of the timer.
*/
void
-tp_ctimeout_MIN(refp, which, ticks)
- register struct tp_ref *refp;
+tp_ctimeout_MIN(tpcb, which, ticks)
+ register struct tp_pcb *tpcb;
int which, ticks;
{
- register struct Ccallout *cp = &(refp->tpr_callout[which]);
-
IFTRACE(D_TIMER)
tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active",
- (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active);
+ tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
ENDTRACE
- if(cp->c_active)
- IncStat(ts_Ccan_act);
IncStat(ts_Cset);
- if( cp->c_active )
- cp->c_time = MIN(ticks, cp->c_time);
- else {
- cp->c_time = ticks;
- cp->c_active = TRUE;
- }
+ if (tpcb->tp_timer[which]) {
+ tpcb->tp_timer[which] = min(ticks, tpcb->tp_timer[which]);
+ IncStat(ts_Ccan_act);
+ } else
+ tpcb->tp_timer[which] = ticks;
}
/*
* Cancel the (which) timer in the ref structure indicated by (refp).
*/
void
-tp_cuntimeout(refp, which)
+tp_cuntimeout(tpcb, which)
+ register struct tp_pcb *tpcb;
int which;
- register struct tp_ref *refp;
{
- register struct Ccallout *cp;
-
- cp = &(refp->tpr_callout[which]);
-
IFDEBUG(D_TIMER)
- printf("tp_cuntimeout(0x%x, %d) active %d\n", refp, which, cp->c_active);
+ printf("tp_cuntimeout(0x%x, %d) active %d\n",
+ tpcb, which, tpcb->tp_timer[which]);
ENDDEBUG
IFTRACE(D_TIMER)
tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref,
- which, cp->c_active, 0);
+ which, tpcb->tp_timer[which], 0);
ENDTRACE
- if(cp->c_active)
+ if (tpcb->tp_timer[which])
IncStat(ts_Ccan_act);
else
IncStat(ts_Ccan_inact);
- cp->c_active = FALSE;
+ tpcb->tp_timer[which] = 0;
}
+#endif