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