xref: /csrg-svn/sys/netiso/tp_timer.c (revision 50940)
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