[BACK]Return to timer.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / kern

Annotation of prex-old/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: 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