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

Annotation of prex-old/sys/kern/thread.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:  * thread.c - thread management routines.
        !            32:  */
        !            33:
        !            34: #include <kernel.h>
        !            35: #include <kmem.h>
        !            36: #include <task.h>
        !            37: #include <thread.h>
        !            38: #include <ipc.h>
        !            39: #include <sched.h>
        !            40: #include <sync.h>
        !            41: #include <system.h>
        !            42:
        !            43: struct thread idle_thread;
        !            44: thread_t cur_thread = &idle_thread;
        !            45: static thread_t zombie;
        !            46:
        !            47: /*
        !            48:  * Allocate a new thread and attach kernel stack for it.
        !            49:  * Returns thread pointer on success, or NULL on failure.
        !            50:  */
        !            51: static thread_t
        !            52: thread_alloc(void)
        !            53: {
        !            54:        thread_t th;
        !            55:        void *stack;
        !            56:
        !            57:        if ((th = kmem_alloc(sizeof(struct thread))) == NULL)
        !            58:                return NULL;
        !            59:        memset(th, 0, sizeof(struct thread));
        !            60:
        !            61:        if ((stack = kmem_alloc(KSTACK_SIZE)) == NULL) {
        !            62:                kmem_free(th);
        !            63:                return NULL;
        !            64:        }
        !            65:        th->kstack = stack;
        !            66:        th->magic = THREAD_MAGIC;
        !            67:        list_init(&th->mutexes);
        !            68:        return th;
        !            69: }
        !            70:
        !            71: static void
        !            72: thread_free(thread_t th)
        !            73: {
        !            74:
        !            75:        kmem_free(th->kstack);
        !            76:        kmem_free(th);
        !            77: }
        !            78:
        !            79: /*
        !            80:  * Create a new thread within the specified task.
        !            81:  *
        !            82:  * The context of a current thread will be copied to the new thread.
        !            83:  * The new thread will start from the return address of thread_create()
        !            84:  * call in user mode code. Since a new thread will share the user
        !            85:  * mode stack with a current thread, user mode applications are
        !            86:  * responsible to allocate stack for it. The new thread is initially
        !            87:  * set to suspend state, and so, thread_resume() must be called to
        !            88:  * start it.
        !            89:  *
        !            90:  * The following scheduling parameters are reset to default values
        !            91:  * in the created thread.
        !            92:  *  - Thread State
        !            93:  *  - Scheduling Policy
        !            94:  *  - Scheduling Priority
        !            95:  */
        !            96: int
        !            97: thread_create(task_t task, thread_t *thp)
        !            98: {
        !            99:        thread_t th;
        !           100:        int err = 0;
        !           101:
        !           102:        sched_lock();
        !           103:        if (!task_valid(task)) {
        !           104:                err = ESRCH;
        !           105:                goto out;
        !           106:        }
        !           107:        if (!task_access(task)) {
        !           108:                err = EPERM;
        !           109:                goto out;
        !           110:        }
        !           111:        if ((th = thread_alloc()) == NULL) {
        !           112:                err = ENOMEM;
        !           113:                goto out;
        !           114:        }
        !           115:        /*
        !           116:         * At first, we copy a new thread id as return value.
        !           117:         * This is done here to simplify all error recoveries
        !           118:         * of the subsequent code.
        !           119:         */
        !           120:        if (cur_task() == &kern_task)
        !           121:                *thp = th;
        !           122:        else {
        !           123:                if (umem_copyout(&th, thp, sizeof(thread_t))) {
        !           124:                        thread_free(th);
        !           125:                        err = EFAULT;
        !           126:                        goto out;
        !           127:                }
        !           128:        }
        !           129:        /*
        !           130:         * Initialize thread state.
        !           131:         */
        !           132:        th->task = task;
        !           133:        th->suspend_count = task->suspend_count + 1;
        !           134:        memcpy(th->kstack, cur_thread->kstack, KSTACK_SIZE);
        !           135:        context_init(&th->context, (u_long)th->kstack + KSTACK_SIZE);
        !           136:        list_insert(&task->threads, &th->task_link);
        !           137:        sched_start(th);
        !           138:  out:
        !           139:        sched_unlock();
        !           140:        return err;
        !           141: }
        !           142:
        !           143: /*
        !           144:  * Permanently stop execution of the specified thread.
        !           145:  * If given thread is a current thread, this routine never returns.
        !           146:  */
        !           147: int
        !           148: thread_terminate(thread_t th)
        !           149: {
        !           150:        int err;
        !           151:
        !           152:        sched_lock();
        !           153:        if (!thread_valid(th)) {
        !           154:                err = ESRCH;
        !           155:        } else if (!task_access(th->task)) {
        !           156:                err = EPERM;
        !           157:        } else {
        !           158:                err = thread_kill(th);
        !           159:        }
        !           160:        sched_unlock();
        !           161:        return err;
        !           162: }
        !           163:
        !           164: /*
        !           165:  * Kill a thread regardless of the current task state.
        !           166:  *
        !           167:  * This may be used to terminate a kernel thread under the non-context
        !           168:  * condition. For example, a device driver may terminate its interrupt
        !           169:  * thread even if a current task does not have the capability to
        !           170:  * terminate it.
        !           171:  */
        !           172: int
        !           173: thread_kill(thread_t th)
        !           174: {
        !           175:        /*
        !           176:         * Clean up thread state.
        !           177:         */
        !           178:        msg_cleanup(th);
        !           179:        timer_cleanup(th);
        !           180:        mutex_cleanup(th);
        !           181:        list_remove(&th->task_link);
        !           182:        sched_stop(th);
        !           183:        th->exc_bitmap = 0;
        !           184:        th->magic = 0;
        !           185:
        !           186:        /*
        !           187:         * We can not release the context of the "current" thread
        !           188:         * because our thread switching always requires the current
        !           189:         * context. So, the resource deallocation is deferred until
        !           190:         * another thread calls thread_kill().
        !           191:         */
        !           192:        if (zombie != NULL) {
        !           193:                /*
        !           194:                 * Deallocate a zombie thread which was killed
        !           195:                 * in previous request.
        !           196:                 */
        !           197:                ASSERT(zombie != cur_thread);
        !           198:                thread_free(zombie);
        !           199:                zombie = NULL;
        !           200:        }
        !           201:        if (th == cur_thread) {
        !           202:                /*
        !           203:                 * If the current thread is being terminated,
        !           204:                 * enter zombie state and wait for sombody
        !           205:                 * to be killed us.
        !           206:                 */
        !           207:                zombie = th;
        !           208:        } else
        !           209:                thread_free(th);
        !           210:        return 0;
        !           211: }
        !           212:
        !           213: /*
        !           214:  * Load entry/stack address of the user mode context.
        !           215:  *
        !           216:  * The entry and stack address can be set to NULL.
        !           217:  * If it is NULL, old state is just kept.
        !           218:  */
        !           219: int
        !           220: thread_load(thread_t th, void (*entry)(void), void *stack)
        !           221: {
        !           222:        int err = 0;
        !           223:
        !           224:        if ((entry != NULL && !user_area(entry)) ||
        !           225:            (stack != NULL && !user_area(stack)))
        !           226:                return EINVAL;
        !           227:
        !           228:        sched_lock();
        !           229:        if (!thread_valid(th)) {
        !           230:                err = ESRCH;
        !           231:        } else if (!task_access(th->task)) {
        !           232:                err = EPERM;
        !           233:        } else {
        !           234:                if (entry != NULL)
        !           235:                        context_set(&th->context, CTX_UENTRY, (u_long)entry);
        !           236:                if (stack != NULL)
        !           237:                        context_set(&th->context, CTX_USTACK, (u_long)stack);
        !           238:        }
        !           239:        sched_unlock();
        !           240:        return 0;
        !           241: }
        !           242:
        !           243: thread_t
        !           244: thread_self(void)
        !           245: {
        !           246:
        !           247:        return cur_thread;
        !           248: }
        !           249:
        !           250: /*
        !           251:  * Release current thread for other thread.
        !           252:  */
        !           253: void
        !           254: thread_yield(void)
        !           255: {
        !           256:
        !           257:        sched_yield();
        !           258: }
        !           259:
        !           260: /*
        !           261:  * Suspend thread.
        !           262:  *
        !           263:  * A thread can be suspended any number of times. And, it does
        !           264:  * not start to run again unless the thread is resumed by the
        !           265:  * same count of suspend request.
        !           266:  */
        !           267: int
        !           268: thread_suspend(thread_t th)
        !           269: {
        !           270:        int err = 0;
        !           271:
        !           272:        sched_lock();
        !           273:        if (!thread_valid(th)) {
        !           274:                err = ESRCH;
        !           275:        } else if (!task_access(th->task)) {
        !           276:                err = EPERM;
        !           277:        } else {
        !           278:                if (++th->suspend_count == 1)
        !           279:                        sched_suspend(th);
        !           280:        }
        !           281:        sched_unlock();
        !           282:        return 0;
        !           283: }
        !           284:
        !           285: /*
        !           286:  * Resume thread.
        !           287:  *
        !           288:  * A thread does not begin to run, unless both thread
        !           289:  * suspend count and task suspend count are set to 0.
        !           290:  */
        !           291: int
        !           292: thread_resume(thread_t th)
        !           293: {
        !           294:        int err = 0;
        !           295:
        !           296:        ASSERT(th != cur_thread);
        !           297:
        !           298:        sched_lock();
        !           299:        if (!thread_valid(th)) {
        !           300:                err = ESRCH;
        !           301:        } else if (!task_access(th->task)) {
        !           302:                err= EPERM;
        !           303:        } else if (th->suspend_count == 0) {
        !           304:                err = EINVAL;
        !           305:        } else {
        !           306:                th->suspend_count--;
        !           307:                if (th->suspend_count == 0 && th->task->suspend_count == 0)
        !           308:                        sched_resume(th);
        !           309:        }
        !           310:        sched_unlock();
        !           311:        return err;
        !           312: }
        !           313:
        !           314: /*
        !           315:  * thread_schedparam - get/set scheduling parameter.
        !           316:  * @th:    target thread
        !           317:  * @op:    operation ID
        !           318:  * @param: pointer to parameter
        !           319:  *
        !           320:  * If the caller has CAP_NICE capability, all operations are allowed.
        !           321:  * Otherwise, the caller can change the parameter for the threads in
        !           322:  * the same task, and it can not set the priority to higher value.
        !           323:  */
        !           324: int
        !           325: thread_schedparam(thread_t th, int op, int *param)
        !           326: {
        !           327:        int prio, policy, err = 0;
        !           328:        int capable = 0;
        !           329:
        !           330:        sched_lock();
        !           331:        if (!thread_valid(th)) {
        !           332:                sched_unlock();
        !           333:                return ESRCH;
        !           334:        }
        !           335:        if (task_capable(CAP_NICE))
        !           336:                capable = 1;
        !           337:
        !           338:        if (th->task != cur_task() && !capable) {
        !           339:                sched_unlock();
        !           340:                return EPERM;
        !           341:        }
        !           342:        if ((th->task == &kern_task) &&
        !           343:            (op == OP_SETPRIO || op == OP_SETPOLICY)) {
        !           344:                sched_unlock();
        !           345:                return EPERM;
        !           346:        }
        !           347:        switch (op) {
        !           348:        case OP_GETPRIO:
        !           349:                prio = sched_getprio(th);
        !           350:                err = umem_copyout(&prio, param, sizeof(int));
        !           351:                break;
        !           352:        case OP_SETPRIO:
        !           353:                if ((err = umem_copyin(param, &prio, sizeof(int))))
        !           354:                        break;
        !           355:                if (prio < 0)
        !           356:                        prio = 0;
        !           357:                else if (prio >= PRIO_IDLE)
        !           358:                        prio = PRIO_IDLE - 1;
        !           359:
        !           360:                if (prio < th->prio && !capable) {
        !           361:                        err = EPERM;
        !           362:                        break;
        !           363:                }
        !           364:                /*
        !           365:                 * If a current priority is inherited for mutex,
        !           366:                 * we can not change the priority to lower value.
        !           367:                 * In this case, only the base priority is changed,
        !           368:                 * and a current priority will be adjusted to correct
        !           369:                 * value, later.
        !           370:                 */
        !           371:                if (th->prio != th->base_prio && prio > th->prio)
        !           372:                        prio = th->prio;
        !           373:
        !           374:                mutex_setprio(th, prio);
        !           375:                sched_setprio(th, prio, prio);
        !           376:                break;
        !           377:        case OP_GETPOLICY:
        !           378:                policy = sched_getpolicy(th);
        !           379:                err = umem_copyout(&policy, param, sizeof(int));
        !           380:                break;
        !           381:        case OP_SETPOLICY:
        !           382:                if ((err = umem_copyin(param, &policy, sizeof(int))))
        !           383:                        break;
        !           384:                if (sched_setpolicy(th, policy))
        !           385:                        err = EINVAL;
        !           386:                break;
        !           387:        default:
        !           388:                err = EINVAL;
        !           389:                break;
        !           390:        }
        !           391:        sched_unlock();
        !           392:        return err;
        !           393: }
        !           394:
        !           395: /*
        !           396:  * Idle thread.
        !           397:  *
        !           398:  * This routine is called only once after kernel initialization
        !           399:  * is completed. An idle thread has the role of cutting down the power
        !           400:  * consumption of a system. An idle thread has FIFO scheduling policy
        !           401:  * because it does not have time quantum.
        !           402:  */
        !           403: void
        !           404: thread_idle(void)
        !           405: {
        !           406:
        !           407:        for (;;) {
        !           408:                machine_idle();
        !           409:                sched_yield();
        !           410:        }
        !           411:        /* NOTREACHED */
        !           412: }
        !           413:
        !           414: /*
        !           415:  * Create a thread running in the kernel address space.
        !           416:  *
        !           417:  * A kernel thread does not have user mode context, and its
        !           418:  * scheduling policy is set to SCHED_FIFO. kernel_thread() returns
        !           419:  * thread ID on success, or NULL on failure. We assume scheduler
        !           420:  * is already locked.
        !           421:  *
        !           422:  * Important: Since sched_switch() will disable interrupts in CPU,
        !           423:  * the interrupt is always disabled at the entry point of the kernel
        !           424:  * thread. So, the kernel thread must enable the interrupt first when
        !           425:  * it gets control.
        !           426:  */
        !           427: thread_t
        !           428: kernel_thread(int prio, void (*entry)(u_long), u_long arg)
        !           429: {
        !           430:        thread_t th;
        !           431:
        !           432:        if ((th = thread_alloc()) == NULL)
        !           433:                return NULL;
        !           434:
        !           435:        th->task = &kern_task;
        !           436:        memset(th->kstack, 0, KSTACK_SIZE);
        !           437:        context_init(&th->context, (u_long)th->kstack + KSTACK_SIZE);
        !           438:        context_set(&th->context, CTX_KENTRY, (u_long)entry);
        !           439:        context_set(&th->context, CTX_KARG, arg);
        !           440:        list_insert(&kern_task.threads, &th->task_link);
        !           441:
        !           442:        sched_start(th);
        !           443:        sched_setpolicy(th, SCHED_FIFO);
        !           444:        sched_setprio(th, prio, prio);
        !           445:        sched_resume(th);
        !           446:        return th;
        !           447: }
        !           448:
        !           449: /*
        !           450:  * Return thread information for ps command.
        !           451:  */
        !           452: int
        !           453: thread_info(struct info_thread *info)
        !           454: {
        !           455:        u_long index, target = info->cookie;
        !           456:        list_t i, j;
        !           457:        thread_t th;
        !           458:        task_t task;
        !           459:
        !           460:        sched_lock();
        !           461:        index = 0;
        !           462:        i = &kern_task.link;
        !           463:        do {
        !           464:                task = list_entry(i, struct task, link);
        !           465:                j = &task->threads;
        !           466:                j = list_first(j);
        !           467:                do {
        !           468:                        th = list_entry(j, struct thread, task_link);
        !           469:                        if (index++ == target)
        !           470:                                goto found;
        !           471:                        j = list_next(j);
        !           472:                } while (j != &task->threads);
        !           473:                i = list_next(i);
        !           474:        } while (i != &kern_task.link);
        !           475:
        !           476:        sched_unlock();
        !           477:        return ESRCH;
        !           478:  found:
        !           479:        info->state = th->state;
        !           480:        info->policy = th->policy;
        !           481:        info->prio = th->prio;
        !           482:        info->base_prio = th->base_prio;
        !           483:        info->suspend_count = th->suspend_count;
        !           484:        info->total_ticks = th->total_ticks;
        !           485:        info->id = th;
        !           486:        info->task = th->task;
        !           487:        strlcpy(info->task_name, task->name, MAXTASKNAME);
        !           488:        strlcpy(info->sleep_event,
        !           489:                th->sleep_event ? th->sleep_event->name : "-", 12);
        !           490:
        !           491:        sched_unlock();
        !           492:        return 0;
        !           493: }
        !           494:
        !           495: #if defined(DEBUG) && defined(CONFIG_KDUMP)
        !           496: void
        !           497: thread_dump(void)
        !           498: {
        !           499:        static const char state[][4] = \
        !           500:                { "RUN", "SLP", "SUS", "S&S", "EXT" };
        !           501:        static const char pol[][5] = { "FIFO", "RR  " };
        !           502:        list_t i, j;
        !           503:        thread_t th;
        !           504:        task_t task;
        !           505:
        !           506:        printk("Thread dump:\n");
        !           507:        printk(" mod thread   task     stat pol  prio base ticks    "
        !           508:               "susp sleep event\n");
        !           509:        printk(" --- -------- -------- ---- ---- ---- ---- -------- "
        !           510:               "---- ------------\n");
        !           511:
        !           512:        i = &kern_task.link;
        !           513:        do {
        !           514:                task = list_entry(i, struct task, link);
        !           515:                j = &task->threads;
        !           516:                j = list_first(j);
        !           517:                do {
        !           518:                        th = list_entry(j, struct thread, task_link);
        !           519:                        printk(" %s %08x %8s %s%c %s  %3d  %3d %8d %4d %s\n",
        !           520:                               (task == &kern_task) ? "Knl" : "Usr", th,
        !           521:                               task->name, state[th->state],
        !           522:                               (th == cur_thread) ? '*' : ' ',
        !           523:                               pol[th->policy], th->prio, th->base_prio,
        !           524:                               th->total_ticks, th->suspend_count,
        !           525:                               th->sleep_event ? th->sleep_event->name : "-");
        !           526:                        j = list_next(j);
        !           527:                } while (j != &task->threads);
        !           528:                i = list_next(i);
        !           529:        } while (i != &kern_task.link);
        !           530: }
        !           531: #endif
        !           532:
        !           533: /*
        !           534:  * The first thread in system is created here by hand. This thread
        !           535:  * will become an idle thread when thread_idle() is called later.
        !           536:  */
        !           537: void
        !           538: thread_init(void)
        !           539: {
        !           540:        void *stack;
        !           541:
        !           542:        if ((stack = kmem_alloc(KSTACK_SIZE)) == NULL)
        !           543:                panic("thread_init");
        !           544:
        !           545:        memset(stack, 0, KSTACK_SIZE);
        !           546:        idle_thread.kstack = stack;
        !           547:        idle_thread.magic = THREAD_MAGIC;
        !           548:        idle_thread.task = &kern_task;
        !           549:        idle_thread.state = TH_RUN;
        !           550:        idle_thread.policy = SCHED_FIFO;
        !           551:        idle_thread.prio = PRIO_IDLE;
        !           552:        idle_thread.base_prio = PRIO_IDLE;
        !           553:        idle_thread.lock_count = 1;
        !           554:
        !           555:        context_init(&idle_thread.context, (u_long)stack + KSTACK_SIZE);
        !           556:        list_insert(&kern_task.threads, &idle_thread.task_link);
        !           557: }

CVSweb