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
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
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
29/** @addtogroup genericproc
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Task management.
36 */
37
38#include <main/uinit.h>
39#include <proc/thread.h>
40#include <proc/task.h>
41#include <proc/uarg.h>
42#include <mm/as.h>
43#include <mm/slab.h>
44#include <atomic.h>
45#include <synch/spinlock.h>
46#include <synch/waitq.h>
47#include <arch.h>
48#include <panic.h>
49#include <adt/btree.h>
50#include <adt/list.h>
51#include <ipc/ipc.h>
52#include <security/cap.h>
53#include <memstr.h>
54#include <print.h>
55#include <lib/elf.h>
56#include <errno.h>
57#include <func.h>
58#include <syscall/copy.h>
59
60#ifndef LOADED_PROG_STACK_PAGES_NO
61#define LOADED_PROG_STACK_PAGES_NO 1
62#endif
63
64/** Spinlock protecting the tasks_btree B+tree. */
65SPINLOCK_INITIALIZE(tasks_lock);
66
67/** B+tree of active tasks.
68 *
69 * The task is guaranteed to exist after it was found in the tasks_btree as
70 * long as:
71 * @li the tasks_lock is held,
72 * @li the task's lock is held when task's lock is acquired before releasing
73 * tasks_lock or
74 * @li the task's refcount is greater than 0
75 *
76 */
77btree_t tasks_btree;
78
79static task_id_t task_counter = 0;
80
81/** Initialize tasks
82 *
83 * Initialize kernel tasks support.
84 *
85 */
86void task_init(void)
87{
88 TASK = NULL;
89 btree_create(&tasks_btree);
90}
91
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;
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);
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}
138
139/** Create new task
140 *
141 * Create new task with no threads.
142 *
143 * @param as Task's address space.
144 * @param name Symbolic name.
145 *
146 * @return New task's structure
147 *
148 */
149task_t *task_create(as_t *as, char *name)
150{
151 ipl_t ipl;
152 task_t *ta;
153 int i;
154
155 ta = (task_t *) malloc(sizeof(task_t), 0);
156
157 task_create_arch(ta);
158
159 spinlock_initialize(&ta->lock, "task_ta_lock");
160 list_initialize(&ta->th_head);
161 ta->as = as;
162 ta->name = name;
163 atomic_set(&ta->refcount, 0);
164 atomic_set(&ta->lifecount, 0);
165 ta->context = CONTEXT;
166
167 ta->capabilities = 0;
168 ta->cycles = 0;
169
170 ipc_answerbox_init(&ta->answerbox);
171 for (i = 0; i < IPC_MAX_PHONES; i++)
172 ipc_phone_init(&ta->phones[i]);
173 if ((ipc_phone_0) && (context_check(ipc_phone_0->task->context,
174 ta->context)))
175 ipc_phone_connect(&ta->phones[0], ipc_phone_0);
176 atomic_set(&ta->active_calls, 0);
177
178 mutex_initialize(&ta->futexes_lock);
179 btree_create(&ta->futexes);
180
181 ipl = interrupts_disable();
182
183 /*
184 * Increment address space reference count.
185 */
186 atomic_inc(&as->refcount);
187
188 spinlock_lock(&tasks_lock);
189 ta->taskid = ++task_counter;
190 btree_insert(&tasks_btree, (btree_key_t) ta->taskid, (void *) ta, NULL);
191 spinlock_unlock(&tasks_lock);
192 interrupts_restore(ipl);
193
194 return ta;
195}
196
197/** Destroy task.
198 *
199 * @param t Task to be destroyed.
200 */
201void task_destroy(task_t *t)
202{
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 */
213 task_destroy_arch(t);
214
215 /*
216 * Free up dynamically allocated state.
217 */
218 btree_destroy(&t->futexes);
219
220 /*
221 * Drop our reference to the address space.
222 */
223 if (atomic_predec(&t->as->refcount) == 0)
224 as_destroy(t->as);
225
226 free(t);
227 TASK = NULL;
228}
229
230/** Create new task with 1 thread and run it
231 *
232 * @param program_addr Address of program executable image.
233 * @param name Program name.
234 *
235 * @return Task of the running program or NULL on error.
236 */
237task_t *task_run_program(void *program_addr, char *name)
238{
239 as_t *as;
240 as_area_t *a;
241 int rc;
242 thread_t *t;
243 task_t *task;
244 uspace_arg_t *kernel_uarg;
245
246 as = as_create(0);
247 ASSERT(as);
248
249 rc = elf_load((elf_header_t *) program_addr, as);
250 if (rc != EE_OK) {
251 as_destroy(as);
252 return NULL;
253 }
254
255 kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
256 kernel_uarg->uspace_entry =
257 (void *) ((elf_header_t *) program_addr)->e_entry;
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;
262
263 task = task_create(as, name);
264 ASSERT(task);
265
266 /*
267 * Create the data as_area.
268 */
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);
272
273 /*
274 * Create the main thread.
275 */
276 t = thread_create(uinit, kernel_uarg, task, THREAD_FLAG_USPACE,
277 "uinit", false);
278 ASSERT(t);
279
280 thread_ready(t);
281
282 return task;
283}
284
285/** Syscall for reading task ID from userspace.
286 *
287 * @param uspace_task_id Userspace address of 8-byte buffer where to store
288 * current task ID.
289 *
290 * @return 0 on success or an error code from @ref errno.h.
291 */
292unative_t sys_task_get_id(task_id_t *uspace_task_id)
293{
294 /*
295 * No need to acquire lock on TASK because taskid
296 * remains constant for the lifespan of the task.
297 */
298 return (unative_t) copy_to_uspace(uspace_task_id, &TASK->taskid,
299 sizeof(TASK->taskid));
300}
301
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
318/** Get accounting data of given task.
319 *
320 * Note that task lock of 't' must be already held and
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);
337 /* Process only counted threads */
338 if (!thr->uncounted) {
339 if (thr == THREAD) {
340 /* Update accounting of current thread */
341 thread_update_accounting();
342 }
343 ret += thr->cycles;
344 }
345 spinlock_unlock(&thr->lock);
346 }
347
348 return ret;
349}
350
351/** Kill task.
352 *
353 * This function is idempotent.
354 * It signals all the task's threads to bail it out.
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;
365
366 if (id == 1)
367 return EPERM;
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 }
376 spinlock_unlock(&tasks_lock);
377
378 /*
379 * Interrupt all threads except ktaskclnp.
380 */
381 spinlock_lock(&ta->lock);
382 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
383 thread_t *thr;
384 bool sleeping = false;
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)
395 waitq_interrupt_sleep(thr);
396 }
397 spinlock_unlock(&ta->lock);
398 interrupts_restore(ipl);
399
400 return 0;
401}
402
403/** Print task list */
404void task_print_list(void)
405{
406 link_t *cur;
407 ipl_t ipl;
408
409 /* Messing with task structures, avoid deadlock */
410 ipl = interrupts_disable();
411 spinlock_lock(&tasks_lock);
412
413 printf("taskid name ctx address as cycles threads "
414 "calls callee\n");
415 printf("------ ---------- --- ---------- ---------- ---------- ------- "
416 "------ ------>\n");
417
418 for (cur = tasks_btree.leaf_head.next; cur != &tasks_btree.leaf_head;
419 cur = cur->next) {
420 btree_node_t *node;
421 unsigned int i;
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);
431
432 uint64_t cycles;
433 char suffix;
434 order(task_get_accounting(t), &cycles, &suffix);
435
436 printf("%-6llu %-10s %-3ld %#10zx %#10zx %9llu%c %7zd "
437 "%6zd", t->taskid, t->name, t->context, t, t->as,
438 cycles, suffix, t->refcount,
439 atomic_get(&t->active_calls));
440 for (j = 0; j < IPC_MAX_PHONES; j++) {
441 if (t->phones[j].callee)
442 printf(" %zd:%#zx", j,
443 t->phones[j].callee);
444 }
445 printf("\n");
446
447 spinlock_unlock(&t->lock);
448 }
449 }
450
451 spinlock_unlock(&tasks_lock);
452 interrupts_restore(ipl);
453}
454
455/** @}
456 */
Note: See TracBrowser for help on using the repository browser.