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