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

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

Link each phone to its containing task.

This makes it possible to set the call's sender reliably using just the
info stored in the phone used to make the call.

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * Copyright (c) 2010 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 <proc/thread.h>
39#include <proc/task.h>
40#include <mm/as.h>
41#include <mm/slab.h>
42#include <atomic.h>
43#include <synch/spinlock.h>
44#include <synch/waitq.h>
45#include <arch.h>
46#include <arch/barrier.h>
47#include <adt/avl.h>
48#include <adt/btree.h>
49#include <adt/list.h>
50#include <ipc/ipc.h>
51#include <ipc/ipcrsc.h>
52#include <ipc/event.h>
53#include <print.h>
54#include <errno.h>
55#include <func.h>
56#include <str.h>
57#include <memstr.h>
58#include <syscall/copy.h>
59#include <macros.h>
60
61/** Spinlock protecting the tasks_tree AVL tree. */
62IRQ_SPINLOCK_INITIALIZE(tasks_lock);
63
64/** AVL tree of active tasks.
65 *
66 * The task is guaranteed to exist after it was found in the tasks_tree as
67 * long as:
68 *
69 * @li the tasks_lock is held,
70 * @li the task's lock is held when task's lock is acquired before releasing
71 * tasks_lock or
72 * @li the task's refcount is greater than 0
73 *
74 */
75avltree_t tasks_tree;
76
77static task_id_t task_counter = 0;
78
79static slab_cache_t *task_slab;
80
81/* Forward declarations. */
82static void task_kill_internal(task_t *);
83static int tsk_constructor(void *, unsigned int);
84
85/** Initialize kernel tasks support.
86 *
87 */
88void task_init(void)
89{
90 TASK = NULL;
91 avltree_create(&tasks_tree);
92 task_slab = slab_cache_create("task_t", sizeof(task_t), 0,
93 tsk_constructor, NULL, 0);
94}
95
96/** Task finish walker.
97 *
98 * The idea behind this walker is to kill and count all tasks different from
99 * TASK.
100 *
101 */
102static bool task_done_walker(avltree_node_t *node, void *arg)
103{
104 task_t *task = avltree_get_instance(node, task_t, tasks_tree_node);
105 size_t *cnt = (size_t *) arg;
106
107 if (task != TASK) {
108 (*cnt)++;
109
110#ifdef CONFIG_DEBUG
111 printf("[%"PRIu64"] ", task->taskid);
112#endif
113
114 task_kill_internal(task);
115 }
116
117 /* Continue the walk */
118 return true;
119}
120
121/** Kill all tasks except the current task.
122 *
123 */
124void task_done(void)
125{
126 size_t tasks_left;
127
128 /* Repeat until there are any tasks except TASK */
129 do {
130#ifdef CONFIG_DEBUG
131 printf("Killing tasks... ");
132#endif
133
134 irq_spinlock_lock(&tasks_lock, true);
135 tasks_left = 0;
136 avltree_walk(&tasks_tree, task_done_walker, &tasks_left);
137 irq_spinlock_unlock(&tasks_lock, true);
138
139 thread_sleep(1);
140
141#ifdef CONFIG_DEBUG
142 printf("\n");
143#endif
144 } while (tasks_left > 0);
145}
146
147int tsk_constructor(void *obj, unsigned int kmflags)
148{
149 task_t *task = (task_t *) obj;
150
151 atomic_set(&task->refcount, 0);
152 atomic_set(&task->lifecount, 0);
153
154 irq_spinlock_initialize(&task->lock, "task_t_lock");
155 mutex_initialize(&task->futexes_lock, MUTEX_PASSIVE);
156
157 list_initialize(&task->threads);
158
159 ipc_answerbox_init(&task->answerbox, task);
160
161 size_t i;
162 for (i = 0; i < IPC_MAX_PHONES; i++)
163 ipc_phone_init(&task->phones[i], task);
164
165 spinlock_initialize(&task->active_calls_lock, "active_calls_lock");
166 list_initialize(&task->active_calls);
167
168#ifdef CONFIG_UDEBUG
169 /* Init kbox stuff */
170 task->kb.thread = NULL;
171 ipc_answerbox_init(&task->kb.box, task);
172 mutex_initialize(&task->kb.cleanup_lock, MUTEX_PASSIVE);
173#endif
174
175 return 0;
176}
177
178/** Create new task with no threads.
179 *
180 * @param as Task's address space.
181 * @param name Symbolic name (a copy is made).
182 *
183 * @return New task's structure.
184 *
185 */
186task_t *task_create(as_t *as, const char *name)
187{
188 task_t *task = (task_t *) slab_alloc(task_slab, 0);
189 task_create_arch(task);
190
191 task->as = as;
192 str_cpy(task->name, TASK_NAME_BUFLEN, name);
193
194 task->container = CONTAINER;
195 task->capabilities = 0;
196 task->ucycles = 0;
197 task->kcycles = 0;
198
199 task->ipc_info.call_sent = 0;
200 task->ipc_info.call_received = 0;
201 task->ipc_info.answer_sent = 0;
202 task->ipc_info.answer_received = 0;
203 task->ipc_info.irq_notif_received = 0;
204 task->ipc_info.forwarded = 0;
205
206 event_task_init(task);
207
208 task->answerbox.active = true;
209
210#ifdef CONFIG_UDEBUG
211 /* Init debugging stuff */
212 udebug_task_init(&task->udebug);
213
214 /* Init kbox stuff */
215 task->kb.box.active = true;
216 task->kb.finished = false;
217#endif
218
219 if ((ipc_phone_0) &&
220 (container_check(ipc_phone_0->task->container, task->container)))
221 (void) ipc_phone_connect(&task->phones[0], ipc_phone_0);
222
223 btree_create(&task->futexes);
224
225 /*
226 * Get a reference to the address space.
227 */
228 as_hold(task->as);
229
230 irq_spinlock_lock(&tasks_lock, true);
231
232 task->taskid = ++task_counter;
233 avltree_node_initialize(&task->tasks_tree_node);
234 task->tasks_tree_node.key = task->taskid;
235 avltree_insert(&tasks_tree, &task->tasks_tree_node);
236
237 irq_spinlock_unlock(&tasks_lock, true);
238
239 return task;
240}
241
242/** Destroy task.
243 *
244 * @param task Task to be destroyed.
245 *
246 */
247void task_destroy(task_t *task)
248{
249 /*
250 * Remove the task from the task B+tree.
251 */
252 irq_spinlock_lock(&tasks_lock, true);
253 avltree_delete(&tasks_tree, &task->tasks_tree_node);
254 irq_spinlock_unlock(&tasks_lock, true);
255
256 /*
257 * Perform architecture specific task destruction.
258 */
259 task_destroy_arch(task);
260
261 /*
262 * Free up dynamically allocated state.
263 */
264 btree_destroy(&task->futexes);
265
266 /*
267 * Drop our reference to the address space.
268 */
269 as_release(task->as);
270
271 slab_free(task_slab, task);
272}
273
274/** Hold a reference to a task.
275 *
276 * Holding a reference to a task prevents destruction of that task.
277 *
278 * @param task Task to be held.
279 *
280 */
281void task_hold(task_t *task)
282{
283 atomic_inc(&task->refcount);
284}
285
286/** Release a reference to a task.
287 *
288 * The last one to release a reference to a task destroys the task.
289 *
290 * @param task Task to be released.
291 *
292 */
293void task_release(task_t *task)
294{
295 if ((atomic_predec(&task->refcount)) == 0)
296 task_destroy(task);
297}
298
299#ifdef __32_BITS__
300
301/** Syscall for reading task ID from userspace (32 bits)
302 *
303 * @param uspace_taskid Pointer to user-space buffer
304 * where to store current task ID.
305 *
306 * @return Zero on success or an error code from @ref errno.h.
307 *
308 */
309sysarg_t sys_task_get_id(sysarg64_t *uspace_taskid)
310{
311 /*
312 * No need to acquire lock on TASK because taskid remains constant for
313 * the lifespan of the task.
314 */
315 return (sysarg_t) copy_to_uspace(uspace_taskid, &TASK->taskid,
316 sizeof(TASK->taskid));
317}
318
319#endif /* __32_BITS__ */
320
321#ifdef __64_BITS__
322
323/** Syscall for reading task ID from userspace (64 bits)
324 *
325 * @return Current task ID.
326 *
327 */
328sysarg_t sys_task_get_id(void)
329{
330 /*
331 * No need to acquire lock on TASK because taskid remains constant for
332 * the lifespan of the task.
333 */
334 return TASK->taskid;
335}
336
337#endif /* __64_BITS__ */
338
339/** Syscall for setting the task name.
340 *
341 * The name simplifies identifying the task in the task list.
342 *
343 * @param name The new name for the task. (typically the same
344 * as the command used to execute it).
345 *
346 * @return 0 on success or an error code from @ref errno.h.
347 *
348 */
349sysarg_t sys_task_set_name(const char *uspace_name, size_t name_len)
350{
351 char namebuf[TASK_NAME_BUFLEN];
352
353 /* Cap length of name and copy it from userspace. */
354 if (name_len > TASK_NAME_BUFLEN - 1)
355 name_len = TASK_NAME_BUFLEN - 1;
356
357 int rc = copy_from_uspace(namebuf, uspace_name, name_len);
358 if (rc != 0)
359 return (sysarg_t) rc;
360
361 namebuf[name_len] = '\0';
362
363 /*
364 * As the task name is referenced also from the
365 * threads, lock the threads' lock for the course
366 * of the update.
367 */
368
369 irq_spinlock_lock(&tasks_lock, true);
370 irq_spinlock_lock(&TASK->lock, false);
371 irq_spinlock_lock(&threads_lock, false);
372
373 /* Set task name */
374 str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf);
375
376 irq_spinlock_unlock(&threads_lock, false);
377 irq_spinlock_unlock(&TASK->lock, false);
378 irq_spinlock_unlock(&tasks_lock, true);
379
380 return EOK;
381}
382
383/** Syscall to forcefully terminate a task
384 *
385 * @param uspace_taskid Pointer to task ID in user space.
386 *
387 * @return 0 on success or an error code from @ref errno.h.
388 *
389 */
390sysarg_t sys_task_kill(task_id_t *uspace_taskid)
391{
392 task_id_t taskid;
393 int rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(taskid));
394 if (rc != 0)
395 return (sysarg_t) rc;
396
397 return (sysarg_t) task_kill(taskid);
398}
399
400/** Find task structure corresponding to task ID.
401 *
402 * The tasks_lock must be already held by the caller of this function and
403 * interrupts must be disabled.
404 *
405 * @param id Task ID.
406 *
407 * @return Task structure address or NULL if there is no such task ID.
408 *
409 */
410task_t *task_find_by_id(task_id_t id)
411{
412 ASSERT(interrupts_disabled());
413 ASSERT(irq_spinlock_locked(&tasks_lock));
414
415 avltree_node_t *node =
416 avltree_search(&tasks_tree, (avltree_key_t) id);
417
418 if (node)
419 return avltree_get_instance(node, task_t, tasks_tree_node);
420
421 return NULL;
422}
423
424/** Get accounting data of given task.
425 *
426 * Note that task lock of 'task' must be already held and interrupts must be
427 * already disabled.
428 *
429 * @param task Pointer to the task.
430 * @param ucycles Out pointer to sum of all user cycles.
431 * @param kcycles Out pointer to sum of all kernel cycles.
432 *
433 */
434void task_get_accounting(task_t *task, uint64_t *ucycles, uint64_t *kcycles)
435{
436 ASSERT(interrupts_disabled());
437 ASSERT(irq_spinlock_locked(&task->lock));
438
439 /* Accumulated values of task */
440 uint64_t uret = task->ucycles;
441 uint64_t kret = task->kcycles;
442
443 /* Current values of threads */
444 list_foreach(task->threads, cur) {
445 thread_t *thread = list_get_instance(cur, thread_t, th_link);
446
447 irq_spinlock_lock(&thread->lock, false);
448
449 /* Process only counted threads */
450 if (!thread->uncounted) {
451 if (thread == THREAD) {
452 /* Update accounting of current thread */
453 thread_update_accounting(false);
454 }
455
456 uret += thread->ucycles;
457 kret += thread->kcycles;
458 }
459
460 irq_spinlock_unlock(&thread->lock, false);
461 }
462
463 *ucycles = uret;
464 *kcycles = kret;
465}
466
467static void task_kill_internal(task_t *task)
468{
469 irq_spinlock_lock(&task->lock, false);
470 irq_spinlock_lock(&threads_lock, false);
471
472 /*
473 * Interrupt all threads.
474 */
475
476 list_foreach(task->threads, cur) {
477 thread_t *thread = list_get_instance(cur, thread_t, th_link);
478 bool sleeping = false;
479
480 irq_spinlock_lock(&thread->lock, false);
481
482 thread->interrupted = true;
483 if (thread->state == Sleeping)
484 sleeping = true;
485
486 irq_spinlock_unlock(&thread->lock, false);
487
488 if (sleeping)
489 waitq_interrupt_sleep(thread);
490 }
491
492 irq_spinlock_unlock(&threads_lock, false);
493 irq_spinlock_unlock(&task->lock, false);
494}
495
496/** Kill task.
497 *
498 * This function is idempotent.
499 * It signals all the task's threads to bail it out.
500 *
501 * @param id ID of the task to be killed.
502 *
503 * @return Zero on success or an error code from errno.h.
504 *
505 */
506int task_kill(task_id_t id)
507{
508 if (id == 1)
509 return EPERM;
510
511 irq_spinlock_lock(&tasks_lock, true);
512
513 task_t *task = task_find_by_id(id);
514 if (!task) {
515 irq_spinlock_unlock(&tasks_lock, true);
516 return ENOENT;
517 }
518
519 task_kill_internal(task);
520 irq_spinlock_unlock(&tasks_lock, true);
521
522 return EOK;
523}
524
525/** Kill the currently running task.
526 *
527 * @param notify Send out fault notifications.
528 *
529 * @return Zero on success or an error code from errno.h.
530 *
531 */
532void task_kill_self(bool notify)
533{
534 /*
535 * User space can subscribe for FAULT events to take action
536 * whenever a task faults (to take a dump, run a debugger, etc.).
537 * The notification is always available, but unless udebug is enabled,
538 * that's all you get.
539 */
540 if (notify) {
541 /* Notify the subscriber that a fault occurred. */
542 if (event_notify_3(EVENT_FAULT, false, LOWER32(TASK->taskid),
543 UPPER32(TASK->taskid), (sysarg_t) THREAD) == EOK) {
544#ifdef CONFIG_UDEBUG
545 /* Wait for a debugging session. */
546 udebug_thread_fault();
547#endif
548 }
549 }
550
551 irq_spinlock_lock(&tasks_lock, true);
552 task_kill_internal(TASK);
553 irq_spinlock_unlock(&tasks_lock, true);
554
555 thread_exit();
556}
557
558/** Process syscall to terminate the current task.
559 *
560 * @param notify Send out fault notifications.
561 *
562 */
563sysarg_t sys_task_exit(sysarg_t notify)
564{
565 task_kill_self(notify);
566
567 /* Unreachable */
568 return EOK;
569}
570
571static bool task_print_walker(avltree_node_t *node, void *arg)
572{
573 bool *additional = (bool *) arg;
574 task_t *task = avltree_get_instance(node, task_t, tasks_tree_node);
575 irq_spinlock_lock(&task->lock, false);
576
577 uint64_t ucycles;
578 uint64_t kcycles;
579 char usuffix, ksuffix;
580 task_get_accounting(task, &ucycles, &kcycles);
581 order_suffix(ucycles, &ucycles, &usuffix);
582 order_suffix(kcycles, &kcycles, &ksuffix);
583
584#ifdef __32_BITS__
585 if (*additional)
586 printf("%-8" PRIu64 " %9" PRIua, task->taskid,
587 atomic_get(&task->refcount));
588 else
589 printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %10p %10p"
590 " %9" PRIu64 "%c %9" PRIu64 "%c\n", task->taskid,
591 task->name, task->container, task, task->as,
592 ucycles, usuffix, kcycles, ksuffix);
593#endif
594
595#ifdef __64_BITS__
596 if (*additional)
597 printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
598 "%9" PRIua, task->taskid, ucycles, usuffix, kcycles,
599 ksuffix, atomic_get(&task->refcount));
600 else
601 printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %18p %18p\n",
602 task->taskid, task->name, task->container, task, task->as);
603#endif
604
605 if (*additional) {
606 size_t i;
607 for (i = 0; i < IPC_MAX_PHONES; i++) {
608 if (task->phones[i].callee)
609 printf(" %zu:%p", i, task->phones[i].callee);
610 }
611 printf("\n");
612 }
613
614 irq_spinlock_unlock(&task->lock, false);
615 return true;
616}
617
618/** Print task list
619 *
620 * @param additional Print additional information.
621 *
622 */
623void task_print_list(bool additional)
624{
625 /* Messing with task structures, avoid deadlock */
626 irq_spinlock_lock(&tasks_lock, true);
627
628#ifdef __32_BITS__
629 if (additional)
630 printf("[id ] [threads] [calls] [callee\n");
631 else
632 printf("[id ] [name ] [ctn] [address ] [as ]"
633 " [ucycles ] [kcycles ]\n");
634#endif
635
636#ifdef __64_BITS__
637 if (additional)
638 printf("[id ] [ucycles ] [kcycles ] [threads] [calls]"
639 " [callee\n");
640 else
641 printf("[id ] [name ] [ctn] [address ]"
642 " [as ]\n");
643#endif
644
645 avltree_walk(&tasks_tree, task_print_walker, &additional);
646
647 irq_spinlock_unlock(&tasks_lock, true);
648}
649
650/** @}
651 */
Note: See TracBrowser for help on using the repository browser.