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