source: mainline/kernel/generic/src/proc/task.c@ 8e8c1a5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8e8c1a5 was b3f8fb7, checked in by Martin Decky <martin@…>, 18 years ago

huge type system cleanup
remove cyclical type dependencies across multiple header files
many minor coding style fixes

  • Property mode set to 100644
File size: 12.5 KB
RevLine 
[f761f1eb]1/*
[df4ed85]2 * Copyright (c) 2001-2004 Jakub Jermar
[f761f1eb]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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[cc73a8a1]29/** @addtogroup genericproc
[b45c443]30 * @{
31 */
32
[9179d0a]33/**
[b45c443]34 * @file
[9179d0a]35 * @brief Task management.
36 */
37
[5be1923]38#include <main/uinit.h>
[f761f1eb]39#include <proc/thread.h>
40#include <proc/task.h>
[0f250f9]41#include <proc/uarg.h>
[20d50a1]42#include <mm/as.h>
[085d973]43#include <mm/slab.h>
[f761f1eb]44#include <synch/spinlock.h>
45#include <arch.h>
46#include <panic.h>
[7f6e755]47#include <adt/btree.h>
[5c9a08b]48#include <adt/list.h>
[6d9c49a]49#include <ipc/ipc.h>
[1077d91]50#include <security/cap.h>
[6d9c49a]51#include <memstr.h>
[37c57f2]52#include <print.h>
[d4b5542]53#include <lib/elf.h>
[7509ddc]54#include <errno.h>
[95155b0c]55#include <func.h>
[e3c762cd]56#include <syscall/copy.h>
[0dbc4e7]57#include <console/klog.h>
[8e5e78f]58
[a84af84]59#ifndef LOADED_PROG_STACK_PAGES_NO
60#define LOADED_PROG_STACK_PAGES_NO 1
61#endif
[8e5e78f]62
[88169d9]63/** Spinlock protecting the tasks_btree B+tree. */
[dc747e3]64SPINLOCK_INITIALIZE(tasks_lock);
[88169d9]65
66/** B+tree of active tasks.
67 *
[6f4495f5]68 * The task is guaranteed to exist after it was found in the tasks_btree as
69 * long as:
[88169d9]70 * @li the tasks_lock is held,
[6f4495f5]71 * @li the task's lock is held when task's lock is acquired before releasing
72 * tasks_lock or
[7bb6b06]73 * @li the task's refcount is greater than 0
[88169d9]74 *
75 */
[7f6e755]76btree_t tasks_btree;
[88169d9]77
[286e03d]78static task_id_t task_counter = 0;
[70527f1]79
[b91bb65]80static void ktaskclnp(void *arg);
[48e7dd6]81static void ktaskgc(void *arg);
[7509ddc]82
[70527f1]83/** Initialize tasks
84 *
85 * Initialize kernel tasks support.
86 *
87 */
[f761f1eb]88void task_init(void)
89{
[43114c5]90 TASK = NULL;
[7f6e755]91 btree_create(&tasks_btree);
[f761f1eb]92}
93
[70527f1]94
95/** Create new task
96 *
97 * Create new task with no threads.
98 *
[20d50a1]99 * @param as Task's address space.
[ff14c520]100 * @param name Symbolic name.
[70527f1]101 *
[5be1923]102 * @return New task's structure
[70527f1]103 *
104 */
[ff14c520]105task_t *task_create(as_t *as, char *name)
[f761f1eb]106{
[22f7769]107 ipl_t ipl;
[f761f1eb]108 task_t *ta;
[2ba7810]109 int i;
[f761f1eb]110
[bb68433]111 ta = (task_t *) malloc(sizeof(task_t), 0);
112
[963074b3]113 task_create_arch(ta);
114
[bb68433]115 spinlock_initialize(&ta->lock, "task_ta_lock");
116 list_initialize(&ta->th_head);
117 ta->as = as;
[ff14c520]118 ta->name = name;
[b91bb65]119 ta->main_thread = NULL;
[7509ddc]120 ta->refcount = 0;
[cfffb290]121 ta->context = CONTEXT;
[7509ddc]122
[1077d91]123 ta->capabilities = 0;
[7509ddc]124 ta->accept_new_threads = true;
[0313ff0]125 ta->cycles = 0;
[2ba7810]126
[6d9c49a]127 ipc_answerbox_init(&ta->answerbox);
[cfffb290]128 for (i = 0; i < IPC_MAX_PHONES; i++)
[2ba7810]129 ipc_phone_init(&ta->phones[i]);
[6f4495f5]130 if ((ipc_phone_0) && (context_check(ipc_phone_0->task->context,
131 ta->context)))
[2ba7810]132 ipc_phone_connect(&ta->phones[0], ipc_phone_0);
[5f62ef9]133 atomic_set(&ta->active_calls, 0);
[4fded58]134
135 mutex_initialize(&ta->futexes_lock);
136 btree_create(&ta->futexes);
[bb68433]137
138 ipl = interrupts_disable();
[482826d]139
140 /*
141 * Increment address space reference count.
142 * TODO: Reconsider the locking scheme.
143 */
144 mutex_lock(&as->lock);
145 as->refcount++;
146 mutex_unlock(&as->lock);
147
[bb68433]148 spinlock_lock(&tasks_lock);
[286e03d]149
150 ta->taskid = ++task_counter;
[b7f364e]151 btree_insert(&tasks_btree, (btree_key_t) ta->taskid, (void *) ta, NULL);
[286e03d]152
[bb68433]153 spinlock_unlock(&tasks_lock);
154 interrupts_restore(ipl);
155
[f761f1eb]156 return ta;
157}
158
[7509ddc]159/** Destroy task.
160 *
161 * @param t Task to be destroyed.
162 */
163void task_destroy(task_t *t)
164{
[31e8ddd]165 task_destroy_arch(t);
166 btree_destroy(&t->futexes);
167
168 mutex_lock_active(&t->as->lock);
169 if (--t->as->refcount == 0) {
170 mutex_unlock(&t->as->lock);
171 as_destroy(t->as);
172 /*
173 * t->as is destroyed.
174 */
[def5207]175 } else
[31e8ddd]176 mutex_unlock(&t->as->lock);
177
178 free(t);
179 TASK = NULL;
[7509ddc]180}
181
[5be1923]182/** Create new task with 1 thread and run it
[ff14c520]183 *
[9179d0a]184 * @param program_addr Address of program executable image.
[ff14c520]185 * @param name Program name.
[5be1923]186 *
[7f0837c]187 * @return Task of the running program or NULL on error.
[5be1923]188 */
[ff14c520]189task_t * task_run_program(void *program_addr, char *name)
[5be1923]190{
191 as_t *as;
192 as_area_t *a;
193 int rc;
[b91bb65]194 thread_t *t1, *t2;
[5be1923]195 task_t *task;
[0f250f9]196 uspace_arg_t *kernel_uarg;
[5be1923]197
198 as = as_create(0);
[a0bb10ef]199 ASSERT(as);
[5be1923]200
[649799a]201 rc = elf_load((elf_header_t *) program_addr, as);
[5be1923]202 if (rc != EE_OK) {
[482826d]203 as_destroy(as);
[5be1923]204 return NULL;
205 }
206
[0f250f9]207 kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
[6f4495f5]208 kernel_uarg->uspace_entry =
209 (void *) ((elf_header_t *) program_addr)->e_entry;
[0f250f9]210 kernel_uarg->uspace_stack = (void *) USTACK_ADDRESS;
211 kernel_uarg->uspace_thread_function = NULL;
212 kernel_uarg->uspace_thread_arg = NULL;
213 kernel_uarg->uspace_uarg = NULL;
[9f52563]214
[ff14c520]215 task = task_create(as, name);
[a0bb10ef]216 ASSERT(task);
217
[5be1923]218 /*
219 * Create the data as_area.
220 */
[6f4495f5]221 a = as_area_create(as, AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE,
222 LOADED_PROG_STACK_PAGES_NO * PAGE_SIZE, USTACK_ADDRESS,
223 AS_AREA_ATTR_NONE, &anon_backend, NULL);
[5be1923]224
[b91bb65]225 /*
226 * Create the main thread.
227 */
[6f4495f5]228 t1 = thread_create(uinit, kernel_uarg, task, THREAD_FLAG_USPACE,
229 "uinit", false);
[b91bb65]230 ASSERT(t1);
[a0bb10ef]231
[b91bb65]232 /*
233 * Create killer thread for the new task.
234 */
[62b6d17]235 t2 = thread_create(ktaskgc, t1, task, 0, "ktaskgc", true);
[b91bb65]236 ASSERT(t2);
237 thread_ready(t2);
238
239 thread_ready(t1);
240
[5be1923]241 return task;
242}
[37c57f2]243
[ec55358]244/** Syscall for reading task ID from userspace.
245 *
[6f4495f5]246 * @param uspace_task_id Userspace address of 8-byte buffer where to store
247 * current task ID.
[ec55358]248 *
[e3c762cd]249 * @return 0 on success or an error code from @ref errno.h.
[ec55358]250 */
[7f1c620]251unative_t sys_task_get_id(task_id_t *uspace_task_id)
[ec55358]252{
253 /*
254 * No need to acquire lock on TASK because taskid
255 * remains constant for the lifespan of the task.
256 */
[6f4495f5]257 return (unative_t) copy_to_uspace(uspace_task_id, &TASK->taskid,
258 sizeof(TASK->taskid));
[ec55358]259}
260
[9a8d91b]261/** Find task structure corresponding to task ID.
262 *
263 * The tasks_lock must be already held by the caller of this function
264 * and interrupts must be disabled.
265 *
266 * @param id Task ID.
267 *
268 * @return Task structure address or NULL if there is no such task ID.
269 */
270task_t *task_find_by_id(task_id_t id)
271{
272 btree_node_t *leaf;
273
274 return (task_t *) btree_search(&tasks_btree, (btree_key_t) id, &leaf);
275}
276
[0313ff0]277/** Get accounting data of given task.
278 *
[771cd22]279 * Note that task lock of 't' must be already held and
[0313ff0]280 * interrupts must be already disabled.
281 *
282 * @param t Pointer to thread.
283 *
284 */
285uint64_t task_get_accounting(task_t *t)
286{
287 /* Accumulated value of task */
288 uint64_t ret = t->cycles;
289
290 /* Current values of threads */
291 link_t *cur;
292 for (cur = t->th_head.next; cur != &t->th_head; cur = cur->next) {
293 thread_t *thr = list_get_instance(cur, thread_t, th_link);
294
295 spinlock_lock(&thr->lock);
[62b6d17]296 /* Process only counted threads */
297 if (!thr->uncounted) {
[6f4495f5]298 if (thr == THREAD) {
299 /* Update accounting of current thread */
300 thread_update_accounting();
301 }
[62b6d17]302 ret += thr->cycles;
303 }
[0313ff0]304 spinlock_unlock(&thr->lock);
305 }
306
307 return ret;
308}
309
[7509ddc]310/** Kill task.
311 *
312 * @param id ID of the task to be killed.
313 *
314 * @return 0 on success or an error code from errno.h
315 */
316int task_kill(task_id_t id)
317{
318 ipl_t ipl;
319 task_t *ta;
320 thread_t *t;
321 link_t *cur;
[9b6aae6]322
323 if (id == 1)
324 return EPERM;
[7509ddc]325
326 ipl = interrupts_disable();
327 spinlock_lock(&tasks_lock);
328
329 if (!(ta = task_find_by_id(id))) {
330 spinlock_unlock(&tasks_lock);
331 interrupts_restore(ipl);
332 return ENOENT;
333 }
[2569ec90]334
[7509ddc]335 spinlock_lock(&ta->lock);
336 ta->refcount++;
337 spinlock_unlock(&ta->lock);
[31e8ddd]338
[2569ec90]339 btree_remove(&tasks_btree, ta->taskid, NULL);
[31e8ddd]340 spinlock_unlock(&tasks_lock);
[7509ddc]341
[62b6d17]342 t = thread_create(ktaskclnp, NULL, ta, 0, "ktaskclnp", true);
[7509ddc]343
344 spinlock_lock(&ta->lock);
[34dcd3f]345 ta->accept_new_threads = false;
[7509ddc]346 ta->refcount--;
[b91bb65]347
348 /*
[0182a665]349 * Interrupt all threads except ktaskclnp.
[b91bb65]350 */
[7509ddc]351 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
352 thread_t *thr;
353 bool sleeping = false;
354
355 thr = list_get_instance(cur, thread_t, th_link);
356 if (thr == t)
357 continue;
358
359 spinlock_lock(&thr->lock);
360 thr->interrupted = true;
361 if (thr->state == Sleeping)
362 sleeping = true;
363 spinlock_unlock(&thr->lock);
364
365 if (sleeping)
[b3f8fb7]366 thread_interrupt_sleep(thr);
[7509ddc]367 }
368
[34dcd3f]369 spinlock_unlock(&ta->lock);
370 interrupts_restore(ipl);
[7509ddc]371
[34dcd3f]372 if (t)
373 thread_ready(t);
374
[7509ddc]375 return 0;
376}
377
[37c57f2]378/** Print task list */
379void task_print_list(void)
380{
381 link_t *cur;
382 ipl_t ipl;
383
384 /* Messing with thread structures, avoid deadlock */
385 ipl = interrupts_disable();
386 spinlock_lock(&tasks_lock);
[f88fcbe]387
[6f4495f5]388 printf("taskid name ctx address as cycles threads "
389 "calls callee\n");
390 printf("------ ---------- --- ---------- ---------- ---------- ------- " "------ ------>\n");
[37c57f2]391
[6f4495f5]392 for (cur = tasks_btree.leaf_head.next; cur != &tasks_btree.leaf_head;
393 cur = cur->next) {
[7f6e755]394 btree_node_t *node;
395 int i;
396
397 node = list_get_instance(cur, btree_node_t, leaf_link);
398 for (i = 0; i < node->keys; i++) {
399 task_t *t;
400 int j;
401
402 t = (task_t *) node->value[i];
403
404 spinlock_lock(&t->lock);
[0313ff0]405
[95155b0c]406 uint64_t cycles;
[0313ff0]407 char suffix;
[95155b0c]408 order(task_get_accounting(t), &cycles, &suffix);
[0313ff0]409
[6f4495f5]410 printf("%-6lld %-10s %-3ld %#10zx %#10zx %9llu%c %7zd "
411 "%6zd", t->taskid, t->name, t->context, t, t->as,
412 cycles, suffix, t->refcount,
413 atomic_get(&t->active_calls));
[f88fcbe]414 for (j = 0; j < IPC_MAX_PHONES; j++) {
[7f6e755]415 if (t->phones[j].callee)
[6f4495f5]416 printf(" %zd:%#zx", j,
417 t->phones[j].callee);
[7f6e755]418 }
419 printf("\n");
[0313ff0]420
[7f6e755]421 spinlock_unlock(&t->lock);
[37c57f2]422 }
423 }
424
425 spinlock_unlock(&tasks_lock);
426 interrupts_restore(ipl);
427}
[7509ddc]428
[b91bb65]429/** Kernel thread used to cleanup the task after it is killed. */
[34dcd3f]430void ktaskclnp(void *arg)
[7509ddc]431{
[34dcd3f]432 ipl_t ipl;
[b91bb65]433 thread_t *t = NULL, *main_thread;
[34dcd3f]434 link_t *cur;
[48e7dd6]435 bool again;
[34dcd3f]436
437 thread_detach(THREAD);
438
439loop:
440 ipl = interrupts_disable();
441 spinlock_lock(&TASK->lock);
442
[b91bb65]443 main_thread = TASK->main_thread;
444
[34dcd3f]445 /*
446 * Find a thread to join.
447 */
[48e7dd6]448 again = false;
[34dcd3f]449 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
450 t = list_get_instance(cur, thread_t, th_link);
[48e7dd6]451
452 spinlock_lock(&t->lock);
453 if (t == THREAD) {
454 spinlock_unlock(&t->lock);
455 continue;
456 } else if (t == main_thread) {
457 spinlock_unlock(&t->lock);
[34dcd3f]458 continue;
[48e7dd6]459 } else if (t->join_type != None) {
460 spinlock_unlock(&t->lock);
461 again = true;
[b91bb65]462 continue;
[48e7dd6]463 } else {
464 t->join_type = TaskClnp;
465 spinlock_unlock(&t->lock);
466 again = false;
[34dcd3f]467 break;
[48e7dd6]468 }
[34dcd3f]469 }
470
471 spinlock_unlock(&TASK->lock);
472 interrupts_restore(ipl);
473
[48e7dd6]474 if (again) {
475 /*
476 * Other cleanup (e.g. ktaskgc) is in progress.
477 */
478 scheduler();
479 goto loop;
480 }
481
[34dcd3f]482 if (t != THREAD) {
[6f4495f5]483 ASSERT(t != main_thread); /* uninit is joined and detached
484 * in ktaskgc */
[34dcd3f]485 thread_join(t);
486 thread_detach(t);
[6f4495f5]487 goto loop; /* go for another thread */
[34dcd3f]488 }
489
490 /*
491 * Now there are no other threads in this task
492 * and no new threads can be created.
493 */
[7bb6b06]494
[e090e1bc]495 ipc_cleanup();
496 futex_cleanup();
[0dbc4e7]497 klog_printf("Cleanup of task %lld completed.", TASK->taskid);
[7509ddc]498}
[b91bb65]499
[88636f68]500/** Kernel thread used to kill the userspace task when its main thread exits.
[b91bb65]501 *
502 * This thread waits until the main userspace thread (i.e. uninit) exits.
[48e7dd6]503 * When this happens, the task is killed. In the meantime, exited threads
504 * are garbage collected.
[b91bb65]505 *
506 * @param arg Pointer to the thread structure of the task's main thread.
507 */
[48e7dd6]508void ktaskgc(void *arg)
[b91bb65]509{
510 thread_t *t = (thread_t *) arg;
[48e7dd6]511loop:
[b91bb65]512 /*
513 * Userspace threads cannot detach themselves,
514 * therefore the thread pointer is guaranteed to be valid.
515 */
[6f4495f5]516 if (thread_join_timeout(t, 1000000, SYNCH_FLAGS_NONE) ==
517 ESYNCH_TIMEOUT) { /* sleep uninterruptibly here! */
[48e7dd6]518 ipl_t ipl;
519 link_t *cur;
520 thread_t *thr = NULL;
521
522 /*
[6f4495f5]523 * The join timed out. Try to do some garbage collection of
524 * Undead threads.
[48e7dd6]525 */
526more_gc:
527 ipl = interrupts_disable();
528 spinlock_lock(&TASK->lock);
529
[6f4495f5]530 for (cur = TASK->th_head.next; cur != &TASK->th_head;
531 cur = cur->next) {
[48e7dd6]532 thr = list_get_instance(cur, thread_t, th_link);
533 spinlock_lock(&thr->lock);
[6f4495f5]534 if (thr != t && thr->state == Undead &&
535 thr->join_type == None) {
[48e7dd6]536 thr->join_type = TaskGC;
537 spinlock_unlock(&thr->lock);
538 break;
539 }
540 spinlock_unlock(&thr->lock);
541 thr = NULL;
542 }
543 spinlock_unlock(&TASK->lock);
[92922e6]544 interrupts_restore(ipl);
[48e7dd6]545
546 if (thr) {
547 thread_join(thr);
548 thread_detach(thr);
549 scheduler();
550 goto more_gc;
551 }
552
553 goto loop;
554 }
[b91bb65]555 thread_detach(t);
556 task_kill(TASK->taskid);
557}
[b45c443]558
[cc73a8a1]559/** @}
[b45c443]560 */
Note: See TracBrowser for help on using the repository browser.