Annotation of prex/sys/kern/timer.c, Revision 1.1.1.1
1.1 nbrk 1: /*-
2: * Copyright (c) 2005-2007, 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 <timer.h>
38: #include <irq.h>
39: #include <sched.h>
40: #include <thread.h>
41: #include <kmem.h>
42: #include <exception.h>
43:
44: static volatile u_long lbolt; /* ticks elapsed since bootup */
45:
46: static struct event timer_event; /* event to wakeup a timer thread */
47: static struct event delay_event; /* event for the thread delay */
48: static struct list timer_list; /* list of active timers */
49: static struct list expire_list; /* list of expired timers */
50:
51: static void (*volatile tick_hook)(int); /* hook routine for timer tick */
52:
53: /*
54: * Macro to get a timer element for the next expiration.
55: */
56: #define timer_next() \
57: (list_entry(list_first(&timer_list), struct timer, link))
58:
59: /*
60: * Get remaining ticks to the expiration time.
61: * Return 0 if time already passed.
62: */
63: static u_long
64: time_remain(u_long expire)
65: {
66:
67: if (time_before(lbolt, expire))
68: return expire - lbolt;
69: return 0;
70: }
71:
72: /*
73: * Add a timer element to the timer list in the proper place.
74: * Requires interrupts to be disabled by the caller.
75: */
76: static void
77: timer_add(struct timer *tmr, u_long ticks)
78: {
79: list_t head, n;
80: struct timer *t;
81:
82: tmr->expire = lbolt + ticks;
83:
84: /*
85: * We sort the timer list by time. So, we can
86: * quickly get the next expiration time from
87: * the head element of the timer list.
88: */
89: head = &timer_list;
90: for (n = list_first(head); n != head; n = list_next(n)) {
91: t = list_entry(n, struct timer, link);
92: if (time_before(tmr->expire, t->expire))
93: break;
94: }
95: list_insert(list_prev(n), &tmr->link);
96: }
97:
98: /*
99: * Schedule a callout function to run after a specified
100: * length of time. A device driver can call
101: * timer_callout()/timer_stop() from ISR at interrupt level.
102: */
103: void
104: timer_callout(struct timer *tmr, u_long msec,
105: void (*func)(void *), void *arg)
106: {
107: u_long ticks;
108:
109: ASSERT(tmr);
110:
111: ticks = msec_to_tick(msec);
112: if (ticks == 0)
113: ticks = 1;
114:
115: irq_lock();
116: if (tmr->active)
117: list_remove(&tmr->link);
118: tmr->func = func;
119: tmr->arg = arg;
120: tmr->active = 1;
121: tmr->interval = 0;
122: timer_add(tmr, ticks);
123: irq_unlock();
124: }
125:
126: void
127: timer_stop(struct timer *tmr)
128: {
129: ASSERT(tmr);
130:
131: irq_lock();
132: if (tmr->active) {
133: list_remove(&tmr->link);
134: tmr->active = 0;
135: }
136: irq_unlock();
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 failure.
144: * This service is not available at interrupt level.
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: ASSERT(irq_level == 0);
154:
155: rc = sched_tsleep(&delay_event, msec);
156: if (rc != SLP_TIMEOUT) {
157: tmr = &cur_thread->timeout;
158: remain = tick_to_msec(time_remain(tmr->expire));
159: }
160: return remain;
161: }
162:
163: /*
164: * timer_sleep - sleep system call.
165: *
166: * Stop execution of the current thread for the indicated amount
167: * of time. If the sleep is interrupted, the remaining time
168: * is set in "remain". Returns EINTR if sleep is canceled by
169: * some reasons.
170: */
171: int
172: timer_sleep(u_long msec, u_long *remain)
173: {
174: u_long left;
175: int err = 0;
176:
177: left = timer_delay(msec);
178:
179: if (remain != NULL)
180: err = umem_copyout(&left, remain, sizeof(left));
181: if (err == 0 && left > 0)
182: err = EINTR;
183: return err;
184: }
185:
186: /*
187: * Alarm timer expired:
188: * Send an alarm exception to the target task.
189: */
190: static void
191: alarm_expire(void *arg)
192: {
193:
194: exception_post((task_t)arg, SIGALRM);
195: }
196:
197: /*
198: * timer_alarm - alarm system call.
199: *
200: * SIGALRM exception is sent to the caller task when specified
201: * delay time is passed. If passed time is 0, stop the current
202: * running timer.
203: */
204: int
205: timer_alarm(u_long msec, u_long *remain)
206: {
207: struct timer *tmr;
208: task_t self = cur_task();
209: u_long left = 0;
210: int err = 0;
211:
212: irq_lock();
213: tmr = &self->alarm;
214: if (tmr->active) {
215: /*
216: * Save the remaining time to return
217: * before we update the timer value.
218: */
219: left = tick_to_msec(time_remain(tmr->expire));
220: }
221: if (msec == 0) {
222: timer_stop(tmr);
223: } else {
224: timer_callout(tmr, msec, &alarm_expire, self);
225: }
226: irq_unlock();
227:
228: if (remain != NULL)
229: err = umem_copyout(&left, remain, sizeof(left));
230: return err;
231: }
232:
233: /*
234: * timer_periodic - set periodic timer for the specified thread.
235: *
236: * The periodic thread will 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 th, u_long start, u_long period)
241: {
242: struct timer *tmr;
243: int err = 0;
244:
245: ASSERT(irq_level == 0);
246:
247: if (start != 0 && period == 0)
248: return EINVAL;
249:
250: sched_lock();
251: if (!thread_valid(th)) {
252: sched_unlock();
253: return ESRCH;
254: }
255: if (th->task != cur_task()) {
256: sched_unlock();
257: return EPERM;
258: }
259: tmr = th->periodic;
260: if (start == 0) {
261: if (tmr != NULL && tmr->active)
262: timer_stop(tmr);
263: else
264: err = EINVAL;
265: } else {
266: if (tmr == NULL) {
267: /*
268: * Allocate a timer element at first call.
269: * We don't put this data in the thread
270: * structure because only a few threads
271: * will use the periodic timer function.
272: */
273: tmr = kmem_alloc(sizeof(tmr));
274: if (tmr == NULL) {
275: sched_unlock();
276: return ENOMEM;
277: }
278: event_init(&tmr->event, "periodic");
279: tmr->active = 1;
280: th->periodic = tmr;
281: }
282: /*
283: * Program an interval timer.
284: */
285: irq_lock();
286: tmr->interval = msec_to_tick(period);
287: if (tmr->interval == 0)
288: tmr->interval = 1;
289: timer_add(tmr, msec_to_tick(start));
290: irq_unlock();
291: }
292: sched_unlock();
293: return err;
294: }
295:
296:
297: /*
298: * timer_waitperiod - wait next period of the periodic timer.
299: *
300: * Since this routine can exit by any exceptions, the control
301: * may return at non-period 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, err = 0;
310:
311: ASSERT(irq_level == 0);
312:
313: if ((tmr = cur_thread->periodic) == NULL)
314: return EINVAL;
315:
316: if (time_before(lbolt, tmr->expire)) {
317: /*
318: * Sleep until timer_tick() routine
319: * wakes us up.
320: */
321: rc = sched_sleep(&tmr->event);
322: if (rc != SLP_SUCCESS)
323: err = EINTR;
324: }
325: return err;
326: }
327:
328: /*
329: * Clean up our resource for the thread termination.
330: */
331: void
332: timer_cleanup(thread_t th)
333: {
334:
335: if (th->periodic != NULL) {
336: timer_stop(th->periodic);
337: kmem_free(th->periodic);
338: }
339: }
340:
341: /*
342: * Install a timer hook routine.
343: * We allow only one hook routine in system.
344: */
345: int
346: timer_hook(void (*func)(int))
347: {
348:
349: if (tick_hook != NULL)
350: return -1;
351: irq_lock();
352: tick_hook = func;
353: irq_unlock();
354: return 0;
355: }
356:
357: /*
358: * Timer thread.
359: *
360: * Handle all expired timers. Each callout routine is
361: * called with scheduler locked and interrupts enabled.
362: */
363: static void
364: timer_thread(void *arg)
365: {
366: struct timer *tmr;
367:
368: for (;;) {
369: /* Wait until next timer expiration. */
370: sched_sleep(&timer_event);
371:
372: while (!list_empty(&expire_list)) {
373: /*
374: * Callout
375: */
376: tmr = list_entry(list_first(&expire_list),
377: struct timer, link);
378: list_remove(&tmr->link);
379: tmr->active = 0;
380: sched_lock();
381: interrupt_enable();
382: (*tmr->func)(tmr->arg);
383:
384: /*
385: * Unlock scheduler here in order to give
386: * chance to higher priority threads to run.
387: */
388: sched_unlock();
389: interrupt_disable();
390: }
391: }
392: /* NOTREACHED */
393: }
394:
395: /*
396: * Timer tick handler
397: *
398: * timer_tick() is called straight from the real time
399: * clock interrupt. All interrupts are still disabled
400: * at the entry of this routine.
401: */
402: void
403: timer_tick(void)
404: {
405: struct timer *tmr;
406: u_long ticks;
407: int idle, wakeup = 0;
408:
409: /*
410: * Bump time in ticks.
411: * Note that it is allowed to wrap.
412: */
413: lbolt++;
414:
415: /*
416: * Handle all of the timer elements that have expired.
417: */
418: while (!list_empty(&timer_list)) {
419: /*
420: * Check timer expiration.
421: */
422: tmr = timer_next();
423: if (time_before(lbolt, tmr->expire))
424: break;
425: /*
426: * Remove an expired timer from the list and wakup
427: * the appropriate thread. If it is periodic timer,
428: * reprogram the next expiration time. Otherwise,
429: * it is moved to the expired list.
430: */
431: list_remove(&tmr->link);
432: if (tmr->interval != 0) {
433: /*
434: * Periodic timer
435: */
436: ticks = time_remain(tmr->expire + tmr->interval);
437: if (ticks == 0)
438: ticks = 1;
439: timer_add(tmr, ticks);
440: sched_wakeup(&tmr->event);
441: } else {
442: /*
443: * One-shot timer
444: */
445: list_insert(&expire_list, &tmr->link);
446: wakeup = 1;
447: }
448: }
449: if (wakeup)
450: sched_wakeup(&timer_event);
451:
452: sched_tick();
453:
454: /*
455: * Call a hook routine for power management
456: * or profiling work.
457: */
458: if (tick_hook != NULL) {
459: idle = (cur_thread->prio == PRIO_IDLE) ? 1 : 0;
460: (*tick_hook)(idle);
461: }
462: }
463:
464: u_long
465: timer_count(void)
466: {
467:
468: return lbolt;
469: }
470:
471: void
472: timer_info(struct info_timer *info)
473: {
474:
475: info->hz = HZ;
476: }
477:
478: /*
479: * Initialize the timer facility, called at system startup time.
480: */
481: void
482: timer_init(void)
483: {
484: thread_t th;
485:
486: list_init(&timer_list);
487: list_init(&expire_list);
488: event_init(&timer_event, "timer");
489: event_init(&delay_event, "delay");
490:
491: /* Start timer thread */
492: th = kthread_create(&timer_thread, NULL, PRIO_TIMER);
493: if (th == NULL)
494: panic("timer_init");
495: }
CVSweb