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

Annotation of prex/sys/kern/sched.c, Revision 1.1

1.1     ! nbrk        1: /*-
        !             2:  * Copyright (c) 2005-2008, 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:  * sched.c - scheduler
        !            32:  */
        !            33:
        !            34: /**
        !            35:  * General design:
        !            36:  *
        !            37:  * The Prex scheduler is based on the algorithm known as priority
        !            38:  * based multi level queue. Each thread has its own priority
        !            39:  * assigned between 0 and 255. The lower number means higher
        !            40:  * priority like BSD unix.  The scheduler maintains 256 level run
        !            41:  * queues mapped to each priority.  The lowest priority (=255) is
        !            42:  * used only for an idle thread.
        !            43:  *
        !            44:  * All threads have two different types of priorities:
        !            45:  *
        !            46:  *  Base priority:
        !            47:  *      This is a static priority used for priority computation.
        !            48:  *      A user mode program can change this value via system call.
        !            49:  *
        !            50:  *  Current priority:
        !            51:  *      An actual scheduling priority. A kernel may adjust this
        !            52:  *      priority dynamically if it's needed.
        !            53:  *
        !            54:  * Each thread has one of the following state.
        !            55:  *
        !            56:  *  - TH_RUN     Running or ready to run
        !            57:  *  - TH_SLEEP   Sleep for some event
        !            58:  *  - TH_SUSPEND Suspend count is not 0
        !            59:  *  - TH_EXIT    Terminated
        !            60:  *
        !            61:  * The thread is always preemptive even in the kernel mode.
        !            62:  * There are following 4 reasons to switch thread.
        !            63:  *
        !            64:  * (1) Block
        !            65:  *      Thread is blocked for sleep or suspend.
        !            66:  *      It is put on the tail of the run queue when it becomes
        !            67:  *      runnable again.
        !            68:  *
        !            69:  * (2) Preemption
        !            70:  *      If higher priority thread becomes runnable, the current
        !            71:  *      thread is put on the _head_ of the run queue.
        !            72:  *
        !            73:  * (3) Quantum expiration
        !            74:  *      If the thread consumes its time quantum, it is put on
        !            75:  *      the tail of the run queue.
        !            76:  *
        !            77:  * (4) Yield
        !            78:  *      If the thread releases CPU by itself, it is put on the
        !            79:  *      tail of the run queue.
        !            80:  *
        !            81:  * There are following three types of scheduling policies.
        !            82:  *
        !            83:  *  - SCHED_FIFO   First in-first-out
        !            84:  *  - SCHED_RR     Round robin (SCHED_FIFO + timeslice)
        !            85:  *  - SCHED_OTHER  Another scheduling (not supported)
        !            86:  */
        !            87:
        !            88: #include <kernel.h>
        !            89: #include <queue.h>
        !            90: #include <event.h>
        !            91: #include <irq.h>
        !            92: #include <thread.h>
        !            93: #include <timer.h>
        !            94: #include <vm.h>
        !            95: #include <task.h>
        !            96: #include <system.h>
        !            97: #include <sched.h>
        !            98:
        !            99: static struct queue    runq[NPRIO];    /* run queues */
        !           100: static struct queue    wakeq;          /* queue for waking threads */
        !           101: static struct queue    dpcq;           /* DPC queue */
        !           102: static int             top_prio;       /* highest priority in runq */
        !           103: static struct event    dpc_event;      /* event for DPC */
        !           104:
        !           105: /*
        !           106:  * Search for highest-priority runnable thread.
        !           107:  */
        !           108: static int
        !           109: runq_top(void)
        !           110: {
        !           111:        int prio;
        !           112:
        !           113:        for (prio = 0; prio < MIN_PRIO; prio++)
        !           114:                if (!queue_empty(&runq[prio]))
        !           115:                        break;
        !           116:        return prio;
        !           117: }
        !           118:
        !           119: /*
        !           120:  * Put a thread on the tail of the run queue.
        !           121:  */
        !           122: static void
        !           123: runq_enqueue(thread_t th)
        !           124: {
        !           125:
        !           126:        enqueue(&runq[th->prio], &th->link);
        !           127:        if (th->prio < top_prio) {
        !           128:                top_prio = th->prio;
        !           129:                cur_thread->resched = 1;
        !           130:        }
        !           131: }
        !           132:
        !           133: /*
        !           134:  * Insert a thread to the head of the run queue.
        !           135:  * We assume this routine is called while thread switching.
        !           136:  */
        !           137: static void
        !           138: runq_insert(thread_t th)
        !           139: {
        !           140:
        !           141:        queue_insert(&runq[th->prio], &th->link);
        !           142:        if (th->prio < top_prio)
        !           143:                top_prio = th->prio;
        !           144: }
        !           145:
        !           146: /*
        !           147:  * Pick up and remove the highest-priority thread
        !           148:  * from the run queue.
        !           149:  */
        !           150: static thread_t
        !           151: runq_dequeue(void)
        !           152: {
        !           153:        queue_t q;
        !           154:        thread_t th;
        !           155:
        !           156:        q = dequeue(&runq[top_prio]);
        !           157:        th = queue_entry(q, struct thread, link);
        !           158:        if (queue_empty(&runq[top_prio]))
        !           159:                top_prio = runq_top();
        !           160:        return th;
        !           161: }
        !           162:
        !           163: /*
        !           164:  * Remove the specified thread from the run queue.
        !           165:  */
        !           166: static void
        !           167: runq_remove(thread_t th)
        !           168: {
        !           169:
        !           170:        queue_remove(&th->link);
        !           171:        top_prio = runq_top();
        !           172: }
        !           173:
        !           174: /*
        !           175:  * Process all pending woken threads.
        !           176:  */
        !           177: static void
        !           178: wakeq_flush(void)
        !           179: {
        !           180:        queue_t q;
        !           181:        thread_t th;
        !           182:
        !           183:        while (!queue_empty(&wakeq)) {
        !           184:                /*
        !           185:                 * Set a thread runnable.
        !           186:                 */
        !           187:                q = dequeue(&wakeq);
        !           188:                th = queue_entry(q, struct thread, link);
        !           189:                th->slpevt = NULL;
        !           190:                th->state &= ~TH_SLEEP;
        !           191:                if (th != cur_thread && th->state == TH_RUN)
        !           192:                        runq_enqueue(th);
        !           193:        }
        !           194: }
        !           195:
        !           196: /*
        !           197:  * sched_switch - this is the scheduler proper:
        !           198:  *
        !           199:  * If the scheduling reason is preemption, the current
        !           200:  * thread will remain at the head of the run queue.  So,
        !           201:  * the thread still has right to run first again among
        !           202:  * the same priority threads. For other scheduling reason,
        !           203:  * the current thread is inserted into the tail of the run
        !           204:  * queue.
        !           205:  */
        !           206: static void
        !           207: sched_switch(void)
        !           208: {
        !           209:        thread_t prev, next;
        !           210:
        !           211:        /*
        !           212:         * Move a current thread to the run queue.
        !           213:         */
        !           214:        prev = cur_thread;
        !           215:        if (prev->state == TH_RUN) {
        !           216:                if (prev->prio > top_prio)
        !           217:                        runq_insert(prev);      /* preemption */
        !           218:                else
        !           219:                        runq_enqueue(prev);
        !           220:        }
        !           221:        prev->resched = 0;
        !           222:
        !           223:        /*
        !           224:         * Select the thread to run the CPU next.
        !           225:         */
        !           226:        next = runq_dequeue();
        !           227:        if (next == prev)
        !           228:                return;
        !           229:        cur_thread = next;
        !           230:
        !           231:        /*
        !           232:         * Switch to the new thread.
        !           233:         * You are expected to understand this..
        !           234:         */
        !           235:        if (prev->task != next->task)
        !           236:                vm_switch(next->task->map);
        !           237:        context_switch(&prev->ctx, &next->ctx);
        !           238: }
        !           239:
        !           240: /*
        !           241:  * sleep_expire - sleep timer is expired:
        !           242:  *
        !           243:  * Wake up the thread which is sleeping in sched_tsleep().
        !           244:  */
        !           245: static void
        !           246: sleep_expire(void *arg)
        !           247: {
        !           248:
        !           249:        sched_unsleep((thread_t)arg, SLP_TIMEOUT);
        !           250: }
        !           251:
        !           252: /*
        !           253:  * sched_tsleep - sleep the current thread until a wakeup
        !           254:  * is performed on the specified event.
        !           255:  *
        !           256:  * This routine returns a sleep result. If the thread is
        !           257:  * woken by sched_wakeup() or sched_wakeone(), it returns 0.
        !           258:  * Otherwise, it will return the result value which is passed
        !           259:  * by sched_unsleep().  We allow calling sched_sleep() with
        !           260:  * interrupt disabled.
        !           261:  *
        !           262:  * sched_sleep() is also defined as a wrapper macro for
        !           263:  * sched_tsleep() without timeout. Note that all sleep
        !           264:  * requests are interruptible with this kernel.
        !           265:  */
        !           266: int
        !           267: sched_tsleep(struct event *evt, u_long msec)
        !           268: {
        !           269:
        !           270:        ASSERT(irq_level == 0);
        !           271:        ASSERT(evt);
        !           272:
        !           273:        sched_lock();
        !           274:        irq_lock();
        !           275:
        !           276:        cur_thread->slpevt = evt;
        !           277:        cur_thread->state |= TH_SLEEP;
        !           278:        enqueue(&evt->sleepq, &cur_thread->link);
        !           279:
        !           280:        if (msec != 0) {
        !           281:                /*
        !           282:                 * Program timer to wake us up at timeout.
        !           283:                 */
        !           284:                timer_callout(&cur_thread->timeout, msec, &sleep_expire,
        !           285:                              cur_thread);
        !           286:        }
        !           287:        wakeq_flush();
        !           288:        sched_switch(); /* Sleep here. Zzzz.. */
        !           289:
        !           290:        irq_unlock();
        !           291:        sched_unlock();
        !           292:        return cur_thread->slpret;
        !           293: }
        !           294:
        !           295: /*
        !           296:  * sched_wakeup - wake up all threads sleeping on event.
        !           297:  *
        !           298:  * A thread can have sleep and suspend state simultaneously.
        !           299:  * So, the thread does not always run even if it woke up.
        !           300:  *
        !           301:  * Since this routine can be called from ISR at interrupt
        !           302:  * level, there may be contention for access to some data.
        !           303:  * Thus, this routine will temporary move the waking thread
        !           304:  * into wakeq, and they will be moved to runq at more safer
        !           305:  * time in wakeq_flush().
        !           306:  *
        !           307:  * The woken thread will be put on the tail of runq
        !           308:  * regardless of its scheduling policy. If woken threads have
        !           309:  * same priority, next running thread is selected by FIFO order.
        !           310:  */
        !           311: void
        !           312: sched_wakeup(struct event *evt)
        !           313: {
        !           314:        queue_t q;
        !           315:        thread_t th;
        !           316:
        !           317:        ASSERT(evt);
        !           318:
        !           319:        sched_lock();
        !           320:        irq_lock();
        !           321:        while (!queue_empty(&evt->sleepq)) {
        !           322:                /*
        !           323:                 * Move a sleeping thread to the wake queue.
        !           324:                 */
        !           325:                q = dequeue(&evt->sleepq);
        !           326:                th = queue_entry(q, struct thread, link);
        !           327:                th->slpret = 0;
        !           328:                enqueue(&wakeq, q);
        !           329:                timer_stop(&th->timeout);
        !           330:        }
        !           331:        irq_unlock();
        !           332:        sched_unlock();
        !           333: }
        !           334:
        !           335: /*
        !           336:  * sched_wakeone - wake up one thread sleeping on event.
        !           337:  *
        !           338:  * The highest priority thread is woken among sleeping
        !           339:  * threads. This routine returns the thread ID of the
        !           340:  * woken thread, or NULL if no threads are sleeping.
        !           341:  */
        !           342: thread_t
        !           343: sched_wakeone(struct event *evt)
        !           344: {
        !           345:        queue_t head, q;
        !           346:        thread_t top, th = NULL;
        !           347:
        !           348:        sched_lock();
        !           349:        irq_lock();
        !           350:        head = &evt->sleepq;
        !           351:        if (!queue_empty(head)) {
        !           352:                /*
        !           353:                 * Select the highet priority thread in
        !           354:                 * the sleep queue, and wakeup it.
        !           355:                 */
        !           356:                q = queue_first(head);
        !           357:                top = queue_entry(q, struct thread, link);
        !           358:                while (!queue_end(head, q)) {
        !           359:                        th = queue_entry(q, struct thread, link);
        !           360:                        if (th->prio < top->prio)
        !           361:                                top = th;
        !           362:                        q = queue_next(q);
        !           363:                }
        !           364:                queue_remove(&top->link);
        !           365:                top->slpret = 0;
        !           366:                enqueue(&wakeq, &top->link);
        !           367:                timer_stop(&top->timeout);
        !           368:                th = top;
        !           369:        }
        !           370:        irq_unlock();
        !           371:        sched_unlock();
        !           372:        return th;
        !           373: }
        !           374:
        !           375: /*
        !           376:  * sched_unsleep - cancel sleep.
        !           377:  *
        !           378:  * sched_unsleep() removes the specified thread from its
        !           379:  * sleep queue. The specified sleep result will be passed
        !           380:  * to the sleeping thread as a return value of sched_tsleep().
        !           381:  */
        !           382: void
        !           383: sched_unsleep(thread_t th, int result)
        !           384: {
        !           385:
        !           386:        sched_lock();
        !           387:        if (th->state & TH_SLEEP) {
        !           388:                irq_lock();
        !           389:                queue_remove(&th->link);
        !           390:                th->slpret = result;
        !           391:                enqueue(&wakeq, &th->link);
        !           392:                timer_stop(&th->timeout);
        !           393:                irq_unlock();
        !           394:        }
        !           395:        sched_unlock();
        !           396: }
        !           397:
        !           398: /*
        !           399:  * Yield the current processor to another thread.
        !           400:  *
        !           401:  * Note that the current thread may run immediately again,
        !           402:  * if no other thread exists in the same priority queue.
        !           403:  */
        !           404: void
        !           405: sched_yield(void)
        !           406: {
        !           407:
        !           408:        sched_lock();
        !           409:
        !           410:        if (!queue_empty(&runq[cur_thread->prio]))
        !           411:                cur_thread->resched = 1;
        !           412:
        !           413:        sched_unlock(); /* Switch current thread here */
        !           414: }
        !           415:
        !           416: /*
        !           417:  * Suspend the specified thread.
        !           418:  * Called with scheduler locked.
        !           419:  */
        !           420: void
        !           421: sched_suspend(thread_t th)
        !           422: {
        !           423:
        !           424:        if (th->state == TH_RUN) {
        !           425:                if (th == cur_thread)
        !           426:                        cur_thread->resched = 1;
        !           427:                else
        !           428:                        runq_remove(th);
        !           429:        }
        !           430:        th->state |= TH_SUSPEND;
        !           431: }
        !           432:
        !           433: /*
        !           434:  * Resume the specified thread.
        !           435:  * Called with scheduler locked.
        !           436:  */
        !           437: void
        !           438: sched_resume(thread_t th)
        !           439: {
        !           440:
        !           441:        if (th->state & TH_SUSPEND) {
        !           442:                th->state &= ~TH_SUSPEND;
        !           443:                if (th->state == TH_RUN)
        !           444:                        runq_enqueue(th);
        !           445:        }
        !           446: }
        !           447:
        !           448: /*
        !           449:  * sched_tick() is called from timer_tick() once every tick.
        !           450:  * Check quantum expiration, and mark a rescheduling flag.
        !           451:  * We don't need locking in here.
        !           452:  */
        !           453: void
        !           454: sched_tick(void)
        !           455: {
        !           456:
        !           457:        /* Profile running time. */
        !           458:        cur_thread->time++;
        !           459:
        !           460:        if (cur_thread->policy == SCHED_RR) {
        !           461:                if (--cur_thread->timeleft <= 0) {
        !           462:                        /*
        !           463:                         * The quantum is up.
        !           464:                         * Give the thread another.
        !           465:                         */
        !           466:                        cur_thread->timeleft += QUANTUM;
        !           467:                        cur_thread->resched = 1;
        !           468:                }
        !           469:        }
        !           470: }
        !           471:
        !           472: /*
        !           473:  * Set up stuff for thread scheduling.
        !           474:  */
        !           475: void
        !           476: sched_start(thread_t th)
        !           477: {
        !           478:
        !           479:        th->state = TH_RUN | TH_SUSPEND;
        !           480:        th->policy = SCHED_RR;
        !           481:        th->prio = PRIO_USER;
        !           482:        th->baseprio = PRIO_USER;
        !           483:        th->timeleft = QUANTUM;
        !           484: }
        !           485:
        !           486: /*
        !           487:  * Stop thread scheduling.
        !           488:  */
        !           489: void
        !           490: sched_stop(thread_t th)
        !           491: {
        !           492:
        !           493:        if (th == cur_thread) {
        !           494:                /*
        !           495:                 * If specified thread is current thread,
        !           496:                 * the scheduling lock count is force set
        !           497:                 * to 1 to ensure the thread switching in
        !           498:                 * the next sched_unlock().
        !           499:                 */
        !           500:                cur_thread->locks = 1;
        !           501:                cur_thread->resched = 1;
        !           502:        } else {
        !           503:                if (th->state == TH_RUN)
        !           504:                        runq_remove(th);
        !           505:                else if (th->state & TH_SLEEP)
        !           506:                        queue_remove(&th->link);
        !           507:        }
        !           508:        timer_stop(&th->timeout);
        !           509:        th->state = TH_EXIT;
        !           510: }
        !           511:
        !           512: /*
        !           513:  * sched_lock - lock the scheduler.
        !           514:  *
        !           515:  * The thread switch is disabled during scheduler locked.
        !           516:  * This is mainly used to synchronize the thread execution
        !           517:  * to protect global resources. Even when scheduler is
        !           518:  * locked, an interrupt handler can run. So, we have to
        !           519:  * use irq_lock() to synchronize a global data with ISR.
        !           520:  *
        !           521:  * Since the scheduling lock can be nested any number of
        !           522:  * times, the caller has the responsible to unlock the same
        !           523:  * number of locks.
        !           524:  */
        !           525: void
        !           526: sched_lock(void)
        !           527: {
        !           528:
        !           529:        cur_thread->locks++;
        !           530: }
        !           531:
        !           532: /*
        !           533:  * sched_unlock - unlock scheduler.
        !           534:  *
        !           535:  * If nobody locks the scheduler anymore, it checks the
        !           536:  * rescheduling flag and kick scheduler if it's marked.
        !           537:  *
        !           538:  * Note that this routine will be always called at the end
        !           539:  * of each interrupt handler.
        !           540:  */
        !           541: void
        !           542: sched_unlock(void)
        !           543: {
        !           544:        int s;
        !           545:
        !           546:        ASSERT(cur_thread->locks > 0);
        !           547:
        !           548:        interrupt_save(&s);
        !           549:        interrupt_disable();
        !           550:
        !           551:        if (cur_thread->locks == 1) {
        !           552:                wakeq_flush();
        !           553:                while (cur_thread->resched) {
        !           554:
        !           555:                        /* Kick scheduler */
        !           556:                        sched_switch();
        !           557:
        !           558:                        /*
        !           559:                         * Now we run pending interrupts which fired
        !           560:                         * during the thread switch. So, we can catch
        !           561:                         * the rescheduling request from such ISRs.
        !           562:                         * Otherwise, the reschedule may be deferred
        !           563:                         * until _next_ sched_unlock() call.
        !           564:                         */
        !           565:                        interrupt_restore(s);
        !           566:                        interrupt_disable();
        !           567:                        wakeq_flush();
        !           568:                }
        !           569:        }
        !           570:        cur_thread->locks--;
        !           571:
        !           572:        interrupt_restore(s);
        !           573: }
        !           574:
        !           575: int
        !           576: sched_getprio(thread_t th)
        !           577: {
        !           578:
        !           579:        return th->prio;
        !           580: }
        !           581:
        !           582: /*
        !           583:  * sched_setprio - set priority of thread.
        !           584:  *
        !           585:  * The rescheduling flag is set if the priority is
        !           586:  * better than the currently running thread.
        !           587:  * Called with scheduler locked.
        !           588:  */
        !           589: void
        !           590: sched_setprio(thread_t th, int baseprio, int prio)
        !           591: {
        !           592:
        !           593:        th->baseprio = baseprio;
        !           594:
        !           595:        if (th == cur_thread) {
        !           596:                /*
        !           597:                 * If we change the current thread's priority,
        !           598:                 * rescheduling may be happened.
        !           599:                 */
        !           600:                th->prio = prio;
        !           601:                top_prio = runq_top();
        !           602:                if (prio != top_prio)
        !           603:                        cur_thread->resched = 1;
        !           604:        } else {
        !           605:                if (th->state == TH_RUN) {
        !           606:                        /*
        !           607:                         * Update the thread priority and adjust
        !           608:                         * the run queue position for new priority.
        !           609:                         */
        !           610:                        runq_remove(th);
        !           611:                        th->prio = prio;
        !           612:                        runq_enqueue(th);
        !           613:                } else
        !           614:                        th->prio = prio;
        !           615:        }
        !           616: }
        !           617:
        !           618: int
        !           619: sched_getpolicy(thread_t th)
        !           620: {
        !           621:
        !           622:        return th->policy;
        !           623: }
        !           624:
        !           625: int
        !           626: sched_setpolicy(thread_t th, int policy)
        !           627: {
        !           628:        int err = 0;
        !           629:
        !           630:        switch (policy) {
        !           631:        case SCHED_RR:
        !           632:        case SCHED_FIFO:
        !           633:                th->timeleft = QUANTUM;
        !           634:                th->policy = policy;
        !           635:                break;
        !           636:        default:
        !           637:                err = -1;
        !           638:                break;
        !           639:        }
        !           640:        return err;
        !           641: }
        !           642:
        !           643: /*
        !           644:  * Schedule DPC callback.
        !           645:  *
        !           646:  * DPC (Deferred Procedure Call) is used to call the specific
        !           647:  * function at some later time with a DPC priority. It is also
        !           648:  * known as AST or SoftIRQ in other kernels.  DPC is typically
        !           649:  * used by device drivers to do the low-priority jobs without
        !           650:  * degrading real-time performance.
        !           651:  * This routine can be called from ISR.
        !           652:  */
        !           653: void
        !           654: sched_dpc(struct dpc *dpc, void (*func)(void *), void *arg)
        !           655: {
        !           656:        ASSERT(dpc);
        !           657:        ASSERT(func);
        !           658:
        !           659:        irq_lock();
        !           660:        /*
        !           661:         * Insert request to DPC queue.
        !           662:         */
        !           663:        dpc->func = func;
        !           664:        dpc->arg = arg;
        !           665:        if (dpc->state != DPC_PENDING)
        !           666:                enqueue(&dpcq, &dpc->link);
        !           667:        dpc->state = DPC_PENDING;
        !           668:
        !           669:        /* Wake DPC thread */
        !           670:        sched_wakeup(&dpc_event);
        !           671:
        !           672:        irq_unlock();
        !           673: }
        !           674:
        !           675: /*
        !           676:  * DPC thread.
        !           677:  *
        !           678:  * This is a kernel thread to process the pending call back
        !           679:  * request within DPC queue. Each DPC routine is called with
        !           680:  * the following conditions.
        !           681:  *  - Interrupt is enabled.
        !           682:  *  - Scheduler is unlocked.
        !           683:  */
        !           684: static void
        !           685: dpc_thread(void *arg)
        !           686: {
        !           687:        queue_t q;
        !           688:        struct dpc *dpc;
        !           689:
        !           690:        for (;;) {
        !           691:                /* Wait until next DPC request. */
        !           692:                sched_sleep(&dpc_event);
        !           693:
        !           694:                while (!queue_empty(&dpcq)) {
        !           695:                        q = dequeue(&dpcq);
        !           696:                        dpc = queue_entry(q, struct dpc, link);
        !           697:                        dpc->state = DPC_FREE;
        !           698:
        !           699:                        /*
        !           700:                         * Call DPC routine.
        !           701:                         */
        !           702:                        interrupt_enable();
        !           703:                        (*dpc->func)(dpc->arg);
        !           704:                        interrupt_disable();
        !           705:                }
        !           706:        }
        !           707:        /* NOTREACHED */
        !           708: }
        !           709:
        !           710: /*
        !           711:  * Initialize the global scheduler state.
        !           712:  */
        !           713: void
        !           714: sched_init(void)
        !           715: {
        !           716:        thread_t th;
        !           717:        int i;
        !           718:
        !           719:        for (i = 0; i < NPRIO; i++)
        !           720:                queue_init(&runq[i]);
        !           721:        queue_init(&wakeq);
        !           722:        queue_init(&dpcq);
        !           723:        event_init(&dpc_event, "dpc");
        !           724:        top_prio = PRIO_IDLE;
        !           725:        cur_thread->resched = 1;
        !           726:
        !           727:        /* Create a DPC thread. */
        !           728:        th = kthread_create(dpc_thread, NULL, PRIO_DPC);
        !           729:        if (th == NULL)
        !           730:                panic("sched_init");
        !           731:
        !           732:        DPRINTF(("Time slice is %d msec\n", CONFIG_TIME_SLICE));
        !           733: }

CVSweb