xref: /freebsd/sys/kern/kern_timeout.c (revision 33b77e2decd50e53798014b70bf7ca3bdc4c0c7e)
1 /*-
2  * Copyright (c) 1982, 1986, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)kern_clock.c	8.5 (Berkeley) 1/21/94
39  * $Id: kern_timeout.c,v 1.51 1998/01/11 00:44:31 phk Exp $
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 
46 /* Exported to machdep.c. */
47 struct callout *callout;
48 struct callout_list callfree;
49 int callwheelsize, callwheelbits, callwheelmask;
50 struct callout_tailq *callwheel;
51 
52 static int softticks;			/* Like ticks, but for softclock(). */
53 static struct callout *nextsoftcheck;	/* Next callout to be checked. */
54 
55 /*
56  * The callout mechanism is based on the work of Adam M. Costello and
57  * George Varghese, published in a technical report entitled "Redesigning
58  * the BSD Callout and Timer Facilities" and modified slightly for inclusion
59  * in FreeBSD by Justin T. Gibbs.  The original work on the data structures
60  * used in this implementation was published by G.Varghese and A. Lauck in
61  * the paper "Hashed and Hierarchical Timing Wheels: Data Structures for
62  * the Efficient Implementation of a Timer Facility" in the Proceedings of
63  * the 11th ACM Annual Symposium on Operating Systems Principles,
64  * Austin, Texas Nov 1987.
65  */
66 
67 /*
68  * Software (low priority) clock interrupt.
69  * Run periodic events from timeout queue.
70  */
71 
72 #ifndef MAX_SOFTCLOCK_STEPS
73 #define MAX_SOFTCLOCK_STEPS 100 /* Maximum allowed value of steps. */
74 #endif /* MAX_SOFTCLOCK_STEPS */
75 
76 /*ARGSUSED*/
77 void
78 softclock()
79 {
80 	register struct callout *c;
81 	register struct callout_tailq *bucket;
82 	register int s;
83 	register int curticks;
84 	register int steps;	/*
85 				 * Number of steps taken since
86 				 * we last allowed interrupts.
87 				 */
88 
89 
90 	(void)splsoftclock();
91 
92 	steps = 0;
93 	s = splhigh();
94 	while (softticks != ticks) {
95 		softticks++;
96 		/*
97 		 * softticks may be modified by hard clock, so cache
98 		 * it while we work on a given bucket.
99 		 */
100 		curticks = softticks;
101 		bucket = &callwheel[curticks & callwheelmask];
102 		c = TAILQ_FIRST(bucket);
103 		while (c) {
104 			if (c->c_time != curticks) {
105 				c = TAILQ_NEXT(c, c_links.tqe);
106 				++steps;
107 				if (steps >= MAX_SOFTCLOCK_STEPS) {
108 					nextsoftcheck = c;
109 					/* Give interrupts a chance. */
110 					splx(s);
111 					s = splhigh();
112 					c = nextsoftcheck;
113 					steps = 0;
114 				}
115 			} else {
116 				void (*c_func)(void *);
117 				void *c_arg;
118 
119 				nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
120 				TAILQ_REMOVE(bucket, c, c_links.tqe);
121 				c_func = c->c_func;
122 				c_arg = c->c_arg;
123 				c->c_func = NULL;
124 				SLIST_INSERT_HEAD(&callfree, c, c_links.sle);
125 				splx(s);
126 				c_func(c_arg);
127 				s = splhigh();
128 				steps = 0;
129 				c = nextsoftcheck;
130 			}
131 		}
132 	}
133 	nextsoftcheck = NULL;
134 	splx(s);
135 }
136 
137 /*
138  * timeout --
139  *	Execute a function after a specified length of time.
140  *
141  * untimeout --
142  *	Cancel previous timeout function call.
143  *
144  * callout_handle_init --
145  *	Initialize a handle so that using it with untimeout is benign.
146  *
147  *	See AT&T BCI Driver Reference Manual for specification.  This
148  *	implementation differs from that one in that although an
149  *	identification value is returned from timeout, the original
150  *	arguments to timeout as well as the identifier are used to
151  *	identify entries for untimeout.
152  */
153 struct callout_handle
154 timeout(ftn, arg, to_ticks)
155 	timeout_t ftn;
156 	void *arg;
157 	register int to_ticks;
158 {
159 	int s;
160 	struct callout *new;
161 	struct callout_handle handle;
162 
163 	if (to_ticks <= 0)
164 		to_ticks = 1;
165 
166 	/* Lock out the clock. */
167 	s = splhigh();
168 
169 	/* Fill in the next free callout structure. */
170 	new = SLIST_FIRST(&callfree);
171 	if (new == NULL)
172 		/* XXX Attempt to malloc first */
173 		panic("timeout table full");
174 
175 	SLIST_REMOVE_HEAD(&callfree, c_links.sle);
176 	new->c_arg = arg;
177 	new->c_func = ftn;
178 	new->c_time = ticks + to_ticks;
179 	TAILQ_INSERT_TAIL(&callwheel[new->c_time & callwheelmask],
180 			  new, c_links.tqe);
181 
182 	splx(s);
183 	handle.callout = new;
184 	return (handle);
185 }
186 
187 void
188 untimeout(ftn, arg, handle)
189 	timeout_t ftn;
190 	void *arg;
191 	struct callout_handle handle;
192 {
193 	register int s;
194 
195 	/*
196 	 * Check for a handle that was initialized
197 	 * by callout_handle_init, but never used
198 	 * for a real timeout.
199 	 */
200 	if (handle.callout == NULL)
201 		return;
202 
203 	s = splhigh();
204 	if ((handle.callout->c_func == ftn)
205 	 && (handle.callout->c_arg == arg)) {
206 		if (nextsoftcheck == handle.callout) {
207 			nextsoftcheck = TAILQ_NEXT(handle.callout, c_links.tqe);
208 		}
209 		TAILQ_REMOVE(&callwheel[handle.callout->c_time & callwheelmask],
210 			     handle.callout, c_links.tqe);
211 		handle.callout->c_func = NULL;
212 		SLIST_INSERT_HEAD(&callfree, handle.callout, c_links.sle);
213 	}
214 	splx(s);
215 }
216 
217 void
218 callout_handle_init(struct callout_handle *handle)
219 {
220 	handle->callout = NULL;
221 }
222 
223 #ifdef APM_FIXUP_CALLTODO
224 /*
225  * Adjust the kernel calltodo timeout list.  This routine is used after
226  * an APM resume to recalculate the calltodo timer list values with the
227  * number of hz's we have been sleeping.  The next hardclock() will detect
228  * that there are fired timers and run softclock() to execute them.
229  *
230  * Please note, I have not done an exhaustive analysis of what code this
231  * might break.  I am motivated to have my select()'s and alarm()'s that
232  * have expired during suspend firing upon resume so that the applications
233  * which set the timer can do the maintanence the timer was for as close
234  * as possible to the originally intended time.  Testing this code for a
235  * week showed that resuming from a suspend resulted in 22 to 25 timers
236  * firing, which seemed independant on whether the suspend was 2 hours or
237  * 2 days.  Your milage may vary.   - Ken Key <key@cs.utk.edu>
238  */
239 void
240 adjust_timeout_calltodo(time_change)
241     struct timeval *time_change;
242 {
243 	register struct callout *p;
244 	unsigned long delta_ticks;
245 	int s;
246 
247 	/*
248 	 * How many ticks were we asleep?
249 	 * (stolen from hzto()).
250 	 */
251 
252 	/* Don't do anything */
253 	if (time_change->tv_sec < 0)
254 		return;
255 	else if (time_change->tv_sec <= LONG_MAX / 1000000)
256 		delta_ticks = (time_change->tv_sec * 1000000 +
257 			       time_change->tv_usec + (tick - 1)) / tick + 1;
258 	else if (time_change->tv_sec <= LONG_MAX / hz)
259 		delta_ticks = time_change->tv_sec * hz +
260 			      (time_change->tv_usec + (tick - 1)) / tick + 1;
261 	else
262 		delta_ticks = LONG_MAX;
263 
264 	if (delta_ticks > INT_MAX)
265 		delta_ticks = INT_MAX;
266 
267 	/*
268 	 * Now rip through the timer calltodo list looking for timers
269 	 * to expire.
270 	 */
271 
272 	/* don't collide with softclock() */
273 	s = splhigh();
274 	for (p = calltodo.c_next; p != NULL; p = p->c_next) {
275 		p->c_time -= delta_ticks;
276 
277 		/* Break if the timer had more time on it than delta_ticks */
278 		if (p->c_time > 0)
279 			break;
280 
281 		/* take back the ticks the timer didn't use (p->c_time <= 0) */
282 		delta_ticks = -p->c_time;
283 	}
284 	splx(s);
285 
286 	return;
287 }
288 #endif /* APM_FIXUP_CALLTODO */
289