1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* 28 * ARGO TP 29 * 30 * $Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $ 31 * $Source: /usr/argo/sys/netiso/RCS/tp_timer.c,v $ 32 * @(#)tp_timer.c 7.3 (Berkeley) 08/29/89 * 33 * 34 * Contains all the timer code. 35 * There are two sources of calls to these routines: 36 * the clock, and tp.trans. (ok, and tp_pcb.c calls it at init time) 37 * 38 * Timers come in two flavors - those that generally get 39 * cancelled (tp_ctimeout, tp_cuntimeout) 40 * and those that either usually expire (tp_etimeout, 41 * tp_euntimeout, tp_slowtimo) or may require more than one instance 42 * of the timer active at a time. 43 * 44 * The C timers are stored in the tp_ref structure. Their "going off" 45 * is manifested by a driver event of the TM_xxx form. 46 * 47 * The E timers are handled like the generic kernel callouts. 48 * Their "going off" is manifested by a function call w/ 3 arguments. 49 */ 50 51 #ifndef lint 52 static char *rcsid = "$Header: tp_timer.c,v 5.2 88/11/18 17:29:07 nhall Exp $"; 53 #endif lint 54 55 #include "param.h" 56 #include "types.h" 57 #include "time.h" 58 #include "malloc.h" 59 60 #include "tp_param.h" 61 #include "tp_timer.h" 62 #include "tp_stat.h" 63 #include "tp_pcb.h" 64 #include "tp_tpdu.h" 65 #include "argo_debug.h" 66 #include "tp_trace.h" 67 #include "tp_seq.h" 68 69 static struct Ecallout *TP_callfree; 70 static struct Ecallout TP_callout[N_TPREF*2]; 71 72 extern int tp_maxrefopen; /* highest ref # of an open tp connection */ 73 74 /* 75 * CALLED FROM: 76 * at autoconfig time from tp_init() 77 * a combo of event, state, predicate 78 * FUNCTION and ARGUMENTS: 79 * initialize data structures for the timers 80 */ 81 void 82 tp_timerinit() 83 { 84 register int i; 85 /* 86 * Initialize callouts 87 */ 88 TP_callfree = TP_callout; 89 for (i = 1; i < N_TPREF*2; i++) 90 TP_callout[i-1].c_next = &TP_callout[i]; 91 92 bzero( (caddr_t)tp_ref, N_TPREF * sizeof(struct tp_ref) ); 93 94 /* hate to do this but we really don't want zero to be a legit ref */ 95 tp_maxrefopen = 1; 96 tp_ref[0].tpr_state = REF_FROZEN; /* white lie -- no ref timer, don't 97 * want this one to be allocated- ever 98 * unless, of course, you make refs and address instead of an 99 * index - then 0 can be allocated 100 */ 101 102 } 103 104 /********************** e timers *************************/ 105 106 /* 107 * CALLED FROM: 108 * tp_slowtimo() every 1/2 second, for each open reference 109 * FUNCTION and ARGUMENTS: 110 * (refp) indicates a reference structure that is in use. 111 * This ref structure may contain active E-type timers. 112 * Update the timers and if any expire, create an event and 113 * call the driver. 114 */ 115 static void 116 tp_Eclock(refp) 117 struct tp_ref *refp; /* the reference structure */ 118 { 119 register struct Ecallout *p1; /* to drift through the list of callouts */ 120 struct tp_event E; /* event to pass to tp_driver() */ 121 int tp_driver(); /* drives the FSM */ 122 123 /* 124 * Update real-time timeout queue. 125 * At front of queue are some number of events which are ``due''. 126 * The time to these is <= 0 and if negative represents the 127 * number of ticks which have passed since it was supposed to happen. 128 * The rest of the q elements (times > 0) are events yet to happen, 129 * where the time for each is given as a delta from the previous. 130 * Decrementing just the first of these serves to decrement the time 131 * to all events. 132 * 133 * This version, which calls the driver directly, doesn't pass 134 * along the ticks - may want to add the ticks if there's any use 135 * for them. 136 */ 137 IncStat(ts_Eticks); 138 p1 = refp->tpr_calltodo.c_next; 139 while (p1) { 140 if (--p1->c_time > 0) 141 break; 142 if (p1->c_time == 0) 143 break; 144 p1 = p1->c_next; 145 } 146 147 for (;;) { 148 struct tp_pcb *tpcb; 149 if ((p1 = refp->tpr_calltodo.c_next) == 0 || p1->c_time > 0) { 150 break; 151 } 152 refp->tpr_calltodo.c_next = p1->c_next; 153 p1->c_next = TP_callfree; 154 155 #ifndef lint 156 E.ev_number = p1->c_func; 157 E.ATTR(TM_data_retrans).e_low = (SeqNum) p1->c_arg1; 158 E.ATTR(TM_data_retrans).e_high = (SeqNum) p1->c_arg2; 159 E.ATTR(TM_data_retrans).e_retrans = p1->c_arg3; 160 #endif lint 161 IFDEBUG(D_TIMER) 162 printf("E expired! event 0x%x (0x%x,0x%x), pcb 0x%x ref %d\n", 163 p1->c_func, p1->c_arg1, p1->c_arg2, refp->tpr_pcb, 164 refp-tp_ref); 165 ENDDEBUG 166 167 TP_callfree = p1; 168 IncStat(ts_Eexpired); 169 (void) tp_driver( tpcb = refp->tpr_pcb, &E); 170 if (p1->c_func == TM_reference && tpcb->tp_state == TP_CLOSED) 171 free((caddr_t)tpcb, M_PCB); /* XXX wart; where else to do it? */ 172 } 173 } 174 175 /* 176 * CALLED FROM: 177 * tp.trans all over 178 * FUNCTION and ARGUMENTS: 179 * Set an E type timer. (refp) is the ref structure. 180 * Causes fun(arg1,arg2,arg3) to be called after time t. 181 */ 182 void 183 tp_etimeout(refp, fun, arg1, arg2, arg3, ticks) 184 struct tp_ref *refp; 185 int fun; /* function to be called */ 186 u_int arg1, arg2; 187 int arg3; 188 register int ticks; 189 { 190 register struct Ecallout *p1, *p2, *pnew; 191 /* p1 and p2 drift through the list of timeout callout structures, 192 * pnew points to the newly created callout structure 193 */ 194 195 IFDEBUG(D_TIMER) 196 printf("etimeout pcb 0x%x state 0x%x\n", refp->tpr_pcb, 197 refp->tpr_pcb->tp_state); 198 ENDDEBUG 199 IFTRACE(D_TIMER) 200 tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", refp-tp_ref, 201 refp->tpr_state, ticks, tp_stat.ts_Eticks); 202 ENDTRACE 203 204 IncStat(ts_Eset); 205 if (ticks == 0) 206 ticks = 1; 207 pnew = TP_callfree; 208 if (pnew == (struct Ecallout *)0) 209 panic("tp timeout table overflow"); 210 TP_callfree = pnew->c_next; 211 pnew->c_arg1 = arg1; 212 pnew->c_arg2 = arg2; 213 pnew->c_arg3 = arg3; 214 pnew->c_func = fun; 215 for (p1 = &(refp->tpr_calltodo); 216 (p2 = p1->c_next) && p2->c_time < ticks; p1 = p2) 217 if (p2->c_time > 0) 218 ticks -= p2->c_time; 219 p1->c_next = pnew; 220 pnew->c_next = p2; 221 pnew->c_time = ticks; 222 if (p2) 223 p2->c_time -= ticks; 224 } 225 226 /* 227 * CALLED FROM: 228 * tp.trans all over 229 * FUNCTION and ARGUMENTS: 230 * Cancel all occurrences of E-timer function (fun) for reference (refp) 231 */ 232 void 233 tp_euntimeout(refp, fun) 234 struct tp_ref *refp; 235 int fun; 236 { 237 register struct Ecallout *p1, *p2; /* ptrs to drift through the list */ 238 239 IFTRACE(D_TIMER) 240 tptrace(TPPTmisc, "tp_euntimeout ref", refp-tp_ref, 0, 0, 0); 241 ENDTRACE 242 243 p1 = &refp->tpr_calltodo; 244 while ( (p2 = p1->c_next) != 0) { 245 if (p2->c_func == fun) { 246 if (p2->c_next && p2->c_time > 0) 247 p2->c_next->c_time += p2->c_time; 248 p1->c_next = p2->c_next; 249 p2->c_next = TP_callfree; 250 TP_callfree = p2; 251 IncStat(ts_Ecan_act); 252 continue; 253 } 254 p1 = p2; 255 } 256 } 257 258 /* 259 * CALLED FROM: 260 * tp.trans, when an incoming ACK causes things to be dropped 261 * from the retransmission queue, and we want their associated 262 * timers to be cancelled. 263 * FUNCTION and ARGUMENTS: 264 * cancel all occurrences of function (fun) where (arg2) < (seq) 265 */ 266 void 267 tp_euntimeout_lss(refp, fun, seq) 268 struct tp_ref *refp; 269 int fun; 270 SeqNum seq; 271 { 272 register struct Ecallout *p1, *p2; 273 274 IFTRACE(D_TIMER) 275 tptrace(TPPTmisc, "tp_euntimeoutLSS ref", refp-tp_ref, seq, 0, 0); 276 ENDTRACE 277 278 p1 = &refp->tpr_calltodo; 279 while ( (p2 = p1->c_next) != 0) { 280 if ((p2->c_func == fun) && SEQ_LT(refp->tpr_pcb, p2->c_arg2, seq)) { 281 if (p2->c_next && p2->c_time > 0) 282 p2->c_next->c_time += p2->c_time; 283 p1->c_next = p2->c_next; 284 p2->c_next = TP_callfree; 285 TP_callfree = p2; 286 IncStat(ts_Ecan_act); 287 continue; 288 } 289 p1 = p2; 290 } 291 } 292 293 /**************** c timers ********************** 294 * 295 * These are not chained together; they sit 296 * in the tp_ref structure. they are the kind that 297 * are typically cancelled so it's faster not to 298 * mess with the chains 299 */ 300 301 /* 302 * CALLED FROM: 303 * the clock, every 500 ms 304 * FUNCTION and ARGUMENTS: 305 * Look for open references with active timers. 306 * If they exist, call the appropriate timer routines to update 307 * the timers and possibly generate events. 308 * (The E timers are done in other procedures; the C timers are 309 * updated here, and events for them are generated here.) 310 */ 311 ProtoHook 312 tp_slowtimo() 313 { 314 register int r,t; 315 struct Ccallout *cp; 316 struct tp_ref *rp = tp_ref; 317 struct tp_event E; 318 int s = splnet(); 319 320 /* check only open reference structures */ 321 IncStat(ts_Cticks); 322 rp++; /* tp_ref[0] is never used */ 323 for( r=1 ; (r <= tp_maxrefopen) ; r++,rp++ ) { 324 if (rp->tpr_state < REF_OPEN) 325 continue; 326 327 /* check the C-type timers */ 328 cp = rp->tpr_callout; 329 for (t=0 ; t < N_CTIMERS; t++,cp++) { 330 if( cp->c_active ) { 331 if( --cp->c_time <= 0 ) { 332 cp->c_active = FALSE; 333 E.ev_number = t; 334 IFDEBUG(D_TIMER) 335 printf("C expired! type 0x%x\n", t); 336 ENDDEBUG 337 IncStat(ts_Cexpired); 338 tp_driver( rp->tpr_pcb, &E); 339 } 340 } 341 } 342 /* now update the list */ 343 tp_Eclock(rp); 344 } 345 splx(s); 346 return 0; 347 } 348 349 /* 350 * CALLED FROM: 351 * tp.trans, tp_emit() 352 * FUNCTION and ARGUMENTS: 353 * Set a C type timer of type (which) to go off after (ticks) time. 354 */ 355 void 356 tp_ctimeout(refp, which, ticks) 357 register struct tp_ref *refp; 358 int which, ticks; 359 { 360 register struct Ccallout *cp = &(refp->tpr_callout[which]); 361 362 IFTRACE(D_TIMER) 363 tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active", 364 (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 365 ENDTRACE 366 if(cp->c_active) 367 IncStat(ts_Ccan_act); 368 IncStat(ts_Cset); 369 cp->c_time = ticks; 370 cp->c_active = TRUE; 371 } 372 373 /* 374 * CALLED FROM: 375 * tp.trans 376 * FUNCTION and ARGUMENTS: 377 * Version of tp_ctimeout that resets the C-type time if the 378 * parameter (ticks) is > the current value of the timer. 379 */ 380 void 381 tp_ctimeout_MIN(refp, which, ticks) 382 register struct tp_ref *refp; 383 int which, ticks; 384 { 385 register struct Ccallout *cp = &(refp->tpr_callout[which]); 386 387 IFTRACE(D_TIMER) 388 tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active", 389 (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 390 ENDTRACE 391 if(cp->c_active) 392 IncStat(ts_Ccan_act); 393 IncStat(ts_Cset); 394 if( cp->c_active ) 395 cp->c_time = MIN(ticks, cp->c_time); 396 else { 397 cp->c_time = ticks; 398 cp->c_active = TRUE; 399 } 400 } 401 402 /* 403 * CALLED FROM: 404 * tp.trans 405 * FUNCTION and ARGUMENTS: 406 * Cancel the (which) timer in the ref structure indicated by (refp). 407 */ 408 void 409 tp_cuntimeout(refp, which) 410 int which; 411 register struct tp_ref *refp; 412 { 413 register struct Ccallout *cp; 414 415 cp = &(refp->tpr_callout[which]); 416 417 IFDEBUG(D_TIMER) 418 printf("tp_cuntimeout(0x%x, %d) active %d\n", refp, which, cp->c_active); 419 ENDDEBUG 420 421 IFTRACE(D_TIMER) 422 tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref, 423 which, cp->c_active, 0); 424 ENDTRACE 425 426 if(cp->c_active) 427 IncStat(ts_Ccan_act); 428 else 429 IncStat(ts_Ccan_inact); 430 cp->c_active = FALSE; 431 } 432