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