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