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.6 (Berkeley) 08/30/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 if (tpcb->tp_notdetached) { 183 IFDEBUG(D_CONN) 184 printf("PRU_DETACH: not detached\n"); 185 ENDDEBUG 186 tp_detach(tpcb); 187 } 188 free((caddr_t)tpcb, M_PCB); /* XXX wart; where else to do it? */ 189 } 190 } 191 } 192 193 /* 194 * CALLED FROM: 195 * tp.trans all over 196 * FUNCTION and ARGUMENTS: 197 * Set an E type timer. (refp) is the ref structure. 198 * Causes fun(arg1,arg2,arg3) to be called after time t. 199 */ 200 void 201 tp_etimeout(refp, fun, arg1, arg2, arg3, ticks) 202 struct tp_ref *refp; 203 int fun; /* function to be called */ 204 u_int arg1, arg2; 205 int arg3; 206 register int ticks; 207 { 208 register struct Ecallout *p1, *p2, *pnew; 209 /* p1 and p2 drift through the list of timeout callout structures, 210 * pnew points to the newly created callout structure 211 */ 212 213 IFDEBUG(D_TIMER) 214 printf("etimeout pcb 0x%x state 0x%x\n", refp->tpr_pcb, 215 refp->tpr_pcb->tp_state); 216 ENDDEBUG 217 IFTRACE(D_TIMER) 218 tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", refp-tp_ref, 219 refp->tpr_state, ticks, tp_stat.ts_Eticks); 220 ENDTRACE 221 222 IncStat(ts_Eset); 223 if (ticks == 0) 224 ticks = 1; 225 pnew = TP_callfree; 226 if (pnew == (struct Ecallout *)0) 227 panic("tp timeout table overflow"); 228 TP_callfree = pnew->c_next; 229 pnew->c_arg1 = arg1; 230 pnew->c_arg2 = arg2; 231 pnew->c_arg3 = arg3; 232 pnew->c_func = fun; 233 for (p1 = &(refp->tpr_calltodo); 234 (p2 = p1->c_next) && p2->c_time < ticks; p1 = p2) 235 if (p2->c_time > 0) 236 ticks -= p2->c_time; 237 p1->c_next = pnew; 238 pnew->c_next = p2; 239 pnew->c_time = ticks; 240 if (p2) 241 p2->c_time -= ticks; 242 } 243 244 /* 245 * CALLED FROM: 246 * tp.trans all over 247 * FUNCTION and ARGUMENTS: 248 * Cancel all occurrences of E-timer function (fun) for reference (refp) 249 */ 250 void 251 tp_euntimeout(refp, fun) 252 struct tp_ref *refp; 253 int fun; 254 { 255 register struct Ecallout *p1, *p2; /* ptrs to drift through the list */ 256 257 IFTRACE(D_TIMER) 258 tptrace(TPPTmisc, "tp_euntimeout ref", refp-tp_ref, 0, 0, 0); 259 ENDTRACE 260 261 p1 = &refp->tpr_calltodo; 262 while ( (p2 = p1->c_next) != 0) { 263 if (p2->c_func == fun) { 264 if (p2->c_next && p2->c_time > 0) 265 p2->c_next->c_time += p2->c_time; 266 p1->c_next = p2->c_next; 267 p2->c_next = TP_callfree; 268 TP_callfree = p2; 269 IncStat(ts_Ecan_act); 270 continue; 271 } 272 p1 = p2; 273 } 274 } 275 276 /* 277 * CALLED FROM: 278 * tp.trans, when an incoming ACK causes things to be dropped 279 * from the retransmission queue, and we want their associated 280 * timers to be cancelled. 281 * FUNCTION and ARGUMENTS: 282 * cancel all occurrences of function (fun) where (arg2) < (seq) 283 */ 284 void 285 tp_euntimeout_lss(refp, fun, seq) 286 struct tp_ref *refp; 287 int fun; 288 SeqNum seq; 289 { 290 register struct Ecallout *p1, *p2; 291 292 IFTRACE(D_TIMER) 293 tptrace(TPPTmisc, "tp_euntimeoutLSS ref", refp-tp_ref, seq, 0, 0); 294 ENDTRACE 295 296 p1 = &refp->tpr_calltodo; 297 while ( (p2 = p1->c_next) != 0) { 298 if ((p2->c_func == fun) && SEQ_LT(refp->tpr_pcb, p2->c_arg2, seq)) { 299 if (p2->c_next && p2->c_time > 0) 300 p2->c_next->c_time += p2->c_time; 301 p1->c_next = p2->c_next; 302 p2->c_next = TP_callfree; 303 TP_callfree = p2; 304 IncStat(ts_Ecan_act); 305 continue; 306 } 307 p1 = p2; 308 } 309 } 310 311 /**************** c timers ********************** 312 * 313 * These are not chained together; they sit 314 * in the tp_ref structure. they are the kind that 315 * are typically cancelled so it's faster not to 316 * mess with the chains 317 */ 318 319 /* 320 * CALLED FROM: 321 * the clock, every 500 ms 322 * FUNCTION and ARGUMENTS: 323 * Look for open references with active timers. 324 * If they exist, call the appropriate timer routines to update 325 * the timers and possibly generate events. 326 * (The E timers are done in other procedures; the C timers are 327 * updated here, and events for them are generated here.) 328 */ 329 ProtoHook 330 tp_slowtimo() 331 { 332 register int r,t; 333 struct Ccallout *cp; 334 struct tp_ref *rp = tp_ref; 335 struct tp_event E; 336 int s = splnet(); 337 338 /* check only open reference structures */ 339 IncStat(ts_Cticks); 340 rp++; /* tp_ref[0] is never used */ 341 for( r=1 ; (r <= tp_maxrefopen) ; r++,rp++ ) { 342 if (rp->tpr_state < REF_OPEN) 343 continue; 344 345 /* check the C-type timers */ 346 cp = rp->tpr_callout; 347 for (t=0 ; t < N_CTIMERS; t++,cp++) { 348 if( cp->c_active ) { 349 if( --cp->c_time <= 0 ) { 350 cp->c_active = FALSE; 351 E.ev_number = t; 352 IFDEBUG(D_TIMER) 353 printf("C expired! type 0x%x\n", t); 354 ENDDEBUG 355 IncStat(ts_Cexpired); 356 tp_driver( rp->tpr_pcb, &E); 357 } 358 } 359 } 360 /* now update the list */ 361 tp_Eclock(rp); 362 } 363 splx(s); 364 return 0; 365 } 366 367 /* 368 * CALLED FROM: 369 * tp.trans, tp_emit() 370 * FUNCTION and ARGUMENTS: 371 * Set a C type timer of type (which) to go off after (ticks) time. 372 */ 373 void 374 tp_ctimeout(refp, which, ticks) 375 register struct tp_ref *refp; 376 int which, ticks; 377 { 378 register struct Ccallout *cp = &(refp->tpr_callout[which]); 379 380 IFTRACE(D_TIMER) 381 tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active", 382 (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 383 ENDTRACE 384 if(cp->c_active) 385 IncStat(ts_Ccan_act); 386 IncStat(ts_Cset); 387 cp->c_time = ticks; 388 cp->c_active = TRUE; 389 } 390 391 /* 392 * CALLED FROM: 393 * tp.trans 394 * FUNCTION and ARGUMENTS: 395 * Version of tp_ctimeout that resets the C-type time if the 396 * parameter (ticks) is > the current value of the timer. 397 */ 398 void 399 tp_ctimeout_MIN(refp, which, ticks) 400 register struct tp_ref *refp; 401 int which, ticks; 402 { 403 register struct Ccallout *cp = &(refp->tpr_callout[which]); 404 405 IFTRACE(D_TIMER) 406 tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active", 407 (int)(refp - tp_ref), which, refp->tpr_pcb, cp->c_active); 408 ENDTRACE 409 if(cp->c_active) 410 IncStat(ts_Ccan_act); 411 IncStat(ts_Cset); 412 if( cp->c_active ) 413 cp->c_time = MIN(ticks, cp->c_time); 414 else { 415 cp->c_time = ticks; 416 cp->c_active = TRUE; 417 } 418 } 419 420 /* 421 * CALLED FROM: 422 * tp.trans 423 * FUNCTION and ARGUMENTS: 424 * Cancel the (which) timer in the ref structure indicated by (refp). 425 */ 426 void 427 tp_cuntimeout(refp, which) 428 int which; 429 register struct tp_ref *refp; 430 { 431 register struct Ccallout *cp; 432 433 cp = &(refp->tpr_callout[which]); 434 435 IFDEBUG(D_TIMER) 436 printf("tp_cuntimeout(0x%x, %d) active %d\n", refp, which, cp->c_active); 437 ENDDEBUG 438 439 IFTRACE(D_TIMER) 440 tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp-tp_ref, 441 which, cp->c_active, 0); 442 ENDTRACE 443 444 if(cp->c_active) 445 IncStat(ts_Ccan_act); 446 else 447 IncStat(ts_Ccan_inact); 448 cp->c_active = FALSE; 449 } 450