source: mainline/generic/src/proc/task.c@ 2569ec90

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

In task_kill(), remove the task from the tasks_btree before proceeding.
Thus, when the kernel finds the task in the tasks_btree and locks it before
releasing tasks_lock, it is guaranteed that the task will not be destroyed
until the lock is held. If the kernel needs to unlock the task, do some operation
and lock it again, it should increase its refcount before doing so. In that case,
when releasing the lock, it must decrement the refcount and if it reaches
zero, it must call task_destroy().

  • Property mode set to 100644
File size: 9.4 KB
RevLine 
[f761f1eb]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
[9179d0a]29/**
30 * @file task.c
31 * @brief Task management.
32 */
33
[5be1923]34#include <main/uinit.h>
[f761f1eb]35#include <proc/thread.h>
36#include <proc/task.h>
[0f250f9]37#include <proc/uarg.h>
[20d50a1]38#include <mm/as.h>
[085d973]39#include <mm/slab.h>
[f761f1eb]40#include <synch/spinlock.h>
41#include <arch.h>
42#include <panic.h>
[7f6e755]43#include <adt/btree.h>
[5c9a08b]44#include <adt/list.h>
[6d9c49a]45#include <ipc/ipc.h>
[1077d91]46#include <security/cap.h>
[6d9c49a]47#include <memstr.h>
[37c57f2]48#include <print.h>
[5be1923]49#include <elf.h>
[7509ddc]50#include <errno.h>
[e3c762cd]51#include <syscall/copy.h>
[8e5e78f]52
[a84af84]53#ifndef LOADED_PROG_STACK_PAGES_NO
54#define LOADED_PROG_STACK_PAGES_NO 1
55#endif
[8e5e78f]56
[dc747e3]57SPINLOCK_INITIALIZE(tasks_lock);
[7f6e755]58btree_t tasks_btree;
[286e03d]59static task_id_t task_counter = 0;
[70527f1]60
[b91bb65]61static void ktaskclnp(void *arg);
62static void ktaskkill(void *arg);
[7509ddc]63
[70527f1]64/** Initialize tasks
65 *
66 * Initialize kernel tasks support.
67 *
68 */
[f761f1eb]69void task_init(void)
70{
[43114c5]71 TASK = NULL;
[7f6e755]72 btree_create(&tasks_btree);
[f761f1eb]73}
74
[70527f1]75
76/** Create new task
77 *
78 * Create new task with no threads.
79 *
[20d50a1]80 * @param as Task's address space.
[ff14c520]81 * @param name Symbolic name.
[70527f1]82 *
[5be1923]83 * @return New task's structure
[70527f1]84 *
85 */
[ff14c520]86task_t *task_create(as_t *as, char *name)
[f761f1eb]87{
[22f7769]88 ipl_t ipl;
[f761f1eb]89 task_t *ta;
[2ba7810]90 int i;
[f761f1eb]91
[bb68433]92 ta = (task_t *) malloc(sizeof(task_t), 0);
93
[963074b3]94 task_create_arch(ta);
95
[bb68433]96 spinlock_initialize(&ta->lock, "task_ta_lock");
97 list_initialize(&ta->th_head);
98 ta->as = as;
[ff14c520]99 ta->name = name;
[b91bb65]100 ta->main_thread = NULL;
[7509ddc]101 ta->refcount = 0;
102
[1077d91]103 ta->capabilities = 0;
[7509ddc]104 ta->accept_new_threads = true;
[2ba7810]105
[6d9c49a]106 ipc_answerbox_init(&ta->answerbox);
[2ba7810]107 for (i=0; i < IPC_MAX_PHONES;i++)
108 ipc_phone_init(&ta->phones[i]);
[e74cb73]109 if (ipc_phone_0)
[2ba7810]110 ipc_phone_connect(&ta->phones[0], ipc_phone_0);
[5f62ef9]111 atomic_set(&ta->active_calls, 0);
[4fded58]112
113 mutex_initialize(&ta->futexes_lock);
114 btree_create(&ta->futexes);
[bb68433]115
116 ipl = interrupts_disable();
[482826d]117
118 /*
119 * Increment address space reference count.
120 * TODO: Reconsider the locking scheme.
121 */
122 mutex_lock(&as->lock);
123 as->refcount++;
124 mutex_unlock(&as->lock);
125
[bb68433]126 spinlock_lock(&tasks_lock);
[286e03d]127
128 ta->taskid = ++task_counter;
[b7f364e]129 btree_insert(&tasks_btree, (btree_key_t) ta->taskid, (void *) ta, NULL);
[286e03d]130
[bb68433]131 spinlock_unlock(&tasks_lock);
132 interrupts_restore(ipl);
133
[f761f1eb]134 return ta;
135}
136
[7509ddc]137/** Destroy task.
138 *
139 * @param t Task to be destroyed.
140 */
141void task_destroy(task_t *t)
142{
[31e8ddd]143 task_destroy_arch(t);
144 btree_destroy(&t->futexes);
145
146 mutex_lock_active(&t->as->lock);
147 if (--t->as->refcount == 0) {
148 mutex_unlock(&t->as->lock);
149 as_destroy(t->as);
150 /*
151 * t->as is destroyed.
152 */
153 } else {
154 mutex_unlock(&t->as->lock);
155 }
156
157 free(t);
158 TASK = NULL;
[7509ddc]159}
160
[5be1923]161/** Create new task with 1 thread and run it
[ff14c520]162 *
[9179d0a]163 * @param program_addr Address of program executable image.
[ff14c520]164 * @param name Program name.
[5be1923]165 *
[7f0837c]166 * @return Task of the running program or NULL on error.
[5be1923]167 */
[ff14c520]168task_t * task_run_program(void *program_addr, char *name)
[5be1923]169{
170 as_t *as;
171 as_area_t *a;
172 int rc;
[b91bb65]173 thread_t *t1, *t2;
[5be1923]174 task_t *task;
[0f250f9]175 uspace_arg_t *kernel_uarg;
[5be1923]176
177 as = as_create(0);
[a0bb10ef]178 ASSERT(as);
[5be1923]179
[649799a]180 rc = elf_load((elf_header_t *) program_addr, as);
[5be1923]181 if (rc != EE_OK) {
[482826d]182 as_destroy(as);
[5be1923]183 return NULL;
184 }
185
[0f250f9]186 kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
187 kernel_uarg->uspace_entry = (void *) ((elf_header_t *) program_addr)->e_entry;
188 kernel_uarg->uspace_stack = (void *) USTACK_ADDRESS;
189 kernel_uarg->uspace_thread_function = NULL;
190 kernel_uarg->uspace_thread_arg = NULL;
191 kernel_uarg->uspace_uarg = NULL;
[9f52563]192
[ff14c520]193 task = task_create(as, name);
[a0bb10ef]194 ASSERT(task);
195
[5be1923]196 /*
197 * Create the data as_area.
198 */
[0ee077ee]199 a = as_area_create(as, AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE,
200 LOADED_PROG_STACK_PAGES_NO*PAGE_SIZE,
[8182031]201 USTACK_ADDRESS, AS_AREA_ATTR_NONE, &anon_backend, NULL);
[5be1923]202
[b91bb65]203 /*
204 * Create the main thread.
205 */
206 t1 = thread_create(uinit, kernel_uarg, task, 0, "uinit");
207 ASSERT(t1);
[a0bb10ef]208
[b91bb65]209 /*
210 * Create killer thread for the new task.
211 */
212 t2 = thread_create(ktaskkill, t1, task, 0, "ktaskkill");
213 ASSERT(t2);
214 thread_ready(t2);
215
216 thread_ready(t1);
217
[5be1923]218 return task;
219}
[37c57f2]220
[ec55358]221/** Syscall for reading task ID from userspace.
222 *
[9179d0a]223 * @param uspace_task_id Userspace address of 8-byte buffer where to store current task ID.
[ec55358]224 *
[e3c762cd]225 * @return 0 on success or an error code from @ref errno.h.
[ec55358]226 */
[24f3874]227__native sys_task_get_id(task_id_t *uspace_task_id)
[ec55358]228{
229 /*
230 * No need to acquire lock on TASK because taskid
231 * remains constant for the lifespan of the task.
232 */
[e3c762cd]233 return (__native) copy_to_uspace(uspace_task_id, &TASK->taskid, sizeof(TASK->taskid));
[ec55358]234}
235
[9a8d91b]236/** Find task structure corresponding to task ID.
237 *
238 * The tasks_lock must be already held by the caller of this function
239 * and interrupts must be disabled.
240 *
241 * @param id Task ID.
242 *
243 * @return Task structure address or NULL if there is no such task ID.
244 */
245task_t *task_find_by_id(task_id_t id)
246{
247 btree_node_t *leaf;
248
249 return (task_t *) btree_search(&tasks_btree, (btree_key_t) id, &leaf);
250}
251
[7509ddc]252/** Kill task.
253 *
254 * @param id ID of the task to be killed.
255 *
256 * @return 0 on success or an error code from errno.h
257 */
258int task_kill(task_id_t id)
259{
260 ipl_t ipl;
261 task_t *ta;
262 thread_t *t;
263 link_t *cur;
264
265 ipl = interrupts_disable();
266 spinlock_lock(&tasks_lock);
267
268 if (!(ta = task_find_by_id(id))) {
269 spinlock_unlock(&tasks_lock);
270 interrupts_restore(ipl);
271 return ENOENT;
272 }
[2569ec90]273
[7509ddc]274 spinlock_lock(&ta->lock);
275 ta->refcount++;
276 spinlock_unlock(&ta->lock);
[31e8ddd]277
[2569ec90]278 btree_remove(&tasks_btree, ta->taskid, NULL);
[31e8ddd]279 spinlock_unlock(&tasks_lock);
[7509ddc]280
[34dcd3f]281 t = thread_create(ktaskclnp, NULL, ta, 0, "ktaskclnp");
[7509ddc]282
283 spinlock_lock(&ta->lock);
[34dcd3f]284 ta->accept_new_threads = false;
[7509ddc]285 ta->refcount--;
[b91bb65]286
287 /*
288 * Interrupt all threads except this one.
289 */
[7509ddc]290 for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
291 thread_t *thr;
292 bool sleeping = false;
293
294 thr = list_get_instance(cur, thread_t, th_link);
295 if (thr == t)
296 continue;
297
298 spinlock_lock(&thr->lock);
299 thr->interrupted = true;
300 if (thr->state == Sleeping)
301 sleeping = true;
302 spinlock_unlock(&thr->lock);
303
304 if (sleeping)
305 waitq_interrupt_sleep(thr);
306 }
307
[34dcd3f]308 spinlock_unlock(&ta->lock);
309 interrupts_restore(ipl);
[7509ddc]310
[34dcd3f]311 if (t)
312 thread_ready(t);
313
[7509ddc]314 return 0;
315}
316
[37c57f2]317/** Print task list */
318void task_print_list(void)
319{
320 link_t *cur;
321 ipl_t ipl;
322
323 /* Messing with thread structures, avoid deadlock */
324 ipl = interrupts_disable();
325 spinlock_lock(&tasks_lock);
326
[7f6e755]327 for (cur = tasks_btree.leaf_head.next; cur != &tasks_btree.leaf_head; cur = cur->next) {
328 btree_node_t *node;
329 int i;
330
331 node = list_get_instance(cur, btree_node_t, leaf_link);
332 for (i = 0; i < node->keys; i++) {
333 task_t *t;
334 int j;
335
336 t = (task_t *) node->value[i];
337
338 spinlock_lock(&t->lock);
[c4e4507]339 printf("%s(%lld): address=%#zX, as=%#zX, ActiveCalls: %zd",
340 t->name, t->taskid, t, t->as, atomic_get(&t->active_calls));
[7f6e755]341 for (j=0; j < IPC_MAX_PHONES; j++) {
342 if (t->phones[j].callee)
[280a27e]343 printf(" Ph(%zd): %#zX ", j, t->phones[j].callee);
[7f6e755]344 }
345 printf("\n");
346 spinlock_unlock(&t->lock);
[37c57f2]347 }
348 }
349
350 spinlock_unlock(&tasks_lock);
351 interrupts_restore(ipl);
352}
[7509ddc]353
[b91bb65]354/** Kernel thread used to cleanup the task after it is killed. */
[34dcd3f]355void ktaskclnp(void *arg)
[7509ddc]356{
[34dcd3f]357 ipl_t ipl;
[b91bb65]358 thread_t *t = NULL, *main_thread;
[34dcd3f]359 link_t *cur;
360
361 thread_detach(THREAD);
362
363loop:
364 ipl = interrupts_disable();
365 spinlock_lock(&TASK->lock);
366
[b91bb65]367 main_thread = TASK->main_thread;
368
[34dcd3f]369 /*
370 * Find a thread to join.
371 */
372 for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
373 t = list_get_instance(cur, thread_t, th_link);
374 if (t == THREAD)
375 continue;
[b91bb65]376 else if (t == main_thread)
377 continue;
[34dcd3f]378 else
379 break;
380 }
381
382 spinlock_unlock(&TASK->lock);
383 interrupts_restore(ipl);
384
385 if (t != THREAD) {
[b91bb65]386 ASSERT(t != main_thread); /* uninit is joined and detached in ktaskkill */
[34dcd3f]387 thread_join(t);
388 thread_detach(t);
[b91bb65]389 goto loop; /* go for another thread */
[34dcd3f]390 }
391
392 /*
393 * Now there are no other threads in this task
394 * and no new threads can be created.
395 */
396
[e090e1bc]397 ipc_cleanup();
398 futex_cleanup();
[7509ddc]399}
[b91bb65]400
401/** Kernel task used to kill a userspace task when its main thread exits.
402 *
403 * This thread waits until the main userspace thread (i.e. uninit) exits.
404 * When this happens, the task is killed.
405 *
406 * @param arg Pointer to the thread structure of the task's main thread.
407 */
408void ktaskkill(void *arg)
409{
410 thread_t *t = (thread_t *) arg;
411
412 /*
413 * Userspace threads cannot detach themselves,
414 * therefore the thread pointer is guaranteed to be valid.
415 */
416 thread_join(t); /* sleep uninterruptibly here! */
417 thread_detach(t);
418 task_kill(TASK->taskid);
419}
Note: See TracBrowser for help on using the repository browser.