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