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

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

Make all accesses to capabilites exclusive

This commit makes sure that all accesses to the capabilities array and other
metadata are protected by a mutex. This is necessary for future resizing of the
capabilities array.

Group task's capabilities by type so that it is possible to visit all
capabilities of the given type effectively.

Provide cap_publish() and cap_unpublish() to automate steps that make the
capability visible/invisible to userspace and insert/remove the capability from
the respective type list.

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