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