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