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