scoutos / prex-0.9.0 / sys / kern / timer.c @ 03e9c04a
History | View | Annotate | Download (9.38 KB)
1 | 03e9c04a | Brad Neuman | /*-
|
---|---|---|---|
2 | * Copyright (c) 2005-2009, Kohsuke Ohtani
|
||
3 | * All rights reserved.
|
||
4 | *
|
||
5 | * Redistribution and use in source and binary forms, with or without
|
||
6 | * modification, are permitted provided that the following conditions
|
||
7 | * are met:
|
||
8 | * 1. Redistributions of source code must retain the above copyright
|
||
9 | * notice, this list of conditions and the following disclaimer.
|
||
10 | * 2. Redistributions in binary form must reproduce the above copyright
|
||
11 | * notice, this list of conditions and the following disclaimer in the
|
||
12 | * documentation and/or other materials provided with the distribution.
|
||
13 | * 3. Neither the name of the author nor the names of any co-contributors
|
||
14 | * may be used to endorse or promote products derived from this software
|
||
15 | * without specific prior written permission.
|
||
16 | *
|
||
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
27 | * SUCH DAMAGE.
|
||
28 | */
|
||
29 | |||
30 | /*
|
||
31 | * timer.c - kernel timer services.
|
||
32 | */
|
||
33 | |||
34 | #include <kernel.h> |
||
35 | #include <task.h> |
||
36 | #include <event.h> |
||
37 | #include <sched.h> |
||
38 | #include <thread.h> |
||
39 | #include <kmem.h> |
||
40 | #include <exception.h> |
||
41 | #include <timer.h> |
||
42 | #include <sys/signal.h> |
||
43 | |||
44 | static volatile u_long lbolt; /* ticks elapsed since bootup */ |
||
45 | static volatile u_long idle_ticks; /* total ticks for idle */ |
||
46 | |||
47 | static struct event timer_event; /* event to wakeup a timer thread */ |
||
48 | static struct event delay_event; /* event for the thread delay */ |
||
49 | static struct list timer_list; /* list of active timers */ |
||
50 | static struct list expire_list; /* list of expired timers */ |
||
51 | |||
52 | /*
|
||
53 | * Get remaining ticks to the expiration time.
|
||
54 | * Return 0 if timer has been expired.
|
||
55 | */
|
||
56 | static u_long
|
||
57 | time_remain(u_long expire) |
||
58 | { |
||
59 | |||
60 | if (time_before(lbolt, expire))
|
||
61 | return expire - lbolt;
|
||
62 | return 0; |
||
63 | } |
||
64 | |||
65 | /*
|
||
66 | * Activate a timer.
|
||
67 | */
|
||
68 | static void |
||
69 | timer_add(struct timer *tmr, u_long ticks)
|
||
70 | { |
||
71 | list_t head, n; |
||
72 | struct timer *t;
|
||
73 | |||
74 | if (ticks == 0) |
||
75 | ticks++; |
||
76 | |||
77 | tmr->expire = lbolt + ticks; |
||
78 | tmr->state = TM_ACTIVE; |
||
79 | |||
80 | /*
|
||
81 | * Insert a timer element into the timer list which
|
||
82 | * is sorted by expiration time.
|
||
83 | */
|
||
84 | head = &timer_list; |
||
85 | for (n = list_first(head); n != head; n = list_next(n)) {
|
||
86 | t = list_entry(n, struct timer, link);
|
||
87 | if (time_before(tmr->expire, t->expire))
|
||
88 | break;
|
||
89 | } |
||
90 | list_insert(list_prev(n), &tmr->link); |
||
91 | } |
||
92 | |||
93 | /*
|
||
94 | * Stop an active timer.
|
||
95 | */
|
||
96 | void
|
||
97 | timer_stop(struct timer *tmr)
|
||
98 | { |
||
99 | int s;
|
||
100 | |||
101 | ASSERT(tmr != NULL);
|
||
102 | |||
103 | s = splhigh(); |
||
104 | if (tmr->state == TM_ACTIVE) {
|
||
105 | list_remove(&tmr->link); |
||
106 | tmr->state = TM_STOP; |
||
107 | } |
||
108 | splx(s); |
||
109 | } |
||
110 | |||
111 | /*
|
||
112 | * Schedule a callout function to run after a specified
|
||
113 | * length of time.
|
||
114 | *
|
||
115 | * Note: A device driver can call timer_callout() or
|
||
116 | * timer_stop() from ISR at interrupt level.
|
||
117 | */
|
||
118 | void
|
||
119 | timer_callout(struct timer *tmr, u_long msec, void (*fn)(void *), void *arg) |
||
120 | { |
||
121 | int s;
|
||
122 | |||
123 | ASSERT(tmr != NULL);
|
||
124 | ASSERT(fn != NULL);
|
||
125 | |||
126 | s = splhigh(); |
||
127 | |||
128 | if (tmr->state == TM_ACTIVE)
|
||
129 | list_remove(&tmr->link); |
||
130 | |||
131 | tmr->func = fn; |
||
132 | tmr->arg = arg; |
||
133 | tmr->interval = 0;
|
||
134 | timer_add(tmr, mstohz(msec)); |
||
135 | |||
136 | splx(s); |
||
137 | } |
||
138 | |||
139 | /*
|
||
140 | * timer_delay - delay thread execution.
|
||
141 | *
|
||
142 | * The caller thread is blocked for the specified time.
|
||
143 | * Returns 0 on success, or the remaining time (msec) on
|
||
144 | * failure.
|
||
145 | */
|
||
146 | u_long |
||
147 | timer_delay(u_long msec) |
||
148 | { |
||
149 | struct timer *tmr;
|
||
150 | u_long remain = 0;
|
||
151 | int rc;
|
||
152 | |||
153 | rc = sched_tsleep(&delay_event, msec); |
||
154 | if (rc != SLP_TIMEOUT) {
|
||
155 | tmr = &curthread->timeout; |
||
156 | remain = hztoms(time_remain(tmr->expire)); |
||
157 | } |
||
158 | return remain;
|
||
159 | } |
||
160 | |||
161 | /*
|
||
162 | * timer_sleep - sleep system call.
|
||
163 | *
|
||
164 | * Stop execution of the current thread for the indicated amount
|
||
165 | * of time. If the sleep is interrupted, the remaining time is
|
||
166 | * set in "remain".
|
||
167 | */
|
||
168 | int
|
||
169 | timer_sleep(u_long msec, u_long *remain) |
||
170 | { |
||
171 | u_long left; |
||
172 | |||
173 | left = timer_delay(msec); |
||
174 | |||
175 | if (remain != NULL) { |
||
176 | if (copyout(&left, remain, sizeof(left))) |
||
177 | return EFAULT;
|
||
178 | } |
||
179 | if (left > 0) |
||
180 | return EINTR;
|
||
181 | return 0; |
||
182 | } |
||
183 | |||
184 | /*
|
||
185 | * Alarm timer expired:
|
||
186 | * Send an alarm exception to the target task.
|
||
187 | */
|
||
188 | static void |
||
189 | alarm_expire(void *arg)
|
||
190 | { |
||
191 | task_t task = (task_t)arg; |
||
192 | |||
193 | exception_post(task, SIGALRM); |
||
194 | } |
||
195 | |||
196 | /*
|
||
197 | * timer_alarm - alarm system call.
|
||
198 | *
|
||
199 | * SIGALRM exception is sent to the caller task when specified
|
||
200 | * delay time is passed. If "msec" argument is 0, stop the
|
||
201 | * current running timer.
|
||
202 | */
|
||
203 | int
|
||
204 | timer_alarm(u_long msec, u_long *remain) |
||
205 | { |
||
206 | struct timer *tmr;
|
||
207 | u_long left = 0;
|
||
208 | int s;
|
||
209 | |||
210 | s = splhigh(); |
||
211 | tmr = &curtask->alarm; |
||
212 | |||
213 | /*
|
||
214 | * If the timer is active, save the remaining time
|
||
215 | * before we update the timer setting.
|
||
216 | */
|
||
217 | if (tmr->state == TM_ACTIVE)
|
||
218 | left = hztoms(time_remain(tmr->expire)); |
||
219 | |||
220 | if (msec == 0) |
||
221 | timer_stop(tmr); |
||
222 | else
|
||
223 | timer_callout(tmr, msec, &alarm_expire, curtask); |
||
224 | |||
225 | splx(s); |
||
226 | if (remain != NULL) { |
||
227 | if (copyout(&left, remain, sizeof(left))) |
||
228 | return EFAULT;
|
||
229 | } |
||
230 | return 0; |
||
231 | } |
||
232 | |||
233 | /*
|
||
234 | * timer_periodic - set periodic timer for the specified thread.
|
||
235 | *
|
||
236 | * The periodic thread can wait the timer period by calling
|
||
237 | * timer_waitperiod(). The unit of start/period is milli-seconds.
|
||
238 | */
|
||
239 | int
|
||
240 | timer_periodic(thread_t t, u_long start, u_long period) |
||
241 | { |
||
242 | struct timer *tmr;
|
||
243 | int s;
|
||
244 | |||
245 | if (start != 0 && period == 0) |
||
246 | return EINVAL;
|
||
247 | |||
248 | sched_lock(); |
||
249 | if (!thread_valid(t)) {
|
||
250 | sched_unlock(); |
||
251 | return ESRCH;
|
||
252 | } |
||
253 | if (t->task != curtask) {
|
||
254 | sched_unlock(); |
||
255 | return EPERM;
|
||
256 | } |
||
257 | |||
258 | tmr = t->periodic; |
||
259 | if (start == 0) { |
||
260 | /*
|
||
261 | * Cancel periodic timer.
|
||
262 | */
|
||
263 | if (tmr == NULL || tmr->state != TM_ACTIVE) { |
||
264 | sched_unlock(); |
||
265 | return EINVAL;
|
||
266 | } |
||
267 | timer_stop(tmr); |
||
268 | } else {
|
||
269 | if (tmr == NULL) { |
||
270 | /*
|
||
271 | * Allocate a timer element at first call.
|
||
272 | * This is to save the data area in the thread
|
||
273 | * structure.
|
||
274 | */
|
||
275 | if ((tmr = kmem_alloc(sizeof(tmr))) == NULL) { |
||
276 | sched_unlock(); |
||
277 | return ENOMEM;
|
||
278 | } |
||
279 | memset(tmr, 0, sizeof(*tmr)); |
||
280 | event_init(&tmr->event, "periodic");
|
||
281 | t->periodic = tmr; |
||
282 | } |
||
283 | /*
|
||
284 | * Program an interval timer.
|
||
285 | */
|
||
286 | s = splhigh(); |
||
287 | tmr->interval = mstohz(period); |
||
288 | if (tmr->interval == 0) |
||
289 | tmr->interval = 1;
|
||
290 | timer_add(tmr, mstohz(start)); |
||
291 | splx(s); |
||
292 | } |
||
293 | sched_unlock(); |
||
294 | return 0; |
||
295 | } |
||
296 | |||
297 | /*
|
||
298 | * timer_waitperiod - wait next period of the periodic timer.
|
||
299 | *
|
||
300 | * If the caller task receives any exception, this system call
|
||
301 | * will return before target time. So, the caller must retry
|
||
302 | * immediately if the error status is EINTR. This will be
|
||
303 | * automatically done by the library stub routine.
|
||
304 | */
|
||
305 | int
|
||
306 | timer_waitperiod(void)
|
||
307 | { |
||
308 | struct timer *tmr;
|
||
309 | int rc;
|
||
310 | |||
311 | tmr = curthread->periodic; |
||
312 | if (tmr == NULL || tmr->state != TM_ACTIVE) |
||
313 | return EINVAL;
|
||
314 | |||
315 | if (time_before(lbolt, tmr->expire)) {
|
||
316 | /*
|
||
317 | * Sleep until timer_handler() routine wakes us up.
|
||
318 | */
|
||
319 | rc = sched_sleep(&tmr->event); |
||
320 | if (rc != SLP_SUCCESS)
|
||
321 | return EINTR;
|
||
322 | } |
||
323 | return 0; |
||
324 | } |
||
325 | |||
326 | /*
|
||
327 | * Untimeout the timers for the thread termination.
|
||
328 | */
|
||
329 | void
|
||
330 | timer_cancel(thread_t t) |
||
331 | { |
||
332 | |||
333 | if (t->periodic != NULL) { |
||
334 | timer_stop(t->periodic); |
||
335 | kmem_free(t->periodic); |
||
336 | t->periodic = NULL;
|
||
337 | } |
||
338 | } |
||
339 | |||
340 | /*
|
||
341 | * Timer thread.
|
||
342 | *
|
||
343 | * Handle all expired timers. Each callout routine is
|
||
344 | * called with scheduler locked and interrupts enabled.
|
||
345 | */
|
||
346 | static void |
||
347 | timer_thread(void *dummy)
|
||
348 | { |
||
349 | struct timer *tmr;
|
||
350 | |||
351 | splhigh(); |
||
352 | |||
353 | for (;;) {
|
||
354 | /*
|
||
355 | * Wait until next timer expiration.
|
||
356 | */
|
||
357 | sched_sleep(&timer_event); |
||
358 | |||
359 | while (!list_empty(&expire_list)) {
|
||
360 | /*
|
||
361 | * callout
|
||
362 | */
|
||
363 | tmr = timer_next(&expire_list); |
||
364 | list_remove(&tmr->link); |
||
365 | tmr->state = TM_STOP; |
||
366 | sched_lock(); |
||
367 | spl0(); |
||
368 | (*tmr->func)(tmr->arg); |
||
369 | |||
370 | /*
|
||
371 | * Unlock scheduler here in order to give
|
||
372 | * chance to higher priority threads to run.
|
||
373 | */
|
||
374 | sched_unlock(); |
||
375 | splhigh(); |
||
376 | } |
||
377 | } |
||
378 | /* NOTREACHED */
|
||
379 | } |
||
380 | |||
381 | /*
|
||
382 | * Handle clock interrupts.
|
||
383 | *
|
||
384 | * timer_handler() is called directly from the real time clock
|
||
385 | * interrupt. All interrupts are still disabled at the entry
|
||
386 | * of this routine.
|
||
387 | */
|
||
388 | void
|
||
389 | timer_handler(void)
|
||
390 | { |
||
391 | struct timer *tmr;
|
||
392 | u_long ticks; |
||
393 | int wakeup = 0; |
||
394 | |||
395 | /*
|
||
396 | * Bump time in ticks.
|
||
397 | * Note that it is allowed to wrap.
|
||
398 | */
|
||
399 | lbolt++; |
||
400 | if (curthread->priority == PRI_IDLE)
|
||
401 | idle_ticks++; |
||
402 | |||
403 | while (!list_empty(&timer_list)) {
|
||
404 | /*
|
||
405 | * Check timer expiration.
|
||
406 | */
|
||
407 | tmr = timer_next(&timer_list); |
||
408 | if (time_before(lbolt, tmr->expire))
|
||
409 | break;
|
||
410 | |||
411 | list_remove(&tmr->link); |
||
412 | if (tmr->interval != 0) { |
||
413 | /*
|
||
414 | * Periodic timer - reprogram timer again.
|
||
415 | */
|
||
416 | ticks = time_remain(tmr->expire + tmr->interval); |
||
417 | timer_add(tmr, ticks); |
||
418 | sched_wakeup(&tmr->event); |
||
419 | } else {
|
||
420 | /*
|
||
421 | * One-shot timer
|
||
422 | */
|
||
423 | list_insert(&expire_list, &tmr->link); |
||
424 | wakeup = 1;
|
||
425 | } |
||
426 | } |
||
427 | if (wakeup)
|
||
428 | sched_wakeup(&timer_event); |
||
429 | |||
430 | sched_tick(); |
||
431 | } |
||
432 | |||
433 | /*
|
||
434 | * Return ticks since boot.
|
||
435 | */
|
||
436 | u_long |
||
437 | timer_ticks(void)
|
||
438 | { |
||
439 | |||
440 | return lbolt;
|
||
441 | } |
||
442 | |||
443 | /*
|
||
444 | * Return timer information.
|
||
445 | */
|
||
446 | void
|
||
447 | timer_info(struct timerinfo *info)
|
||
448 | { |
||
449 | |||
450 | info->hz = HZ; |
||
451 | info->cputicks = lbolt; |
||
452 | info->idleticks = idle_ticks; |
||
453 | } |
||
454 | |||
455 | /*
|
||
456 | * Initialize the timer facility, called at system startup time.
|
||
457 | */
|
||
458 | void
|
||
459 | timer_init(void)
|
||
460 | { |
||
461 | |||
462 | event_init(&timer_event, "timer");
|
||
463 | event_init(&delay_event, "delay");
|
||
464 | list_init(&timer_list); |
||
465 | list_init(&expire_list); |
||
466 | |||
467 | if (kthread_create(&timer_thread, NULL, PRI_TIMER) == NULL) |
||
468 | panic("timer_init");
|
||
469 | } |