Annotation of prex/sys/kern/timer.c, Revision 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