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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ea7890e7 was ea7890e7, checked in by Jakub Jermar <jakub@…>, 18 years ago

More efficient and simpler task termination.

Based on the assumption, that after its creation, only the task itself can create more threads for itself,
the last thread with userspace context to execute thread_exit() will perform futex and IPC cleanup. When
the task has no threads, it is destroyed. Both the cleanup and destruction is controlled by reference
counting.

As for userspace threads, even though there could be a global garbage collector for joining threads, it is
much simpler if the uinit thread detaches itself before switching to userspace.

task_kill() is now an idempotent operation. It just instructs the threads within a task to exit.

Change in the name of a thread state: Undead → JoinMe.

  • Property mode set to 100644
File size: 10.3 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>
[31d8e10]44#include <atomic.h>
[f761f1eb]45#include <synch/spinlock.h>
[5573942]46#include <synch/waitq.h>
[f761f1eb]47#include <arch.h>
48#include <panic.h>
[7f6e755]49#include <adt/btree.h>
[5c9a08b]50#include <adt/list.h>
[6d9c49a]51#include <ipc/ipc.h>
[1077d91]52#include <security/cap.h>
[6d9c49a]53#include <memstr.h>
[37c57f2]54#include <print.h>
[d4b5542]55#include <lib/elf.h>
[7509ddc]56#include <errno.h>
[95155b0c]57#include <func.h>
[e3c762cd]58#include <syscall/copy.h>
[8e5e78f]59
[a84af84]60#ifndef LOADED_PROG_STACK_PAGES_NO
61#define LOADED_PROG_STACK_PAGES_NO 1
62#endif
[8e5e78f]63
[88169d9]64/** Spinlock protecting the tasks_btree B+tree. */
[dc747e3]65SPINLOCK_INITIALIZE(tasks_lock);
[88169d9]66
67/** B+tree of active tasks.
68 *
[6f4495f5]69 * The task is guaranteed to exist after it was found in the tasks_btree as
70 * long as:
[88169d9]71 * @li the tasks_lock is held,
[6f4495f5]72 * @li the task's lock is held when task's lock is acquired before releasing
73 * tasks_lock or
[7bb6b06]74 * @li the task's refcount is greater than 0
[88169d9]75 *
76 */
[7f6e755]77btree_t tasks_btree;
[88169d9]78
[286e03d]79static task_id_t task_counter = 0;
[70527f1]80
81/** Initialize tasks
82 *
83 * Initialize kernel tasks support.
84 *
85 */
[f761f1eb]86void task_init(void)
87{
[43114c5]88 TASK = NULL;
[7f6e755]89 btree_create(&tasks_btree);
[f761f1eb]90}
91
[f74bbaf]92/** Kill all tasks except the current task.
93 *
94 */
95void task_done(void)
96{
97 task_t *t;
98 do { /* Repeat until there are any tasks except TASK */
99
100 /* Messing with task structures, avoid deadlock */
101 ipl_t ipl = interrupts_disable();
102 spinlock_lock(&tasks_lock);
103
104 t = NULL;
105 link_t *cur;
[f6d2c81]106 for (cur = tasks_btree.leaf_head.next;
107 cur != &tasks_btree.leaf_head; cur = cur->next) {
108 btree_node_t *node;
109
110 node = list_get_instance(cur, btree_node_t, leaf_link);
[f74bbaf]111
112 unsigned int i;
113 for (i = 0; i < node->keys; i++) {
114 if ((task_t *) node->value[i] != TASK) {
115 t = (task_t *) node->value[i];
116 break;
117 }
118 }
119 }
120
121 if (t != NULL) {
122 task_id_t id = t->taskid;
123
124 spinlock_unlock(&tasks_lock);
125 interrupts_restore(ipl);
126
127#ifdef CONFIG_DEBUG
128 printf("Killing task %llu\n", id);
129#endif
130 task_kill(id);
131 } else {
132 spinlock_unlock(&tasks_lock);
133 interrupts_restore(ipl);
134 }
135
136 } while (t != NULL);
137}
[70527f1]138
139/** Create new task
140 *
141 * Create new task with no threads.
142 *
[20d50a1]143 * @param as Task's address space.
[ff14c520]144 * @param name Symbolic name.
[70527f1]145 *
[5be1923]146 * @return New task's structure
[70527f1]147 *
148 */
[ff14c520]149task_t *task_create(as_t *as, char *name)
[f761f1eb]150{
[22f7769]151 ipl_t ipl;
[f761f1eb]152 task_t *ta;
[2ba7810]153 int i;
[f761f1eb]154
[bb68433]155 ta = (task_t *) malloc(sizeof(task_t), 0);
156
[963074b3]157 task_create_arch(ta);
158
[bb68433]159 spinlock_initialize(&ta->lock, "task_ta_lock");
160 list_initialize(&ta->th_head);
161 ta->as = as;
[ff14c520]162 ta->name = name;
[ea7890e7]163 atomic_set(&ta->refcount, 0);
164 atomic_set(&ta->lifecount, 0);
[cfffb290]165 ta->context = CONTEXT;
[7509ddc]166
[1077d91]167 ta->capabilities = 0;
[0313ff0]168 ta->cycles = 0;
[2ba7810]169
[6d9c49a]170 ipc_answerbox_init(&ta->answerbox);
[cfffb290]171 for (i = 0; i < IPC_MAX_PHONES; i++)
[2ba7810]172 ipc_phone_init(&ta->phones[i]);
[6f4495f5]173 if ((ipc_phone_0) && (context_check(ipc_phone_0->task->context,
174 ta->context)))
[2ba7810]175 ipc_phone_connect(&ta->phones[0], ipc_phone_0);
[5f62ef9]176 atomic_set(&ta->active_calls, 0);
[4fded58]177
178 mutex_initialize(&ta->futexes_lock);
179 btree_create(&ta->futexes);
[bb68433]180
181 ipl = interrupts_disable();
[482826d]182
183 /*
184 * Increment address space reference count.
185 */
[31d8e10]186 atomic_inc(&as->refcount);
[482826d]187
[bb68433]188 spinlock_lock(&tasks_lock);
[286e03d]189 ta->taskid = ++task_counter;
[b7f364e]190 btree_insert(&tasks_btree, (btree_key_t) ta->taskid, (void *) ta, NULL);
[bb68433]191 spinlock_unlock(&tasks_lock);
192 interrupts_restore(ipl);
193
[f761f1eb]194 return ta;
195}
196
[7509ddc]197/** Destroy task.
198 *
199 * @param t Task to be destroyed.
200 */
201void task_destroy(task_t *t)
202{
[ea7890e7]203 /*
204 * Remove the task from the task B+tree.
205 */
206 spinlock_lock(&tasks_lock);
207 btree_remove(&tasks_btree, t->taskid, NULL);
208 spinlock_unlock(&tasks_lock);
209
210 /*
211 * Perform architecture specific task destruction.
212 */
[31e8ddd]213 task_destroy_arch(t);
[ea7890e7]214
215 /*
216 * Free up dynamically allocated state.
217 */
[31e8ddd]218 btree_destroy(&t->futexes);
219
[ea7890e7]220 /*
221 * Drop our reference to the address space.
222 */
[31d8e10]223 if (atomic_predec(&t->as->refcount) == 0)
[31e8ddd]224 as_destroy(t->as);
225
226 free(t);
227 TASK = NULL;
[7509ddc]228}
229
[5be1923]230/** Create new task with 1 thread and run it
[ff14c520]231 *
[9179d0a]232 * @param program_addr Address of program executable image.
[ff14c520]233 * @param name Program name.
[5be1923]234 *
[7f0837c]235 * @return Task of the running program or NULL on error.
[5be1923]236 */
[f6d2c81]237task_t *task_run_program(void *program_addr, char *name)
[5be1923]238{
239 as_t *as;
240 as_area_t *a;
241 int rc;
[ea7890e7]242 thread_t *t;
[5be1923]243 task_t *task;
[0f250f9]244 uspace_arg_t *kernel_uarg;
[5be1923]245
246 as = as_create(0);
[a0bb10ef]247 ASSERT(as);
[5be1923]248
[649799a]249 rc = elf_load((elf_header_t *) program_addr, as);
[5be1923]250 if (rc != EE_OK) {
[482826d]251 as_destroy(as);
[5be1923]252 return NULL;
253 }
254
[0f250f9]255 kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
[6f4495f5]256 kernel_uarg->uspace_entry =
257 (void *) ((elf_header_t *) program_addr)->e_entry;
[0f250f9]258 kernel_uarg->uspace_stack = (void *) USTACK_ADDRESS;
259 kernel_uarg->uspace_thread_function = NULL;
260 kernel_uarg->uspace_thread_arg = NULL;
261 kernel_uarg->uspace_uarg = NULL;
[9f52563]262
[ff14c520]263 task = task_create(as, name);
[a0bb10ef]264 ASSERT(task);
265
[5be1923]266 /*
267 * Create the data as_area.
268 */
[6f4495f5]269 a = as_area_create(as, AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE,
270 LOADED_PROG_STACK_PAGES_NO * PAGE_SIZE, USTACK_ADDRESS,
271 AS_AREA_ATTR_NONE, &anon_backend, NULL);
[5be1923]272
[b91bb65]273 /*
274 * Create the main thread.
275 */
[ea7890e7]276 t = thread_create(uinit, kernel_uarg, task, THREAD_FLAG_USPACE,
[6f4495f5]277 "uinit", false);
[ea7890e7]278 ASSERT(t);
[a0bb10ef]279
[ea7890e7]280 thread_ready(t);
[b91bb65]281
[5be1923]282 return task;
283}
[37c57f2]284
[ec55358]285/** Syscall for reading task ID from userspace.
286 *
[6f4495f5]287 * @param uspace_task_id Userspace address of 8-byte buffer where to store
288 * current task ID.
[ec55358]289 *
[e3c762cd]290 * @return 0 on success or an error code from @ref errno.h.
[ec55358]291 */
[7f1c620]292unative_t sys_task_get_id(task_id_t *uspace_task_id)
[ec55358]293{
294 /*
295 * No need to acquire lock on TASK because taskid
296 * remains constant for the lifespan of the task.
297 */
[6f4495f5]298 return (unative_t) copy_to_uspace(uspace_task_id, &TASK->taskid,
299 sizeof(TASK->taskid));
[ec55358]300}
301
[9a8d91b]302/** Find task structure corresponding to task ID.
303 *
304 * The tasks_lock must be already held by the caller of this function
305 * and interrupts must be disabled.
306 *
307 * @param id Task ID.
308 *
309 * @return Task structure address or NULL if there is no such task ID.
310 */
311task_t *task_find_by_id(task_id_t id)
312{
313 btree_node_t *leaf;
314
315 return (task_t *) btree_search(&tasks_btree, (btree_key_t) id, &leaf);
316}
317
[0313ff0]318/** Get accounting data of given task.
319 *
[771cd22]320 * Note that task lock of 't' must be already held and
[0313ff0]321 * interrupts must be already disabled.
322 *
323 * @param t Pointer to thread.
324 *
325 */
326uint64_t task_get_accounting(task_t *t)
327{
328 /* Accumulated value of task */
329 uint64_t ret = t->cycles;
330
331 /* Current values of threads */
332 link_t *cur;
333 for (cur = t->th_head.next; cur != &t->th_head; cur = cur->next) {
334 thread_t *thr = list_get_instance(cur, thread_t, th_link);
335
336 spinlock_lock(&thr->lock);
[62b6d17]337 /* Process only counted threads */
338 if (!thr->uncounted) {
[6f4495f5]339 if (thr == THREAD) {
340 /* Update accounting of current thread */
341 thread_update_accounting();
342 }
[62b6d17]343 ret += thr->cycles;
344 }
[0313ff0]345 spinlock_unlock(&thr->lock);
346 }
347
348 return ret;
349}
350
[7509ddc]351/** Kill task.
[ea7890e7]352 *
353 * This function is idempotent.
354 * It signals all the task's threads to bail it out.
[7509ddc]355 *
356 * @param id ID of the task to be killed.
357 *
358 * @return 0 on success or an error code from errno.h
359 */
360int task_kill(task_id_t id)
361{
362 ipl_t ipl;
363 task_t *ta;
364 link_t *cur;
[9b6aae6]365
366 if (id == 1)
367 return EPERM;
[7509ddc]368
369 ipl = interrupts_disable();
370 spinlock_lock(&tasks_lock);
371 if (!(ta = task_find_by_id(id))) {
372 spinlock_unlock(&tasks_lock);
373 interrupts_restore(ipl);
374 return ENOENT;
375 }
[31e8ddd]376 spinlock_unlock(&tasks_lock);
[7509ddc]377
[b91bb65]378 /*
[0182a665]379 * Interrupt all threads except ktaskclnp.
[ea7890e7]380 */
381 spinlock_lock(&ta->lock);
[7509ddc]382 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
383 thread_t *thr;
[ea7890e7]384 bool sleeping = false;
[7509ddc]385
386 thr = list_get_instance(cur, thread_t, th_link);
387
388 spinlock_lock(&thr->lock);
389 thr->interrupted = true;
390 if (thr->state == Sleeping)
391 sleeping = true;
392 spinlock_unlock(&thr->lock);
393
394 if (sleeping)
[5573942]395 waitq_interrupt_sleep(thr);
[7509ddc]396 }
[34dcd3f]397 spinlock_unlock(&ta->lock);
398 interrupts_restore(ipl);
[7509ddc]399
400 return 0;
401}
402
[37c57f2]403/** Print task list */
404void task_print_list(void)
405{
406 link_t *cur;
407 ipl_t ipl;
408
[f74bbaf]409 /* Messing with task structures, avoid deadlock */
[37c57f2]410 ipl = interrupts_disable();
411 spinlock_lock(&tasks_lock);
[f88fcbe]412
[6f4495f5]413 printf("taskid name ctx address as cycles threads "
414 "calls callee\n");
[ea7890e7]415 printf("------ ---------- --- ---------- ---------- ---------- ------- "
416 "------ ------>\n");
[37c57f2]417
[6f4495f5]418 for (cur = tasks_btree.leaf_head.next; cur != &tasks_btree.leaf_head;
419 cur = cur->next) {
[7f6e755]420 btree_node_t *node;
[4184e76]421 unsigned int i;
[7f6e755]422
423 node = list_get_instance(cur, btree_node_t, leaf_link);
424 for (i = 0; i < node->keys; i++) {
425 task_t *t;
426 int j;
427
428 t = (task_t *) node->value[i];
429
430 spinlock_lock(&t->lock);
[0313ff0]431
[95155b0c]432 uint64_t cycles;
[0313ff0]433 char suffix;
[95155b0c]434 order(task_get_accounting(t), &cycles, &suffix);
[0313ff0]435
[201abde]436 printf("%-6llu %-10s %-3ld %#10zx %#10zx %9llu%c %7zd "
[6f4495f5]437 "%6zd", t->taskid, t->name, t->context, t, t->as,
438 cycles, suffix, t->refcount,
439 atomic_get(&t->active_calls));
[f88fcbe]440 for (j = 0; j < IPC_MAX_PHONES; j++) {
[7f6e755]441 if (t->phones[j].callee)
[6f4495f5]442 printf(" %zd:%#zx", j,
443 t->phones[j].callee);
[7f6e755]444 }
445 printf("\n");
[0313ff0]446
[7f6e755]447 spinlock_unlock(&t->lock);
[37c57f2]448 }
449 }
450
451 spinlock_unlock(&tasks_lock);
452 interrupts_restore(ipl);
453}
[7509ddc]454
[cc73a8a1]455/** @}
[b45c443]456 */
Note: See TracBrowser for help on using the repository browser.