149268Sbostic /*- 249268Sbostic * Copyright (c) 1991 The Regents of the University of California. 349268Sbostic * All rights reserved. 449268Sbostic * 549268Sbostic * %sccs.include.redist.c% 649268Sbostic * 7*50940Ssklower * @(#)tp_timer.c 7.6 (Berkeley) 08/30/91 849268Sbostic */ 949268Sbostic 1036415Ssklower /*********************************************************** 1136415Ssklower Copyright IBM Corporation 1987 1236415Ssklower 1336415Ssklower All Rights Reserved 1436415Ssklower 1536415Ssklower Permission to use, copy, modify, and distribute this software and its 1636415Ssklower documentation for any purpose and without fee is hereby granted, 1736415Ssklower provided that the above copyright notice appear in all copies and that 1836415Ssklower both that copyright notice and this permission notice appear in 1936415Ssklower supporting documentation, and that the name of IBM not be 2036415Ssklower used in advertising or publicity pertaining to distribution of the 2136415Ssklower software without specific, written prior permission. 2236415Ssklower 2336415Ssklower IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 2436415Ssklower ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 2536415Ssklower IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 2636415Ssklower ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 2736415Ssklower WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 2836415Ssklower ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 2936415Ssklower SOFTWARE. 3036415Ssklower 3136415Ssklower ******************************************************************/ 3236415Ssklower 3336415Ssklower /* 3436415Ssklower * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 3536415Ssklower */ 3636415Ssklower /* 3736415Ssklower * ARGO TP 3836415Ssklower * 3936415Ssklower * $Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $ 4036415Ssklower * $Source: /usr/argo/sys/netiso/RCS/tp_timer.c,v $ 4136415Ssklower * 4236415Ssklower * Contains all the timer code. 4336415Ssklower * There are two sources of calls to these routines: 4436415Ssklower * the clock, and tp.trans. (ok, and tp_pcb.c calls it at init time) 4536415Ssklower * 4636415Ssklower * Timers come in two flavors - those that generally get 4736415Ssklower * cancelled (tp_ctimeout, tp_cuntimeout) 4836415Ssklower * and those that either usually expire (tp_etimeout, 4936415Ssklower * tp_euntimeout, tp_slowtimo) or may require more than one instance 5036415Ssklower * of the timer active at a time. 5136415Ssklower * 5236415Ssklower * The C timers are stored in the tp_ref structure. Their "going off" 5336415Ssklower * is manifested by a driver event of the TM_xxx form. 5436415Ssklower * 5536415Ssklower * The E timers are handled like the generic kernel callouts. 5636415Ssklower * Their "going off" is manifested by a function call w/ 3 arguments. 5736415Ssklower */ 5836415Ssklower 5936415Ssklower #include "param.h" 6036415Ssklower #include "types.h" 6136415Ssklower #include "time.h" 6237469Ssklower #include "malloc.h" 6348752Ssklower #include "socket.h" 6436415Ssklower 6537469Ssklower #include "tp_param.h" 6637469Ssklower #include "tp_timer.h" 6737469Ssklower #include "tp_stat.h" 6837469Ssklower #include "tp_pcb.h" 6937469Ssklower #include "tp_tpdu.h" 7037469Ssklower #include "argo_debug.h" 7137469Ssklower #include "tp_trace.h" 7237469Ssklower #include "tp_seq.h" 7336415Ssklower 7448752Ssklower struct Ecallout *TP_callfree; 7548752Ssklower struct Ecallout *TP_callout; 7648752Ssklower struct tp_ref *tp_ref; 7748752Ssklower int N_TPREF = 100; 7836415Ssklower 7936415Ssklower extern int tp_maxrefopen; /* highest ref # of an open tp connection */ 8036415Ssklower 8136415Ssklower /* 8236415Ssklower * CALLED FROM: 8336415Ssklower * at autoconfig time from tp_init() 8436415Ssklower * a combo of event, state, predicate 8536415Ssklower * FUNCTION and ARGUMENTS: 8636415Ssklower * initialize data structures for the timers 8736415Ssklower */ 8836415Ssklower void 8936415Ssklower tp_timerinit() 9036415Ssklower { 9148752Ssklower register struct Ecallout *e; 9248752Ssklower register int s; 9348752Ssklower #define GETME(x, t, n) {s = (n)*sizeof(*x); x = (t) malloc(s, M_PCB, M_NOWAIT);\ 9448752Ssklower if (x == 0) panic("tp_timerinit"); bzero((caddr_t)x, s);} 9536415Ssklower /* 9648752Ssklower * Initialize storage 9736415Ssklower */ 9848752Ssklower GETME(TP_callout, struct Ecallout *, 2 * N_TPREF); 9948752Ssklower GETME(tp_ref, struct tp_ref *, 1 + N_TPREF); 10036415Ssklower 10148752Ssklower TP_callfree = TP_callout + ((2 * N_TPREF) - 1); 10248752Ssklower for (e = TP_callfree; e > TP_callout; e--) 10348752Ssklower e->c_next = e - 1; 10436415Ssklower 10536415Ssklower /* hate to do this but we really don't want zero to be a legit ref */ 10636415Ssklower tp_maxrefopen = 1; 10736415Ssklower tp_ref[0].tpr_state = REF_FROZEN; /* white lie -- no ref timer, don't 10836415Ssklower * want this one to be allocated- ever 10936415Ssklower * unless, of course, you make refs and address instead of an 11036415Ssklower * index - then 0 can be allocated 11136415Ssklower */ 11248752Ssklower #undef GETME 11336415Ssklower } 11436415Ssklower 11536415Ssklower /********************** e timers *************************/ 11636415Ssklower 11736415Ssklower /* 11836415Ssklower * CALLED FROM: 11936415Ssklower * tp_slowtimo() every 1/2 second, for each open reference 12036415Ssklower * FUNCTION and ARGUMENTS: 12136415Ssklower * (refp) indicates a reference structure that is in use. 12236415Ssklower * This ref structure may contain active E-type timers. 12336415Ssklower * Update the timers and if any expire, create an event and 12436415Ssklower * call the driver. 12536415Ssklower */ 12636415Ssklower static void 12736415Ssklower tp_Eclock(refp) 12836415Ssklower struct tp_ref *refp; /* the reference structure */ 12936415Ssklower { 13036415Ssklower register struct Ecallout *p1; /* to drift through the list of callouts */ 13136415Ssklower struct tp_event E; /* event to pass to tp_driver() */ 13236415Ssklower int tp_driver(); /* drives the FSM */ 13336415Ssklower 13436415Ssklower /* 13536415Ssklower * Update real-time timeout queue. 13636415Ssklower * At front of queue are some number of events which are ``due''. 13736415Ssklower * The time to these is <= 0 and if negative represents the 13836415Ssklower * number of ticks which have passed since it was supposed to happen. 13936415Ssklower * The rest of the q elements (times > 0) are events yet to happen, 14036415Ssklower * where the time for each is given as a delta from the previous. 14136415Ssklower * Decrementing just the first of these serves to decrement the time 14236415Ssklower * to all events. 14336415Ssklower * 14436415Ssklower * This version, which calls the driver directly, doesn't pass 14536415Ssklower * along the ticks - may want to add the ticks if there's any use 14636415Ssklower * for them. 14736415Ssklower */ 14836415Ssklower IncStat(ts_Eticks); 14936415Ssklower p1 = refp->tpr_calltodo.c_next; 15036415Ssklower while (p1) { 15136415Ssklower if (--p1->c_time > 0) 15236415Ssklower break; 15336415Ssklower if (p1->c_time == 0) 15436415Ssklower break; 15536415Ssklower p1 = p1->c_next; 15636415Ssklower } 15736415Ssklower 15836415Ssklower for (;;) { 15937469Ssklower struct tp_pcb *tpcb; 16036415Ssklower if ((p1 = refp->tpr_calltodo.c_next) == 0 || p1->c_time > 0) { 16136415Ssklower break; 16236415Ssklower } 16336415Ssklower refp->tpr_calltodo.c_next = p1->c_next; 16436415Ssklower p1->c_next = TP_callfree; 16536415Ssklower 16636415Ssklower #ifndef lint 16736415Ssklower E.ev_number = p1->c_func; 16836415Ssklower E.ATTR(TM_data_retrans).e_low = (SeqNum) p1->c_arg1; 16936415Ssklower E.ATTR(TM_data_retrans).e_high = (SeqNum) p1->c_arg2; 17036415Ssklower E.ATTR(TM_data_retrans).e_retrans = p1->c_arg3; 17136415Ssklower #endif lint 17236415Ssklower IFDEBUG(D_TIMER) 17336415Ssklower printf("E expired! event 0x%x (0x%x,0x%x), pcb 0x%x ref %d\n", 17436415Ssklower p1->c_func, p1->c_arg1, p1->c_arg2, refp->tpr_pcb, 17536415Ssklower refp-tp_ref); 17636415Ssklower ENDDEBUG 17736415Ssklower 17836415Ssklower TP_callfree = p1; 17936415Ssklower IncStat(ts_Eexpired); 18037469Ssklower (void) tp_driver( tpcb = refp->tpr_pcb, &E); 181*50940Ssklower if (p1->c_func == TM_reference && tpcb->tp_state == TP_CLOSED) { 182*50940Ssklower if (tpcb->tp_notdetached) { 183*50940Ssklower IFDEBUG(D_CONN) 184*50940Ssklower printf("PRU_DETACH: not detached\n"); 185*50940Ssklower ENDDEBUG 186*50940Ssklower tp_detach(tpcb); 187*50940Ssklower } 18837469Ssklower free((caddr_t)tpcb, M_PCB); /* XXX wart; where else to do it? */ 189*50940Ssklower } 19036415Ssklower } 19136415Ssklower } 19236415Ssklower 19336415Ssklower /* 19436415Ssklower * CALLED FROM: 19536415Ssklower * tp.trans all over 19636415Ssklower * FUNCTION and ARGUMENTS: 19736415Ssklower * Set an E type timer. (refp) is the ref structure. 19836415Ssklower * Causes fun(arg1,arg2,arg3) to be called after time t. 19936415Ssklower */ 20036415Ssklower void 20136415Ssklower tp_etimeout(refp, fun, arg1, arg2, arg3, ticks) 20236415Ssklower struct tp_ref *refp; 20336415Ssklower int fun; /* function to be called */ 20436415Ssklower u_int arg1, arg2; 20536415Ssklower int arg3; 20636415Ssklower register int ticks; 20736415Ssklower { 20836415Ssklower register struct Ecallout *p1, *p2, *pnew; 20936415Ssklower /* p1 and p2 drift through the list of timeout callout structures, 21036415Ssklower * pnew points to the newly created callout structure 21136415Ssklower */ 21236415Ssklower 21336415Ssklower IFDEBUG(D_TIMER) 21436415Ssklower printf("etimeout pcb 0x%x state 0x%x\n", refp->tpr_pcb, 21536415Ssklower refp->tpr_pcb->tp_state); 21636415Ssklower ENDDEBUG 21736415Ssklower IFTRACE(D_TIMER) 21836415Ssklower tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", refp-tp_ref, 21936415Ssklower refp->tpr_state, ticks, tp_stat.ts_Eticks); 22036415Ssklower ENDTRACE 22136415Ssklower 22236415Ssklower IncStat(ts_Eset); 22336415Ssklower if (ticks == 0) 22436415Ssklower ticks = 1; 22536415Ssklower pnew = TP_callfree; 22636415Ssklower if (pnew == (struct Ecallout *)0) 22736415Ssklower panic("tp timeout table overflow"); 22836415Ssklower TP_callfree = pnew->c_next; 22936415Ssklower pnew->c_arg1 = arg1; 23036415Ssklower pnew->c_arg2 = arg2; 23136415Ssklower pnew->c_arg3 = arg3; 23236415Ssklower pnew->c_func = fun; 23336415Ssklower for (p1 = &(refp->tpr_calltodo); 23436415Ssklower (p2 = p1->c_next) && p2->c_time < ticks; p1 = p2) 23536415Ssklower if (p2->c_time > 0) 23636415Ssklower ticks -= p2->c_time; 23736415Ssklower p1->c_next = pnew; 23836415Ssklower pnew->c_next = p2; 23936415Ssklower pnew->c_time = ticks; 24036415Ssklower if (p2) 24136415Ssklower p2->c_time -= ticks; 24236415Ssklower } 24336415Ssklower 24436415Ssklower /* 24536415Ssklower * CALLED FROM: 24636415Ssklower * tp.trans all over 24736415Ssklower * FUNCTION and ARGUMENTS: 24836415Ssklower * Cancel all occurrences of E-timer function (fun) for reference (refp) 24936415Ssklower */ 25036415Ssklower void 25136415Ssklower tp_euntimeout(refp, fun) 25236415Ssklower struct tp_ref *refp; 25336415Ssklower int fun; 25436415Ssklower { 25536415Ssklower register struct Ecallout *p1, *p2; /* ptrs to drift through the list */ 25636415Ssklower 25736415Ssklower IFTRACE(D_TIMER) 25836415Ssklower tptrace(TPPTmisc, "tp_euntimeout ref", refp-tp_ref, 0, 0, 0); 25936415Ssklower ENDTRACE 26036415Ssklower 26136415Ssklower p1 = &refp->tpr_calltodo; 26236415Ssklower while ( (p2 = p1->c_next) != 0) { 26336415Ssklower if (p2->c_func == fun) { 26436415Ssklower if (p2->c_next && p2->c_time > 0) 26536415Ssklower p2->c_next->c_time += p2->c_time; 26636415Ssklower p1->c_next = p2->c_next; 26736415Ssklower p2->c_next = TP_callfree; 26836415Ssklower TP_callfree = p2; 26936415Ssklower IncStat(ts_Ecan_act); 27036415Ssklower continue; 27136415Ssklower } 27236415Ssklower p1 = p2; 27336415Ssklower } 27436415Ssklower } 27536415Ssklower 27636415Ssklower /* 27736415Ssklower * CALLED FROM: 27836415Ssklower * tp.trans, when an incoming ACK causes things to be dropped 27936415Ssklower * from the retransmission queue, and we want their associated 28036415Ssklower * timers to be cancelled. 28136415Ssklower * FUNCTION and ARGUMENTS: 28236415Ssklower * cancel all occurrences of function (fun) where (arg2) < (seq) 28336415Ssklower */ 28436415Ssklower void 28536415Ssklower tp_euntimeout_lss(refp, fun, seq) 28636415Ssklower struct tp_ref *refp; 28736415Ssklower int fun; 28836415Ssklower SeqNum seq; 28936415Ssklower { 29036415Ssklower register struct Ecallout *p1, *p2; 29136415Ssklower 29236415Ssklower IFTRACE(D_TIMER) 29336415Ssklower tptrace(TPPTmisc, "tp_euntimeoutLSS ref", refp-tp_ref, seq, 0, 0); 29436415Ssklower ENDTRACE 29536415Ssklower 29636415Ssklower p1 = &refp->tpr_calltodo; 29736415Ssklower while ( (p2 = p1->c_next) != 0) { 29836415Ssklower if ((p2->c_func == fun) && SEQ_LT(refp->tpr_pcb, p2->c_arg2, seq)) { 29936415Ssklower if (p2->c_next && p2->c_time > 0) 30036415Ssklower p2->c_next->c_time += p2->c_time; 30136415Ssklower p1->c_next = p2->c_next; 30236415Ssklower p2->c_next = TP_callfree; 30336415Ssklower TP_callfree = p2; 30436415Ssklower IncStat(ts_Ecan_act); 30536415Ssklower continue; 30636415Ssklower } 30736415Ssklower p1 = p2; 30836415Ssklower } 30936415Ssklower } 31036415Ssklower 31136415Ssklower /**************** c timers ********************** 31236415Ssklower * 31336415Ssklower * These are not chained together; they sit 31436415Ssklower * in the tp_ref structure. they are the kind that 31536415Ssklower * are typically cancelled so it's faster not to 31636415Ssklower * mess with the chains 31736415Ssklower */ 31836415Ssklower 31936415Ssklower /* 32036415Ssklower * CALLED FROM: 32136415Ssklower * the clock, every 500 ms 32236415Ssklower * FUNCTION and ARGUMENTS: 32336415Ssklower * Look for open references with active timers. 32436415Ssklower * If they exist, call the appropriate timer routines to update 32536415Ssklower * the timers and possibly generate events. 32636415Ssklower * (The E timers are done in other procedures; the C timers are 32736415Ssklower * updated here, and events for them are generated here.) 32836415Ssklower */ 32936415Ssklower ProtoHook 33036415Ssklower tp_slowtimo() 33136415Ssklower { 33236415Ssklower register int r,t; 33336415Ssklower struct Ccallout *cp; 33436415Ssklower struct tp_ref *rp = tp_ref; 33536415Ssklower struct tp_event E; 33636415Ssklower int s = splnet(); 33736415Ssklower 33836415Ssklower /* check only open reference structures */ 33936415Ssklower IncStat(ts_Cticks); 34036415Ssklower rp++; /* tp_ref[0] is never used */ 34136415Ssklower for( r=1 ; (r <= tp_maxrefopen) ; r++,rp++ ) { 34236415Ssklower if (rp->tpr_state < REF_OPEN) 34336415Ssklower continue; 34436415Ssklower 34536415Ssklower /* check the C-type timers */ 34636415Ssklower cp = rp->tpr_callout; 34736415Ssklower for (t=0 ; t < N_CTIMERS; t++,cp++) { 34836415Ssklower if( cp->c_active ) { 34936415Ssklower if( --cp->c_time <= 0 ) { 35036415Ssklower cp->c_active = FALSE; 35136415Ssklower E.ev_number = t; 35236415Ssklower IFDEBUG(D_TIMER) 35336415Ssklower printf("C expired! type 0x%x\n", t); 35436415Ssklower ENDDEBUG 35536415Ssklower IncStat(ts_Cexpired); 35636415Ssklower tp_driver( rp->tpr_pcb, &E); 35736415Ssklower } 35836415Ssklower } 35936415Ssklower } 36036415Ssklower /* now update the list */ 36136415Ssklower tp_Eclock(rp); 36236415Ssklower } 36336415Ssklower splx(s); 36436415Ssklower return 0; 36536415Ssklower } 36636415Ssklower 36736415Ssklower /* 36836415Ssklower * CALLED FROM: 36936415Ssklower * tp.trans, tp_emit() 37036415Ssklower * FUNCTION and ARGUMENTS: 37136415Ssklower * Set a C type timer of type (which) to go off after (ticks) time. 37236415Ssklower */ 37336415Ssklower void 37436415Ssklower tp_ctimeout(refp, which, ticks) 37536415Ssklower register struct tp_ref *refp; 37636415Ssklower int which, ticks; 37736415Ssklower { 37836415Ssklower register struct Ccallout *cp = &(refp->tpr_callout[which]); 37936415Ssklower 38036415Ssklower IFTRACE(D_TIMER) 38136415Ssklower tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active", 38236415Ssklower (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 38336415Ssklower ENDTRACE 38436415Ssklower if(cp->c_active) 38536415Ssklower IncStat(ts_Ccan_act); 38636415Ssklower IncStat(ts_Cset); 38736415Ssklower cp->c_time = ticks; 38836415Ssklower cp->c_active = TRUE; 38936415Ssklower } 39036415Ssklower 39136415Ssklower /* 39236415Ssklower * CALLED FROM: 39336415Ssklower * tp.trans 39436415Ssklower * FUNCTION and ARGUMENTS: 39536415Ssklower * Version of tp_ctimeout that resets the C-type time if the 39636415Ssklower * parameter (ticks) is > the current value of the timer. 39736415Ssklower */ 39836415Ssklower void 39936415Ssklower tp_ctimeout_MIN(refp, which, ticks) 40036415Ssklower register struct tp_ref *refp; 40136415Ssklower int which, ticks; 40236415Ssklower { 40336415Ssklower register struct Ccallout *cp = &(refp->tpr_callout[which]); 40436415Ssklower 40536415Ssklower IFTRACE(D_TIMER) 40636415Ssklower tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active", 40736415Ssklower (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 40836415Ssklower ENDTRACE 40936415Ssklower if(cp->c_active) 41036415Ssklower IncStat(ts_Ccan_act); 41136415Ssklower IncStat(ts_Cset); 41236415Ssklower if( cp->c_active ) 41336415Ssklower cp->c_time = MIN(ticks, cp->c_time); 41436415Ssklower else { 41536415Ssklower cp->c_time = ticks; 41636415Ssklower cp->c_active = TRUE; 41736415Ssklower } 41836415Ssklower } 41936415Ssklower 42036415Ssklower /* 42136415Ssklower * CALLED FROM: 42236415Ssklower * tp.trans 42336415Ssklower * FUNCTION and ARGUMENTS: 42436415Ssklower * Cancel the (which) timer in the ref structure indicated by (refp). 42536415Ssklower */ 42636415Ssklower void 42736415Ssklower tp_cuntimeout(refp, which) 42836415Ssklower int which; 42936415Ssklower register struct tp_ref *refp; 43036415Ssklower { 43136415Ssklower register struct Ccallout *cp; 43236415Ssklower 43336415Ssklower cp = &(refp->tpr_callout[which]); 43436415Ssklower 43536415Ssklower IFDEBUG(D_TIMER) 43637469Ssklower printf("tp_cuntimeout(0x%x, %d) active %d\n", refp, which, cp->c_active); 43736415Ssklower ENDDEBUG 43836415Ssklower 43936415Ssklower IFTRACE(D_TIMER) 44036415Ssklower tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref, 44136415Ssklower which, cp->c_active, 0); 44236415Ssklower ENDTRACE 44336415Ssklower 44436415Ssklower if(cp->c_active) 44536415Ssklower IncStat(ts_Ccan_act); 44636415Ssklower else 44736415Ssklower IncStat(ts_Ccan_inact); 44836415Ssklower cp->c_active = FALSE; 44936415Ssklower } 450