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