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