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