[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.2.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:
1.1.1.1.2.1! nbrk       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 */
1.1       nbrk       52:
                     53: /*
1.1.1.1.2.1! nbrk       54:  * Macro to get a timer element for the next expiration.
1.1       nbrk       55:  */
                     56: #define timer_next() \
                     57:        (list_entry(list_first(&timer_list), struct timer, link))
                     58:
                     59: /*
1.1.1.1.2.1! nbrk       60:  * Get remaining ticks to the expiration time.
1.1       nbrk       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: /*
1.1.1.1.2.1! nbrk       73:  * Add a timer element to the timer list in the proper place.
1.1       nbrk       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:        /*
1.1.1.1.2.1! nbrk       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.
1.1       nbrk       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: /*
1.1.1.1.2.1! nbrk       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.
1.1       nbrk      102:  */
                    103: void
1.1.1.1.2.1! nbrk      104: timer_callout(struct timer *tmr, u_long msec,
        !           105:              void (*func)(void *), void *arg)
1.1       nbrk      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.
1.1.1.1.2.1! nbrk      141:  *
1.1       nbrk      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:  *
1.1.1.1.2.1! nbrk      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.
1.1       nbrk      170:  */
                    171: int
1.1.1.1.2.1! nbrk      172: timer_sleep(u_long msec, u_long *remain)
1.1       nbrk      173: {
1.1.1.1.2.1! nbrk      174:        u_long left;
1.1       nbrk      175:        int err = 0;
                    176:
1.1.1.1.2.1! nbrk      177:        left = timer_delay(msec);
1.1       nbrk      178:
                    179:        if (remain != NULL)
1.1.1.1.2.1! nbrk      180:                err = umem_copyout(&left, remain, sizeof(left));
        !           181:        if (err == 0 && left > 0)
1.1       nbrk      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
1.1.1.1.2.1! nbrk      191: alarm_expire(void *arg)
1.1       nbrk      192: {
                    193:
1.1.1.1.2.1! nbrk      194:        exception_post((task_t)arg, SIGALRM);
1.1       nbrk      195: }
                    196:
                    197: /*
1.1.1.1.2.1! nbrk      198:  * timer_alarm - alarm system call.
1.1       nbrk      199:  *
1.1.1.1.2.1! nbrk      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.
1.1       nbrk      203:  */
                    204: int
1.1.1.1.2.1! nbrk      205: timer_alarm(u_long msec, u_long *remain)
1.1       nbrk      206: {
                    207:        struct timer *tmr;
1.1.1.1.2.1! nbrk      208:        task_t self = cur_task();
        !           209:        u_long left = 0;
1.1       nbrk      210:        int err = 0;
                    211:
                    212:        irq_lock();
1.1.1.1.2.1! nbrk      213:        tmr = &self->alarm;
1.1       nbrk      214:        if (tmr->active) {
                    215:                /*
1.1.1.1.2.1! nbrk      216:                 * Save the remaining time to return
        !           217:                 * before we update the timer value.
1.1       nbrk      218:                 */
1.1.1.1.2.1! nbrk      219:                left = tick_to_msec(time_remain(tmr->expire));
1.1       nbrk      220:        }
1.1.1.1.2.1! nbrk      221:        if (msec == 0) {
1.1       nbrk      222:                timer_stop(tmr);
                    223:        } else {
1.1.1.1.2.1! nbrk      224:                timer_callout(tmr, msec, &alarm_expire, self);
1.1       nbrk      225:        }
                    226:        irq_unlock();
                    227:
                    228:        if (remain != NULL)
1.1.1.1.2.1! nbrk      229:                err = umem_copyout(&left, remain, sizeof(left));
1.1       nbrk      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
1.1.1.1.2.1! nbrk      237:  * timer_waitperiod(). The unit of start/period is milli-seconds.
1.1       nbrk      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:                        /*
1.1.1.1.2.1! nbrk      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.
1.1       nbrk      272:                         */
1.1.1.1.2.1! nbrk      273:                        tmr = kmem_alloc(sizeof(tmr));
1.1       nbrk      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:  *
1.1.1.1.2.1! nbrk      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.
1.1       nbrk      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:                /*
1.1.1.1.2.1! nbrk      318:                 * Sleep until timer_tick() routine
        !           319:                 * wakes us up.
1.1       nbrk      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: {
1.1.1.1.2.1! nbrk      334:
1.1       nbrk      335:        if (th->periodic != NULL) {
                    336:                timer_stop(th->periodic);
                    337:                kmem_free(th->periodic);
                    338:        }
                    339: }
                    340:
1.1.1.1.2.1! nbrk      341: /*
        !           342:  * Install a timer hook routine.
        !           343:  * We allow only one hook routine in system.
        !           344:  */
1.1       nbrk      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:  *
1.1.1.1.2.1! nbrk      360:  * Handle all expired timers. Each callout routine is
        !           361:  * called with scheduler locked and interrupts enabled.
1.1       nbrk      362:  */
                    363: static void
1.1.1.1.2.1! nbrk      364: timer_thread(void *arg)
1.1       nbrk      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:  *
1.1.1.1.2.1! nbrk      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.
1.1       nbrk      401:  */
                    402: void
                    403: timer_tick(void)
                    404: {
                    405:        struct timer *tmr;
                    406:        u_long ticks;
                    407:        int idle, wakeup = 0;
                    408:
1.1.1.1.2.1! nbrk      409:        /*
        !           410:         * Bump time in ticks.
        !           411:         * Note that it is allowed to wrap.
        !           412:         */
1.1       nbrk      413:        lbolt++;
                    414:
                    415:        /*
                    416:         * Handle all of the timer elements that have expired.
                    417:         */
1.1.1.1.2.1! nbrk      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;
1.1       nbrk      425:                /*
                    426:                 * Remove an expired timer from the list and wakup
                    427:                 * the appropriate thread. If it is periodic timer,
1.1.1.1.2.1! nbrk      428:                 * reprogram the next expiration time. Otherwise,
1.1       nbrk      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:        /*
1.1.1.1.2.1! nbrk      455:         * Call a hook routine for power management
        !           456:         * or profiling work.
1.1       nbrk      457:         */
                    458:        if (tick_hook != NULL) {
                    459:                idle = (cur_thread->prio == PRIO_IDLE) ? 1 : 0;
1.1.1.1.2.1! nbrk      460:                (*tick_hook)(idle);
1.1       nbrk      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: {
1.1.1.1.2.1! nbrk      484:        thread_t th;
1.1       nbrk      485:
                    486:        list_init(&timer_list);
                    487:        list_init(&expire_list);
                    488:        event_init(&timer_event, "timer");
                    489:        event_init(&delay_event, "delay");
                    490:
1.1.1.1.2.1! nbrk      491:        /* Start timer thread */
        !           492:        th = kthread_create(&timer_thread, NULL, PRIO_TIMER);
        !           493:        if (th == NULL)
1.1       nbrk      494:                panic("timer_init");
                    495: }

CVSweb