149268Sbostic /*- 249268Sbostic * Copyright (c) 1991 The Regents of the University of California. 349268Sbostic * All rights reserved. 449268Sbostic * 549268Sbostic * %sccs.include.redist.c% 649268Sbostic * 7*51006Ssklower * @(#)tp_timer.c 7.7 (Berkeley) 09/05/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; 78*51006Ssklower struct tp_refinfo tp_refinfo; 7936415Ssklower 8036415Ssklower extern int tp_maxrefopen; /* highest ref # of an open tp connection */ 8136415Ssklower 8236415Ssklower /* 8336415Ssklower * CALLED FROM: 8436415Ssklower * at autoconfig time from tp_init() 8536415Ssklower * a combo of event, state, predicate 8636415Ssklower * FUNCTION and ARGUMENTS: 8736415Ssklower * initialize data structures for the timers 8836415Ssklower */ 8936415Ssklower void 9036415Ssklower tp_timerinit() 9136415Ssklower { 9248752Ssklower register struct Ecallout *e; 9348752Ssklower register int s; 9448752Ssklower #define GETME(x, t, n) {s = (n)*sizeof(*x); x = (t) malloc(s, M_PCB, M_NOWAIT);\ 9548752Ssklower if (x == 0) panic("tp_timerinit"); bzero((caddr_t)x, s);} 9636415Ssklower /* 9748752Ssklower * Initialize storage 9836415Ssklower */ 9948752Ssklower GETME(TP_callout, struct Ecallout *, 2 * N_TPREF); 10048752Ssklower GETME(tp_ref, struct tp_ref *, 1 + N_TPREF); 101*51006Ssklower tp_refinfo.tpr_base = tp_ref; 102*51006Ssklower tp_refinfo.tpr_size = N_TPREF; /* XXX: There will be a better way */ 10336415Ssklower 10448752Ssklower TP_callfree = TP_callout + ((2 * N_TPREF) - 1); 10548752Ssklower for (e = TP_callfree; e > TP_callout; e--) 10648752Ssklower e->c_next = e - 1; 10736415Ssklower 10836415Ssklower /* hate to do this but we really don't want zero to be a legit ref */ 10936415Ssklower tp_maxrefopen = 1; 11036415Ssklower tp_ref[0].tpr_state = REF_FROZEN; /* white lie -- no ref timer, don't 11136415Ssklower * want this one to be allocated- ever 11236415Ssklower * unless, of course, you make refs and address instead of an 11336415Ssklower * index - then 0 can be allocated 11436415Ssklower */ 11548752Ssklower #undef GETME 11636415Ssklower } 11736415Ssklower 11836415Ssklower /********************** e timers *************************/ 11936415Ssklower 12036415Ssklower /* 12136415Ssklower * CALLED FROM: 12236415Ssklower * tp_slowtimo() every 1/2 second, for each open reference 12336415Ssklower * FUNCTION and ARGUMENTS: 12436415Ssklower * (refp) indicates a reference structure that is in use. 12536415Ssklower * This ref structure may contain active E-type timers. 12636415Ssklower * Update the timers and if any expire, create an event and 12736415Ssklower * call the driver. 12836415Ssklower */ 12936415Ssklower static void 13036415Ssklower tp_Eclock(refp) 13136415Ssklower struct tp_ref *refp; /* the reference structure */ 13236415Ssklower { 13336415Ssklower register struct Ecallout *p1; /* to drift through the list of callouts */ 13436415Ssklower struct tp_event E; /* event to pass to tp_driver() */ 13536415Ssklower int tp_driver(); /* drives the FSM */ 13636415Ssklower 13736415Ssklower /* 13836415Ssklower * Update real-time timeout queue. 13936415Ssklower * At front of queue are some number of events which are ``due''. 14036415Ssklower * The time to these is <= 0 and if negative represents the 14136415Ssklower * number of ticks which have passed since it was supposed to happen. 14236415Ssklower * The rest of the q elements (times > 0) are events yet to happen, 14336415Ssklower * where the time for each is given as a delta from the previous. 14436415Ssklower * Decrementing just the first of these serves to decrement the time 14536415Ssklower * to all events. 14636415Ssklower * 14736415Ssklower * This version, which calls the driver directly, doesn't pass 14836415Ssklower * along the ticks - may want to add the ticks if there's any use 14936415Ssklower * for them. 15036415Ssklower */ 15136415Ssklower IncStat(ts_Eticks); 15236415Ssklower p1 = refp->tpr_calltodo.c_next; 15336415Ssklower while (p1) { 15436415Ssklower if (--p1->c_time > 0) 15536415Ssklower break; 15636415Ssklower if (p1->c_time == 0) 15736415Ssklower break; 15836415Ssklower p1 = p1->c_next; 15936415Ssklower } 16036415Ssklower 16136415Ssklower for (;;) { 16237469Ssklower struct tp_pcb *tpcb; 16336415Ssklower if ((p1 = refp->tpr_calltodo.c_next) == 0 || p1->c_time > 0) { 16436415Ssklower break; 16536415Ssklower } 16636415Ssklower refp->tpr_calltodo.c_next = p1->c_next; 16736415Ssklower p1->c_next = TP_callfree; 16836415Ssklower 16936415Ssklower #ifndef lint 17036415Ssklower E.ev_number = p1->c_func; 17136415Ssklower E.ATTR(TM_data_retrans).e_low = (SeqNum) p1->c_arg1; 17236415Ssklower E.ATTR(TM_data_retrans).e_high = (SeqNum) p1->c_arg2; 17336415Ssklower E.ATTR(TM_data_retrans).e_retrans = p1->c_arg3; 17436415Ssklower #endif lint 17536415Ssklower IFDEBUG(D_TIMER) 17636415Ssklower printf("E expired! event 0x%x (0x%x,0x%x), pcb 0x%x ref %d\n", 17736415Ssklower p1->c_func, p1->c_arg1, p1->c_arg2, refp->tpr_pcb, 17836415Ssklower refp-tp_ref); 17936415Ssklower ENDDEBUG 18036415Ssklower 18136415Ssklower TP_callfree = p1; 18236415Ssklower IncStat(ts_Eexpired); 18337469Ssklower (void) tp_driver( tpcb = refp->tpr_pcb, &E); 18450940Ssklower if (p1->c_func == TM_reference && tpcb->tp_state == TP_CLOSED) { 18550940Ssklower if (tpcb->tp_notdetached) { 18650940Ssklower IFDEBUG(D_CONN) 18750940Ssklower printf("PRU_DETACH: not detached\n"); 18850940Ssklower ENDDEBUG 18950940Ssklower tp_detach(tpcb); 19050940Ssklower } 19137469Ssklower free((caddr_t)tpcb, M_PCB); /* XXX wart; where else to do it? */ 19250940Ssklower } 19336415Ssklower } 19436415Ssklower } 19536415Ssklower 19636415Ssklower /* 19736415Ssklower * CALLED FROM: 19836415Ssklower * tp.trans all over 19936415Ssklower * FUNCTION and ARGUMENTS: 20036415Ssklower * Set an E type timer. (refp) is the ref structure. 20136415Ssklower * Causes fun(arg1,arg2,arg3) to be called after time t. 20236415Ssklower */ 20336415Ssklower void 20436415Ssklower tp_etimeout(refp, fun, arg1, arg2, arg3, ticks) 20536415Ssklower struct tp_ref *refp; 20636415Ssklower int fun; /* function to be called */ 20736415Ssklower u_int arg1, arg2; 20836415Ssklower int arg3; 20936415Ssklower register int ticks; 21036415Ssklower { 21136415Ssklower register struct Ecallout *p1, *p2, *pnew; 21236415Ssklower /* p1 and p2 drift through the list of timeout callout structures, 21336415Ssklower * pnew points to the newly created callout structure 21436415Ssklower */ 21536415Ssklower 21636415Ssklower IFDEBUG(D_TIMER) 21736415Ssklower printf("etimeout pcb 0x%x state 0x%x\n", refp->tpr_pcb, 21836415Ssklower refp->tpr_pcb->tp_state); 21936415Ssklower ENDDEBUG 22036415Ssklower IFTRACE(D_TIMER) 22136415Ssklower tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", refp-tp_ref, 22236415Ssklower refp->tpr_state, ticks, tp_stat.ts_Eticks); 22336415Ssklower ENDTRACE 22436415Ssklower 22536415Ssklower IncStat(ts_Eset); 22636415Ssklower if (ticks == 0) 22736415Ssklower ticks = 1; 22836415Ssklower pnew = TP_callfree; 22936415Ssklower if (pnew == (struct Ecallout *)0) 23036415Ssklower panic("tp timeout table overflow"); 23136415Ssklower TP_callfree = pnew->c_next; 23236415Ssklower pnew->c_arg1 = arg1; 23336415Ssklower pnew->c_arg2 = arg2; 23436415Ssklower pnew->c_arg3 = arg3; 23536415Ssklower pnew->c_func = fun; 23636415Ssklower for (p1 = &(refp->tpr_calltodo); 23736415Ssklower (p2 = p1->c_next) && p2->c_time < ticks; p1 = p2) 23836415Ssklower if (p2->c_time > 0) 23936415Ssklower ticks -= p2->c_time; 24036415Ssklower p1->c_next = pnew; 24136415Ssklower pnew->c_next = p2; 24236415Ssklower pnew->c_time = ticks; 24336415Ssklower if (p2) 24436415Ssklower p2->c_time -= ticks; 24536415Ssklower } 24636415Ssklower 24736415Ssklower /* 24836415Ssklower * CALLED FROM: 24936415Ssklower * tp.trans all over 25036415Ssklower * FUNCTION and ARGUMENTS: 25136415Ssklower * Cancel all occurrences of E-timer function (fun) for reference (refp) 25236415Ssklower */ 25336415Ssklower void 25436415Ssklower tp_euntimeout(refp, fun) 25536415Ssklower struct tp_ref *refp; 25636415Ssklower int fun; 25736415Ssklower { 25836415Ssklower register struct Ecallout *p1, *p2; /* ptrs to drift through the list */ 25936415Ssklower 26036415Ssklower IFTRACE(D_TIMER) 26136415Ssklower tptrace(TPPTmisc, "tp_euntimeout ref", refp-tp_ref, 0, 0, 0); 26236415Ssklower ENDTRACE 26336415Ssklower 26436415Ssklower p1 = &refp->tpr_calltodo; 26536415Ssklower while ( (p2 = p1->c_next) != 0) { 26636415Ssklower if (p2->c_func == fun) { 26736415Ssklower if (p2->c_next && p2->c_time > 0) 26836415Ssklower p2->c_next->c_time += p2->c_time; 26936415Ssklower p1->c_next = p2->c_next; 27036415Ssklower p2->c_next = TP_callfree; 27136415Ssklower TP_callfree = p2; 27236415Ssklower IncStat(ts_Ecan_act); 27336415Ssklower continue; 27436415Ssklower } 27536415Ssklower p1 = p2; 27636415Ssklower } 27736415Ssklower } 27836415Ssklower 27936415Ssklower /* 28036415Ssklower * CALLED FROM: 28136415Ssklower * tp.trans, when an incoming ACK causes things to be dropped 28236415Ssklower * from the retransmission queue, and we want their associated 28336415Ssklower * timers to be cancelled. 28436415Ssklower * FUNCTION and ARGUMENTS: 28536415Ssklower * cancel all occurrences of function (fun) where (arg2) < (seq) 28636415Ssklower */ 28736415Ssklower void 28836415Ssklower tp_euntimeout_lss(refp, fun, seq) 28936415Ssklower struct tp_ref *refp; 29036415Ssklower int fun; 29136415Ssklower SeqNum seq; 29236415Ssklower { 29336415Ssklower register struct Ecallout *p1, *p2; 29436415Ssklower 29536415Ssklower IFTRACE(D_TIMER) 29636415Ssklower tptrace(TPPTmisc, "tp_euntimeoutLSS ref", refp-tp_ref, seq, 0, 0); 29736415Ssklower ENDTRACE 29836415Ssklower 29936415Ssklower p1 = &refp->tpr_calltodo; 30036415Ssklower while ( (p2 = p1->c_next) != 0) { 30136415Ssklower if ((p2->c_func == fun) && SEQ_LT(refp->tpr_pcb, p2->c_arg2, seq)) { 30236415Ssklower if (p2->c_next && p2->c_time > 0) 30336415Ssklower p2->c_next->c_time += p2->c_time; 30436415Ssklower p1->c_next = p2->c_next; 30536415Ssklower p2->c_next = TP_callfree; 30636415Ssklower TP_callfree = p2; 30736415Ssklower IncStat(ts_Ecan_act); 30836415Ssklower continue; 30936415Ssklower } 31036415Ssklower p1 = p2; 31136415Ssklower } 31236415Ssklower } 31336415Ssklower 31436415Ssklower /**************** c timers ********************** 31536415Ssklower * 31636415Ssklower * These are not chained together; they sit 31736415Ssklower * in the tp_ref structure. they are the kind that 31836415Ssklower * are typically cancelled so it's faster not to 31936415Ssklower * mess with the chains 32036415Ssklower */ 32136415Ssklower 32236415Ssklower /* 32336415Ssklower * CALLED FROM: 32436415Ssklower * the clock, every 500 ms 32536415Ssklower * FUNCTION and ARGUMENTS: 32636415Ssklower * Look for open references with active timers. 32736415Ssklower * If they exist, call the appropriate timer routines to update 32836415Ssklower * the timers and possibly generate events. 32936415Ssklower * (The E timers are done in other procedures; the C timers are 33036415Ssklower * updated here, and events for them are generated here.) 33136415Ssklower */ 33236415Ssklower ProtoHook 33336415Ssklower tp_slowtimo() 33436415Ssklower { 33536415Ssklower register int r,t; 33636415Ssklower struct Ccallout *cp; 33736415Ssklower struct tp_ref *rp = tp_ref; 33836415Ssklower struct tp_event E; 33936415Ssklower int s = splnet(); 34036415Ssklower 34136415Ssklower /* check only open reference structures */ 34236415Ssklower IncStat(ts_Cticks); 34336415Ssklower rp++; /* tp_ref[0] is never used */ 34436415Ssklower for( r=1 ; (r <= tp_maxrefopen) ; r++,rp++ ) { 34536415Ssklower if (rp->tpr_state < REF_OPEN) 34636415Ssklower continue; 34736415Ssklower 34836415Ssklower /* check the C-type timers */ 34936415Ssklower cp = rp->tpr_callout; 35036415Ssklower for (t=0 ; t < N_CTIMERS; t++,cp++) { 35136415Ssklower if( cp->c_active ) { 35236415Ssklower if( --cp->c_time <= 0 ) { 35336415Ssklower cp->c_active = FALSE; 35436415Ssklower E.ev_number = t; 35536415Ssklower IFDEBUG(D_TIMER) 35636415Ssklower printf("C expired! type 0x%x\n", t); 35736415Ssklower ENDDEBUG 35836415Ssklower IncStat(ts_Cexpired); 35936415Ssklower tp_driver( rp->tpr_pcb, &E); 36036415Ssklower } 36136415Ssklower } 36236415Ssklower } 36336415Ssklower /* now update the list */ 36436415Ssklower tp_Eclock(rp); 36536415Ssklower } 36636415Ssklower splx(s); 36736415Ssklower return 0; 36836415Ssklower } 36936415Ssklower 37036415Ssklower /* 37136415Ssklower * CALLED FROM: 37236415Ssklower * tp.trans, tp_emit() 37336415Ssklower * FUNCTION and ARGUMENTS: 37436415Ssklower * Set a C type timer of type (which) to go off after (ticks) time. 37536415Ssklower */ 37636415Ssklower void 37736415Ssklower tp_ctimeout(refp, which, ticks) 37836415Ssklower register struct tp_ref *refp; 37936415Ssklower int which, ticks; 38036415Ssklower { 38136415Ssklower register struct Ccallout *cp = &(refp->tpr_callout[which]); 38236415Ssklower 38336415Ssklower IFTRACE(D_TIMER) 38436415Ssklower tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active", 38536415Ssklower (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 38636415Ssklower ENDTRACE 38736415Ssklower if(cp->c_active) 38836415Ssklower IncStat(ts_Ccan_act); 38936415Ssklower IncStat(ts_Cset); 39036415Ssklower cp->c_time = ticks; 39136415Ssklower cp->c_active = TRUE; 39236415Ssklower } 39336415Ssklower 39436415Ssklower /* 39536415Ssklower * CALLED FROM: 39636415Ssklower * tp.trans 39736415Ssklower * FUNCTION and ARGUMENTS: 39836415Ssklower * Version of tp_ctimeout that resets the C-type time if the 39936415Ssklower * parameter (ticks) is > the current value of the timer. 40036415Ssklower */ 40136415Ssklower void 40236415Ssklower tp_ctimeout_MIN(refp, which, ticks) 40336415Ssklower register struct tp_ref *refp; 40436415Ssklower int which, ticks; 40536415Ssklower { 40636415Ssklower register struct Ccallout *cp = &(refp->tpr_callout[which]); 40736415Ssklower 40836415Ssklower IFTRACE(D_TIMER) 40936415Ssklower tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active", 41036415Ssklower (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 41136415Ssklower ENDTRACE 41236415Ssklower if(cp->c_active) 41336415Ssklower IncStat(ts_Ccan_act); 41436415Ssklower IncStat(ts_Cset); 41536415Ssklower if( cp->c_active ) 41636415Ssklower cp->c_time = MIN(ticks, cp->c_time); 41736415Ssklower else { 41836415Ssklower cp->c_time = ticks; 41936415Ssklower cp->c_active = TRUE; 42036415Ssklower } 42136415Ssklower } 42236415Ssklower 42336415Ssklower /* 42436415Ssklower * CALLED FROM: 42536415Ssklower * tp.trans 42636415Ssklower * FUNCTION and ARGUMENTS: 42736415Ssklower * Cancel the (which) timer in the ref structure indicated by (refp). 42836415Ssklower */ 42936415Ssklower void 43036415Ssklower tp_cuntimeout(refp, which) 43136415Ssklower int which; 43236415Ssklower register struct tp_ref *refp; 43336415Ssklower { 43436415Ssklower register struct Ccallout *cp; 43536415Ssklower 43636415Ssklower cp = &(refp->tpr_callout[which]); 43736415Ssklower 43836415Ssklower IFDEBUG(D_TIMER) 43937469Ssklower printf("tp_cuntimeout(0x%x, %d) active %d\n", refp, which, cp->c_active); 44036415Ssklower ENDDEBUG 44136415Ssklower 44236415Ssklower IFTRACE(D_TIMER) 44336415Ssklower tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref, 44436415Ssklower which, cp->c_active, 0); 44536415Ssklower ENDTRACE 44636415Ssklower 44736415Ssklower if(cp->c_active) 44836415Ssklower IncStat(ts_Ccan_act); 44936415Ssklower else 45036415Ssklower IncStat(ts_Ccan_inact); 45136415Ssklower cp->c_active = FALSE; 45236415Ssklower } 453