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