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

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

Make capability handles type-safe

Define distinct pointer types for the handles of the supported
capability types and use them instead of integer handles. This makes it
virtually impossible to pass a non-handle or a handle of different type
instead of the proper handle. Also turn cap_handle_t into an "untyped"
capability handle that can be assigned to and from the "typed" handles.

This commit also fixes a bug in msim-con driver, which wrongly used the
IRQ number instead of the IRQ capability handle to unregister the IRQ.

This commit also fixes the wrong use of the capability handle instead
of error code in libusbhost.

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