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

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