xref: /freebsd/sys/kern/kern_timeout.c (revision f34fa851e0b97ea3637d73827346927014e1b137)
1df8bae1dSRodney W. Grimes /*-
2df8bae1dSRodney W. Grimes  * Copyright (c) 1982, 1986, 1991, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  * (c) UNIX System Laboratories, Inc.
5df8bae1dSRodney W. Grimes  * All or some portions of this file are derived from material licensed
6df8bae1dSRodney W. Grimes  * to the University of California by American Telephone and Telegraph
7df8bae1dSRodney W. Grimes  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8df8bae1dSRodney W. Grimes  * the permission of UNIX System Laboratories, Inc.
9df8bae1dSRodney W. Grimes  *
10df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
11df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
12df8bae1dSRodney W. Grimes  * are met:
13df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
14df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
15df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
16df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
17df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
18df8bae1dSRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
19df8bae1dSRodney W. Grimes  *    must display the following acknowledgement:
20df8bae1dSRodney W. Grimes  *	This product includes software developed by the University of
21df8bae1dSRodney W. Grimes  *	California, Berkeley and its contributors.
22df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
23df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
24df8bae1dSRodney W. Grimes  *    without specific prior written permission.
25df8bae1dSRodney W. Grimes  *
26df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
37df8bae1dSRodney W. Grimes  *
38acc8326dSGarrett Wollman  *	From: @(#)kern_clock.c	8.5 (Berkeley) 1/21/94
39c3aac50fSPeter Wemm  * $FreeBSD$
40df8bae1dSRodney W. Grimes  */
41df8bae1dSRodney W. Grimes 
42df8bae1dSRodney W. Grimes #include <sys/param.h>
43df8bae1dSRodney W. Grimes #include <sys/systm.h>
4415b7a470SPoul-Henning Kamp #include <sys/callout.h>
45df8bae1dSRodney W. Grimes #include <sys/kernel.h>
46f34fa851SJohn Baldwin #include <sys/lock.h>
47cb799bfeSJohn Baldwin #include <sys/mutex.h>
48df8bae1dSRodney W. Grimes 
4915b7a470SPoul-Henning Kamp /*
5015b7a470SPoul-Henning Kamp  * TODO:
5115b7a470SPoul-Henning Kamp  *	allocate more timeout table slots when table overflows.
5215b7a470SPoul-Henning Kamp  */
5315b7a470SPoul-Henning Kamp 
5415b7a470SPoul-Henning Kamp /* Exported to machdep.c and/or kern_clock.c.  */
55ab36c067SJustin T. Gibbs struct callout *callout;
56ab36c067SJustin T. Gibbs struct callout_list callfree;
57ab36c067SJustin T. Gibbs int callwheelsize, callwheelbits, callwheelmask;
58ab36c067SJustin T. Gibbs struct callout_tailq *callwheel;
5915b7a470SPoul-Henning Kamp int softticks;			/* Like ticks, but for softclock(). */
60fa2fbc3dSJake Burkholder struct mtx callout_lock;
61f23b4c91SGarrett Wollman 
62ab36c067SJustin T. Gibbs static struct callout *nextsoftcheck;	/* Next callout to be checked. */
63df8bae1dSRodney W. Grimes 
64df8bae1dSRodney W. Grimes /*
65ab36c067SJustin T. Gibbs  * The callout mechanism is based on the work of Adam M. Costello and
66ab36c067SJustin T. Gibbs  * George Varghese, published in a technical report entitled "Redesigning
67ab36c067SJustin T. Gibbs  * the BSD Callout and Timer Facilities" and modified slightly for inclusion
68ab36c067SJustin T. Gibbs  * in FreeBSD by Justin T. Gibbs.  The original work on the data structures
69ab36c067SJustin T. Gibbs  * used in this implementation was published by G.Varghese and A. Lauck in
70ab36c067SJustin T. Gibbs  * the paper "Hashed and Hierarchical Timing Wheels: Data Structures for
71ab36c067SJustin T. Gibbs  * the Efficient Implementation of a Timer Facility" in the Proceedings of
72ab36c067SJustin T. Gibbs  * the 11th ACM Annual Symposium on Operating Systems Principles,
73ab36c067SJustin T. Gibbs  * Austin, Texas Nov 1987.
74ab36c067SJustin T. Gibbs  */
75a50ec505SPoul-Henning Kamp 
76ab36c067SJustin T. Gibbs /*
77df8bae1dSRodney W. Grimes  * Software (low priority) clock interrupt.
78df8bae1dSRodney W. Grimes  * Run periodic events from timeout queue.
79df8bae1dSRodney W. Grimes  */
80df8bae1dSRodney W. Grimes void
818088699fSJohn Baldwin softclock(void *dummy)
82df8bae1dSRodney W. Grimes {
83df8bae1dSRodney W. Grimes 	register struct callout *c;
8445327611SJustin T. Gibbs 	register struct callout_tailq *bucket;
85df8bae1dSRodney W. Grimes 	register int s;
8645327611SJustin T. Gibbs 	register int curticks;
8715b7a470SPoul-Henning Kamp 	register int steps;	/* #steps since we last allowed interrupts */
88df8bae1dSRodney W. Grimes 
8915b7a470SPoul-Henning Kamp #ifndef MAX_SOFTCLOCK_STEPS
9015b7a470SPoul-Henning Kamp #define MAX_SOFTCLOCK_STEPS 100 /* Maximum allowed value of steps. */
9115b7a470SPoul-Henning Kamp #endif /* MAX_SOFTCLOCK_STEPS */
92ab36c067SJustin T. Gibbs 
93ab36c067SJustin T. Gibbs 	steps = 0;
94df8bae1dSRodney W. Grimes 	s = splhigh();
959ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
96ab36c067SJustin T. Gibbs 	while (softticks != ticks) {
9745327611SJustin T. Gibbs 		softticks++;
9845327611SJustin T. Gibbs 		/*
9945327611SJustin T. Gibbs 		 * softticks may be modified by hard clock, so cache
10045327611SJustin T. Gibbs 		 * it while we work on a given bucket.
10145327611SJustin T. Gibbs 		 */
10245327611SJustin T. Gibbs 		curticks = softticks;
10345327611SJustin T. Gibbs 		bucket = &callwheel[curticks & callwheelmask];
10445327611SJustin T. Gibbs 		c = TAILQ_FIRST(bucket);
105ab36c067SJustin T. Gibbs 		while (c) {
10645327611SJustin T. Gibbs 			if (c->c_time != curticks) {
107ab36c067SJustin T. Gibbs 				c = TAILQ_NEXT(c, c_links.tqe);
108ab36c067SJustin T. Gibbs 				++steps;
109ab36c067SJustin T. Gibbs 				if (steps >= MAX_SOFTCLOCK_STEPS) {
110ab36c067SJustin T. Gibbs 					nextsoftcheck = c;
11145327611SJustin T. Gibbs 					/* Give interrupts a chance. */
1129ed346baSBosko Milekic 					mtx_unlock_spin(&callout_lock);
113df8bae1dSRodney W. Grimes 					splx(s);
114ab36c067SJustin T. Gibbs 					s = splhigh();
1159ed346baSBosko Milekic 					mtx_lock_spin(&callout_lock);
116ab36c067SJustin T. Gibbs 					c = nextsoftcheck;
117ab36c067SJustin T. Gibbs 					steps = 0;
118df8bae1dSRodney W. Grimes 				}
119ab36c067SJustin T. Gibbs 			} else {
120ab36c067SJustin T. Gibbs 				void (*c_func)(void *);
121ab36c067SJustin T. Gibbs 				void *c_arg;
122fa2fbc3dSJake Burkholder 				int c_flags;
123ab36c067SJustin T. Gibbs 
124ab36c067SJustin T. Gibbs 				nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
12545327611SJustin T. Gibbs 				TAILQ_REMOVE(bucket, c, c_links.tqe);
126ab36c067SJustin T. Gibbs 				c_func = c->c_func;
127ab36c067SJustin T. Gibbs 				c_arg = c->c_arg;
128fa2fbc3dSJake Burkholder 				c_flags = c->c_flags;
129ab36c067SJustin T. Gibbs 				c->c_func = NULL;
130acc8326dSGarrett Wollman 				if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
131acc8326dSGarrett Wollman 					c->c_flags = CALLOUT_LOCAL_ALLOC;
132acc8326dSGarrett Wollman 					SLIST_INSERT_HEAD(&callfree, c,
133acc8326dSGarrett Wollman 							  c_links.sle);
134acc8326dSGarrett Wollman 				} else {
135acc8326dSGarrett Wollman 					c->c_flags =
1369b8b58e0SJonathan Lemon 					    (c->c_flags & ~CALLOUT_PENDING);
137acc8326dSGarrett Wollman 				}
1389ed346baSBosko Milekic 				mtx_unlock_spin(&callout_lock);
139fa2fbc3dSJake Burkholder 				if (!(c_flags & CALLOUT_MPSAFE))
1409ed346baSBosko Milekic 					mtx_lock(&Giant);
141ab36c067SJustin T. Gibbs 				splx(s);
142ab36c067SJustin T. Gibbs 				c_func(c_arg);
143ab36c067SJustin T. Gibbs 				s = splhigh();
144fa2fbc3dSJake Burkholder 				if (!(c_flags & CALLOUT_MPSAFE))
1459ed346baSBosko Milekic 					mtx_unlock(&Giant);
1469ed346baSBosko Milekic 				mtx_lock_spin(&callout_lock);
147ab36c067SJustin T. Gibbs 				steps = 0;
148ab36c067SJustin T. Gibbs 				c = nextsoftcheck;
149ab36c067SJustin T. Gibbs 			}
150ab36c067SJustin T. Gibbs 		}
151ab36c067SJustin T. Gibbs 	}
152ab36c067SJustin T. Gibbs 	nextsoftcheck = NULL;
1539ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
154df8bae1dSRodney W. Grimes 	splx(s);
155df8bae1dSRodney W. Grimes }
156df8bae1dSRodney W. Grimes 
157df8bae1dSRodney W. Grimes /*
158df8bae1dSRodney W. Grimes  * timeout --
159df8bae1dSRodney W. Grimes  *	Execute a function after a specified length of time.
160df8bae1dSRodney W. Grimes  *
161df8bae1dSRodney W. Grimes  * untimeout --
162df8bae1dSRodney W. Grimes  *	Cancel previous timeout function call.
163df8bae1dSRodney W. Grimes  *
164ab36c067SJustin T. Gibbs  * callout_handle_init --
165ab36c067SJustin T. Gibbs  *	Initialize a handle so that using it with untimeout is benign.
166ab36c067SJustin T. Gibbs  *
167df8bae1dSRodney W. Grimes  *	See AT&T BCI Driver Reference Manual for specification.  This
168ab36c067SJustin T. Gibbs  *	implementation differs from that one in that although an
169ab36c067SJustin T. Gibbs  *	identification value is returned from timeout, the original
170ab36c067SJustin T. Gibbs  *	arguments to timeout as well as the identifier are used to
171ab36c067SJustin T. Gibbs  *	identify entries for untimeout.
172df8bae1dSRodney W. Grimes  */
173ab36c067SJustin T. Gibbs struct callout_handle
174ab36c067SJustin T. Gibbs timeout(ftn, arg, to_ticks)
1758f03c6f1SBruce Evans 	timeout_t *ftn;
176df8bae1dSRodney W. Grimes 	void *arg;
177e82ac18eSJonathan Lemon 	int to_ticks;
178df8bae1dSRodney W. Grimes {
179ab36c067SJustin T. Gibbs 	int s;
180ab36c067SJustin T. Gibbs 	struct callout *new;
181ab36c067SJustin T. Gibbs 	struct callout_handle handle;
182df8bae1dSRodney W. Grimes 
183df8bae1dSRodney W. Grimes 	s = splhigh();
1849ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
185df8bae1dSRodney W. Grimes 
186df8bae1dSRodney W. Grimes 	/* Fill in the next free callout structure. */
187ab36c067SJustin T. Gibbs 	new = SLIST_FIRST(&callfree);
188ab36c067SJustin T. Gibbs 	if (new == NULL)
189ab36c067SJustin T. Gibbs 		/* XXX Attempt to malloc first */
190df8bae1dSRodney W. Grimes 		panic("timeout table full");
191ab36c067SJustin T. Gibbs 	SLIST_REMOVE_HEAD(&callfree, c_links.sle);
192df8bae1dSRodney W. Grimes 
193acc8326dSGarrett Wollman 	callout_reset(new, to_ticks, ftn, arg);
194acc8326dSGarrett Wollman 
195ab36c067SJustin T. Gibbs 	handle.callout = new;
1969ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
197acc8326dSGarrett Wollman 	splx(s);
198ab36c067SJustin T. Gibbs 	return (handle);
199df8bae1dSRodney W. Grimes }
200df8bae1dSRodney W. Grimes 
201df8bae1dSRodney W. Grimes void
202ab36c067SJustin T. Gibbs untimeout(ftn, arg, handle)
2038f03c6f1SBruce Evans 	timeout_t *ftn;
204df8bae1dSRodney W. Grimes 	void *arg;
205ab36c067SJustin T. Gibbs 	struct callout_handle handle;
206df8bae1dSRodney W. Grimes {
207df8bae1dSRodney W. Grimes 	register int s;
208df8bae1dSRodney W. Grimes 
209ab36c067SJustin T. Gibbs 	/*
210ab36c067SJustin T. Gibbs 	 * Check for a handle that was initialized
211ab36c067SJustin T. Gibbs 	 * by callout_handle_init, but never used
212ab36c067SJustin T. Gibbs 	 * for a real timeout.
213ab36c067SJustin T. Gibbs 	 */
214ab36c067SJustin T. Gibbs 	if (handle.callout == NULL)
215ab36c067SJustin T. Gibbs 		return;
216df8bae1dSRodney W. Grimes 
217ab36c067SJustin T. Gibbs 	s = splhigh();
2189ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
219acc8326dSGarrett Wollman 	if (handle.callout->c_func == ftn && handle.callout->c_arg == arg)
220acc8326dSGarrett Wollman 		callout_stop(handle.callout);
2219ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
222df8bae1dSRodney W. Grimes 	splx(s);
223df8bae1dSRodney W. Grimes }
224df8bae1dSRodney W. Grimes 
2253c816944SBruce Evans void
226ab36c067SJustin T. Gibbs callout_handle_init(struct callout_handle *handle)
227ab36c067SJustin T. Gibbs {
228ab36c067SJustin T. Gibbs 	handle->callout = NULL;
229ab36c067SJustin T. Gibbs }
230ab36c067SJustin T. Gibbs 
231acc8326dSGarrett Wollman /*
232acc8326dSGarrett Wollman  * New interface; clients allocate their own callout structures.
233acc8326dSGarrett Wollman  *
234acc8326dSGarrett Wollman  * callout_reset() - establish or change a timeout
235acc8326dSGarrett Wollman  * callout_stop() - disestablish a timeout
236acc8326dSGarrett Wollman  * callout_init() - initialize a callout structure so that it can
237acc8326dSGarrett Wollman  *	safely be passed to callout_reset() and callout_stop()
238acc8326dSGarrett Wollman  *
2399b8b58e0SJonathan Lemon  * <sys/callout.h> defines three convenience macros:
240acc8326dSGarrett Wollman  *
2419b8b58e0SJonathan Lemon  * callout_active() - returns truth if callout has not been serviced
2429b8b58e0SJonathan Lemon  * callout_pending() - returns truth if callout is still waiting for timeout
2439b8b58e0SJonathan Lemon  * callout_deactivate() - marks the callout as having been serviced
244acc8326dSGarrett Wollman  */
245acc8326dSGarrett Wollman void
246e82ac18eSJonathan Lemon callout_reset(c, to_ticks, ftn, arg)
247acc8326dSGarrett Wollman 	struct	callout *c;
248acc8326dSGarrett Wollman 	int	to_ticks;
249acc8326dSGarrett Wollman 	void	(*ftn) __P((void *));
250acc8326dSGarrett Wollman 	void	*arg;
251acc8326dSGarrett Wollman {
252acc8326dSGarrett Wollman 	int	s;
253acc8326dSGarrett Wollman 
254acc8326dSGarrett Wollman 	s = splhigh();
2559ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
256acc8326dSGarrett Wollman 	if (c->c_flags & CALLOUT_PENDING)
257acc8326dSGarrett Wollman 		callout_stop(c);
258acc8326dSGarrett Wollman 
259acc8326dSGarrett Wollman 	/*
260acc8326dSGarrett Wollman 	 * We could spl down here and back up at the TAILQ_INSERT_TAIL,
261acc8326dSGarrett Wollman 	 * but there's no point since doing this setup doesn't take much
2629b8b58e0SJonathan Lemon 	 * time.
263acc8326dSGarrett Wollman 	 */
264acc8326dSGarrett Wollman 	if (to_ticks <= 0)
265acc8326dSGarrett Wollman 		to_ticks = 1;
266acc8326dSGarrett Wollman 
267acc8326dSGarrett Wollman 	c->c_arg = arg;
268e82ac18eSJonathan Lemon 	c->c_flags |= (CALLOUT_ACTIVE | CALLOUT_PENDING);
269acc8326dSGarrett Wollman 	c->c_func = ftn;
270acc8326dSGarrett Wollman 	c->c_time = ticks + to_ticks;
271acc8326dSGarrett Wollman 	TAILQ_INSERT_TAIL(&callwheel[c->c_time & callwheelmask],
272acc8326dSGarrett Wollman 			  c, c_links.tqe);
2739ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
274acc8326dSGarrett Wollman 	splx(s);
275acc8326dSGarrett Wollman }
276acc8326dSGarrett Wollman 
277acc8326dSGarrett Wollman void
278acc8326dSGarrett Wollman callout_stop(c)
279acc8326dSGarrett Wollman 	struct	callout *c;
280acc8326dSGarrett Wollman {
281acc8326dSGarrett Wollman 	int	s;
282acc8326dSGarrett Wollman 
283acc8326dSGarrett Wollman 	s = splhigh();
2849ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
285acc8326dSGarrett Wollman 	/*
286acc8326dSGarrett Wollman 	 * Don't attempt to delete a callout that's not on the queue.
287acc8326dSGarrett Wollman 	 */
288acc8326dSGarrett Wollman 	if (!(c->c_flags & CALLOUT_PENDING)) {
2899b8b58e0SJonathan Lemon 		c->c_flags &= ~CALLOUT_ACTIVE;
2909ed346baSBosko Milekic 		mtx_unlock_spin(&callout_lock);
291acc8326dSGarrett Wollman 		splx(s);
292acc8326dSGarrett Wollman 		return;
293acc8326dSGarrett Wollman 	}
2949b8b58e0SJonathan Lemon 	c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING);
295acc8326dSGarrett Wollman 
296acc8326dSGarrett Wollman 	if (nextsoftcheck == c) {
297acc8326dSGarrett Wollman 		nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
298acc8326dSGarrett Wollman 	}
299acc8326dSGarrett Wollman 	TAILQ_REMOVE(&callwheel[c->c_time & callwheelmask], c, c_links.tqe);
300acc8326dSGarrett Wollman 	c->c_func = NULL;
301acc8326dSGarrett Wollman 
302acc8326dSGarrett Wollman 	if (c->c_flags & CALLOUT_LOCAL_ALLOC) {
303acc8326dSGarrett Wollman 		SLIST_INSERT_HEAD(&callfree, c, c_links.sle);
304acc8326dSGarrett Wollman 	}
3059ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
306acc8326dSGarrett Wollman 	splx(s);
307acc8326dSGarrett Wollman }
308acc8326dSGarrett Wollman 
309acc8326dSGarrett Wollman void
310e82ac18eSJonathan Lemon callout_init(c, mpsafe)
311acc8326dSGarrett Wollman 	struct	callout *c;
312e82ac18eSJonathan Lemon 	int mpsafe;
313acc8326dSGarrett Wollman {
3147347e1c6SGarrett Wollman 	bzero(c, sizeof *c);
315e82ac18eSJonathan Lemon 	if (mpsafe)
316e82ac18eSJonathan Lemon 		c->c_flags |= CALLOUT_MPSAFE;
317acc8326dSGarrett Wollman }
318acc8326dSGarrett Wollman 
319e1d6dc65SNate Williams #ifdef APM_FIXUP_CALLTODO
320e1d6dc65SNate Williams /*
321e1d6dc65SNate Williams  * Adjust the kernel calltodo timeout list.  This routine is used after
322e1d6dc65SNate Williams  * an APM resume to recalculate the calltodo timer list values with the
323e1d6dc65SNate Williams  * number of hz's we have been sleeping.  The next hardclock() will detect
324e1d6dc65SNate Williams  * that there are fired timers and run softclock() to execute them.
325e1d6dc65SNate Williams  *
326e1d6dc65SNate Williams  * Please note, I have not done an exhaustive analysis of what code this
327e1d6dc65SNate Williams  * might break.  I am motivated to have my select()'s and alarm()'s that
328e1d6dc65SNate Williams  * have expired during suspend firing upon resume so that the applications
329e1d6dc65SNate Williams  * which set the timer can do the maintanence the timer was for as close
330e1d6dc65SNate Williams  * as possible to the originally intended time.  Testing this code for a
331e1d6dc65SNate Williams  * week showed that resuming from a suspend resulted in 22 to 25 timers
332e1d6dc65SNate Williams  * firing, which seemed independant on whether the suspend was 2 hours or
333e1d6dc65SNate Williams  * 2 days.  Your milage may vary.   - Ken Key <key@cs.utk.edu>
334e1d6dc65SNate Williams  */
335e1d6dc65SNate Williams void
336e1d6dc65SNate Williams adjust_timeout_calltodo(time_change)
337e1d6dc65SNate Williams     struct timeval *time_change;
338e1d6dc65SNate Williams {
339e1d6dc65SNate Williams 	register struct callout *p;
340e1d6dc65SNate Williams 	unsigned long delta_ticks;
341e1d6dc65SNate Williams 	int s;
342e1d6dc65SNate Williams 
343e1d6dc65SNate Williams 	/*
344e1d6dc65SNate Williams 	 * How many ticks were we asleep?
345c8b47828SBruce Evans 	 * (stolen from tvtohz()).
346e1d6dc65SNate Williams 	 */
347e1d6dc65SNate Williams 
348e1d6dc65SNate Williams 	/* Don't do anything */
349e1d6dc65SNate Williams 	if (time_change->tv_sec < 0)
350e1d6dc65SNate Williams 		return;
351e1d6dc65SNate Williams 	else if (time_change->tv_sec <= LONG_MAX / 1000000)
352e1d6dc65SNate Williams 		delta_ticks = (time_change->tv_sec * 1000000 +
353e1d6dc65SNate Williams 			       time_change->tv_usec + (tick - 1)) / tick + 1;
354e1d6dc65SNate Williams 	else if (time_change->tv_sec <= LONG_MAX / hz)
355e1d6dc65SNate Williams 		delta_ticks = time_change->tv_sec * hz +
356e1d6dc65SNate Williams 			      (time_change->tv_usec + (tick - 1)) / tick + 1;
357e1d6dc65SNate Williams 	else
358e1d6dc65SNate Williams 		delta_ticks = LONG_MAX;
359e1d6dc65SNate Williams 
360e1d6dc65SNate Williams 	if (delta_ticks > INT_MAX)
361e1d6dc65SNate Williams 		delta_ticks = INT_MAX;
362e1d6dc65SNate Williams 
363e1d6dc65SNate Williams 	/*
364e1d6dc65SNate Williams 	 * Now rip through the timer calltodo list looking for timers
365e1d6dc65SNate Williams 	 * to expire.
366e1d6dc65SNate Williams 	 */
367e1d6dc65SNate Williams 
368e1d6dc65SNate Williams 	/* don't collide with softclock() */
369e1d6dc65SNate Williams 	s = splhigh();
3709ed346baSBosko Milekic 	mtx_lock_spin(&callout_lock);
371e1d6dc65SNate Williams 	for (p = calltodo.c_next; p != NULL; p = p->c_next) {
372e1d6dc65SNate Williams 		p->c_time -= delta_ticks;
373e1d6dc65SNate Williams 
374e1d6dc65SNate Williams 		/* Break if the timer had more time on it than delta_ticks */
375e1d6dc65SNate Williams 		if (p->c_time > 0)
376e1d6dc65SNate Williams 			break;
377e1d6dc65SNate Williams 
378e1d6dc65SNate Williams 		/* take back the ticks the timer didn't use (p->c_time <= 0) */
379e1d6dc65SNate Williams 		delta_ticks = -p->c_time;
380e1d6dc65SNate Williams 	}
3819ed346baSBosko Milekic 	mtx_unlock_spin(&callout_lock);
382e1d6dc65SNate Williams 	splx(s);
383e1d6dc65SNate Williams 
384e1d6dc65SNate Williams 	return;
385e1d6dc65SNate Williams }
386e1d6dc65SNate Williams #endif /* APM_FIXUP_CALLTODO */
387