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

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

Give the AVL tree walkers the possibility to take an argument.
Each walker is now supposed to return a bool value to support walk termination.

Switch over from the tasks_btree B+tree to tasks_tree AVL tree.
This makes the fix for ticket #48 complete.

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