1/*	$NetBSD: tp_timer.c,v 1.18 2007/03/04 06:03:33 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)tp_timer.c	8.1 (Berkeley) 6/10/93
32 */
33
34/***********************************************************
35		Copyright IBM Corporation 1987
36
37                      All Rights Reserved
38
39Permission to use, copy, modify, and distribute this software and its
40documentation for any purpose and without fee is hereby granted,
41provided that the above copyright notice appear in all copies and that
42both that copyright notice and this permission notice appear in
43supporting documentation, and that the name of IBM not be
44used in advertising or publicity pertaining to distribution of the
45software without specific, written prior permission.
46
47IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53SOFTWARE.
54
55******************************************************************/
56
57/*
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
59 */
60
61#include <sys/cdefs.h>
62__KERNEL_RCSID(0, "$NetBSD: tp_timer.c,v 1.18 2007/03/04 06:03:33 christos Exp $");
63
64#include <sys/param.h>
65#include <sys/systm.h>
66#include <sys/time.h>
67#include <sys/malloc.h>
68#include <sys/protosw.h>
69#include <sys/socket.h>
70#include <sys/kernel.h>
71
72#include <netiso/argo_debug.h>
73#include <netiso/tp_param.h>
74#include <netiso/tp_timer.h>
75#include <netiso/tp_stat.h>
76#include <netiso/tp_pcb.h>
77#include <netiso/tp_tpdu.h>
78#include <netiso/tp_trace.h>
79#include <netiso/tp_seq.h>
80#include <netiso/tp_var.h>
81
82struct tp_ref  *tp_ref;
83int             tp_rttdiv, tp_rttadd, N_TPREF = 127;
84struct tp_refinfo tp_refinfo;
85struct tp_pcb  *tp_ftimeolist = (struct tp_pcb *) & tp_ftimeolist;
86
87/*
88 * CALLED FROM:
89 *  at autoconfig time from tp_init()
90 * 	a combo of event, state, predicate
91 * FUNCTION and ARGUMENTS:
92 *  initialize data structures for the timers
93 */
94void
95tp_timerinit(void)
96{
97	int    s;
98	/*
99	 * Initialize storage
100	 */
101	if (tp_refinfo.tpr_base)
102		return;
103	tp_refinfo.tpr_size = N_TPREF + 1;	/* Need to start somewhere */
104	s = sizeof(*tp_ref) * tp_refinfo.tpr_size;
105	if ((tp_ref = (struct tp_ref *) malloc(s, M_PCB, M_NOWAIT|M_ZERO)) == 0)
106		panic("tp_timerinit");
107	tp_refinfo.tpr_base = tp_ref;
108	tp_rttdiv = hz / PR_SLOWHZ;
109	tp_rttadd = (2 * tp_rttdiv) - 1;
110}
111#ifdef TP_DEBUG_TIMERS
112/**********************  e timers *************************/
113
114/*
115 * CALLED FROM:
116 *  tp.trans all over
117 * FUNCTION and ARGUMENTS:
118 * Set an E type timer.
119 */
120void
121tp_etimeout(
122	struct tp_pcb *tpcb,
123	int             fun,	/* function to be called */
124	int             ticks)
125{
126
127	u_int *callp;
128#ifdef ARGO_DEBUG
129	if (argo_debug[D_TIMER]) {
130		printf("etimeout pcb %p state 0x%x\n", tpcb, tpcb->tp_state);
131	}
132#endif
133#ifdef TPPT
134	if (tp_traceflags[D_TIMER]) {
135		tptrace(TPPTmisc, "tp_etimeout ref refstate tks Etick", tpcb->tp_lref,
136			tpcb->tp_state, ticks, tp_stat.ts_Eticks);
137	}
138#endif
139	if (tpcb == 0)
140		return;
141	IncStat(ts_Eset);
142	if (ticks == 0)
143		ticks = 1;
144	callp = tpcb->tp_timer + fun;
145	if (*callp == 0 || *callp > ticks)
146		*callp = ticks;
147}
148
149/*
150 * CALLED FROM:
151 *  tp.trans all over
152 * FUNCTION and ARGUMENTS:
153 *  Cancel all occurrences of E-timer function (fun) for reference (refp)
154 */
155void
156tp_euntimeout(struct tp_pcb *tpcb, int fun)
157{
158#ifdef TPPT
159	if (tp_traceflags[D_TIMER]) {
160		tptrace(TPPTmisc, "tp_euntimeout ref", tpcb->tp_lref, 0, 0, 0);
161	}
162#endif
163
164	if (tpcb)
165		tpcb->tp_timer[fun] = 0;
166}
167
168/****************  c timers **********************
169 *
170 * These are not chained together; they sit
171 * in the tp_ref structure. they are the kind that
172 * are typically cancelled so it's faster not to
173 * mess with the chains
174 */
175#endif
176/*
177 * CALLED FROM:
178 *  the clock, every 500 ms
179 * FUNCTION and ARGUMENTS:
180 *  Look for open references with active timers.
181 *  If they exist, call the appropriate timer routines to update
182 *  the timers and possibly generate events.
183 */
184void
185tp_slowtimo(void)
186{
187	u_int *cp;
188	struct tp_ref *rp;
189	struct tp_pcb  *tpcb;
190	struct tp_event E;
191	int             t;
192
193	mutex_enter(softnet_lock);
194	KERNEL_LOCK(1, NULL);
195	/* check only open reference structures */
196	IncStat(ts_Cticks);
197	/* tp_ref[0] is never used */
198	for (rp = tp_ref + tp_refinfo.tpr_maxopen; rp > tp_ref; rp--) {
199		if ((tpcb = rp->tpr_pcb) == 0 || tpcb->tp_refstate < REF_OPEN)
200			continue;
201		/* check the timers */
202		for (t = 0; t < TM_NTIMERS; t++) {
203			cp = tpcb->tp_timer + t;
204			if (*cp && --(*cp) <= 0) {
205				*cp = 0;
206				E.ev_number = t;
207#ifdef ARGO_DEBUG
208				if (argo_debug[D_TIMER]) {
209					printf("tp_slowtimo: pcb %p t %d\n",
210					       tpcb, t);
211				}
212#endif
213				IncStat(ts_Cexpired);
214				tp_driver(tpcb, &E);
215				if (t == TM_reference && tpcb->tp_state == TP_CLOSED) {
216					if (tpcb->tp_notdetached) {
217#ifdef ARGO_DEBUG
218						if (argo_debug[D_CONN]) {
219							printf("PRU_DETACH: not detached\n");
220						}
221#endif
222						tp_detach(tpcb);
223					}
224					/* XXX wart; where else to do it? */
225					free((void *) tpcb, M_PCB);
226					break;
227				}
228			}
229		}
230	}
231	KERNEL_UNLOCK_ONE(NULL);
232	mutex_exit(softnet_lock);
233}
234
235/*
236 * Called From: tp.trans from tp_slowtimo() -- retransmission timer went off.
237 */
238void
239tp_data_retrans(struct tp_pcb *tpcb)
240{
241	int             rexmt, win;
242	tpcb->tp_rttemit = 0;	/* cancel current round trip time */
243	tpcb->tp_dupacks = 0;
244	tpcb->tp_sndnxt = tpcb->tp_snduna;
245	if (tpcb->tp_fcredit == 0) {
246		/*
247		 * We transmitted new data, started timing it and the window
248		 * got shrunk under us.  This can only happen if all data
249		 * that they wanted us to send got acked, so don't
250		 * bother shrinking the congestion windows, et. al.
251		 * The retransmission timer should have been reset in goodack()
252		 */
253#ifdef ARGO_DEBUG
254		if (argo_debug[D_ACKRECV]) {
255			printf("tp_data_retrans: 0 window tpcb %p una 0x%x\n",
256			       tpcb, tpcb->tp_snduna);
257		}
258#endif
259		tpcb->tp_rxtshift = 0;
260		tpcb->tp_timer[TM_data_retrans] = 0;
261		tpcb->tp_timer[TM_sendack] = tpcb->tp_dt_ticks;
262		return;
263	}
264	rexmt = tpcb->tp_dt_ticks << min(tpcb->tp_rxtshift, TP_MAXRXTSHIFT);
265	win = min(tpcb->tp_fcredit, (tpcb->tp_cong_win / tpcb->tp_l_tpdusize / 2));
266	win = max(win, 2);
267	tpcb->tp_cong_win = tpcb->tp_l_tpdusize;	/* slow start again. */
268	tpcb->tp_ssthresh = win * tpcb->tp_l_tpdusize;
269	/*
270	 * We're losing; our srtt estimate is probably bogus. Clobber it so
271	 * we'll take the next rtt measurement as our srtt; Maintain current
272	 * rxt times until then.
273	 */
274	if (++tpcb->tp_rxtshift > TP_NRETRANS / 4) {
275		/* tpcb->tp_nlprotosw->nlp_losing(tpcb->tp_npcb) someday */
276		tpcb->tp_rtt = 0;
277	}
278	TP_RANGESET(tpcb->tp_rxtcur, rexmt, tpcb->tp_peer_acktime, 128);
279	tpcb->tp_timer[TM_data_retrans] = tpcb->tp_rxtcur;
280	tp_send(tpcb);
281}
282
283void
284tp_fasttimo(void)
285{
286	struct tp_pcb *t;
287	struct tp_event E;
288
289	mutex_enter(softnet_lock);
290	KERNEL_LOCK(1, NULL);
291	E.ev_number = TM_sendack;
292	while ((t = tp_ftimeolist) != (struct tp_pcb *) & tp_ftimeolist) {
293		if (t == 0) {
294			printf("tp_fasttimeo: should panic");
295			tp_ftimeolist = (struct tp_pcb *) & tp_ftimeolist;
296		} else {
297			if (t->tp_flags & TPF_DELACK) {
298				IncStat(ts_Fdelack);
299				tp_driver(t, &E);
300				t->tp_flags &= ~TPF_DELACK;
301			} else
302				IncStat(ts_Fpruned);
303			tp_ftimeolist = t->tp_fasttimeo;
304			t->tp_fasttimeo = 0;
305		}
306	}
307	KERNEL_UNLOCK_ONE(NULL);
308	mutex_exit(softnet_lock);
309}
310
311#ifdef TP_DEBUG_TIMERS
312/*
313 * CALLED FROM:
314 *  tp.trans, tp_emit()
315 * FUNCTION and ARGUMENTS:
316 * 	Set a C type timer of type (which) to go off after (ticks) time.
317 */
318void
319tp_ctimeout(struct tp_pcb *tpcb, int which, int ticks)
320{
321
322#ifdef TPPT
323	if (tp_traceflags[D_TIMER]) {
324		tptrace(TPPTmisc, "tp_ctimeout ref which tpcb active",
325			tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
326	}
327#endif
328	if (tpcb->tp_timer[which])
329		IncStat(ts_Ccan_act);
330	IncStat(ts_Cset);
331	if (ticks <= 0)
332		ticks = 1;
333	tpcb->tp_timer[which] = ticks;
334}
335
336/*
337 * CALLED FROM:
338 *  tp.trans
339 * FUNCTION and ARGUMENTS:
340 * 	Version of tp_ctimeout that resets the C-type time if the
341 * 	parameter (ticks) is > the current value of the timer.
342 */
343void
344tp_ctimeout_MIN(struct tp_pcb *tpcb, int which, int ticks)
345{
346#ifdef TPPT
347	if (tp_traceflags[D_TIMER]) {
348		tptrace(TPPTmisc, "tp_ctimeout_MIN ref which tpcb active",
349			tpcb->tp_lref, which, tpcb, tpcb->tp_timer[which]);
350	}
351#endif
352		IncStat(ts_Cset);
353	if (tpcb->tp_timer[which]) {
354		tpcb->tp_timer[which] = min(ticks, tpcb->tp_timer[which]);
355		IncStat(ts_Ccan_act);
356	} else
357		tpcb->tp_timer[which] = ticks;
358}
359
360/*
361 * CALLED FROM:
362 *  tp.trans
363 * FUNCTION and ARGUMENTS:
364 *  Cancel the (which) timer in the ref structure indicated by (refp).
365 */
366void
367tp_cuntimeout(struct tp_pcb *tpcb, int which)
368{
369#ifdef ARGO_DEBUG
370	if (argo_debug[D_TIMER]) {
371		printf("tp_cuntimeout(%p, %d) active %d\n",
372		       tpcb, which, tpcb->tp_timer[which]);
373	}
374#endif
375
376#ifdef TPPT
377	if (tp_traceflags[D_TIMER]) {
378		tptrace(TPPTmisc, "tp_cuntimeout ref which, active", refp - tp_ref,
379			which, tpcb->tp_timer[which], 0);
380	}
381#endif
382
383	if (tpcb->tp_timer[which])
384		IncStat(ts_Ccan_act);
385	else
386		IncStat(ts_Ccan_inact);
387	tpcb->tp_timer[which] = 0;
388}
389#endif
390