[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     ! 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