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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5a5269d was 5a5269d, checked in by GitHub <noreply@…>, 6 years ago

Change type of uspace pointers in kernel from pointer type to numeric (#170)

From kernel's perspective, userspace addresses are not valid pointers,
and can only be used in calls to copy_to/from_uspace().
Therefore, we change the type of those arguments and variables to
uspace_addr_t which is an alias for sysarg_t.

This allows the compiler to catch accidental direct accesses to
userspace addresses.

Additionally, to avoid losing the type information in code,
a macro uspace_ptr(type) is used that translates to uspace_addr_t.
I makes no functional difference, but allows keeping the type information
in code in case we implement some sort of static checking for it in the future.

However, ccheck doesn't like that, so instead of using uspace_ptr(char),
we use uspace_ptr_char which is defined as
#define uspace_ptr_char uspace_ptr(char).

  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*
2 * Copyright (c) 2010 Jakub Jermar
3 * Copyright (c) 2018 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup kernel_generic_proc
31 * @{
32 */
33
34/**
35 * @file
36 * @brief Task management.
37 */
38
39#include <assert.h>
40#include <proc/thread.h>
41#include <proc/task.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 <barrier.h>
49#include <adt/list.h>
50#include <adt/odict.h>
51#include <cap/cap.h>
52#include <ipc/ipc.h>
53#include <ipc/ipcrsc.h>
54#include <ipc/event.h>
55#include <stdio.h>
56#include <errno.h>
57#include <halt.h>
58#include <str.h>
59#include <syscall/copy.h>
60#include <macros.h>
61
62/** Spinlock protecting the @c tasks ordered dictionary. */
63IRQ_SPINLOCK_INITIALIZE(tasks_lock);
64
65/** Ordered dictionary of active tasks by task ID.
66 *
67 * Members are task_t structures.
68 *
69 * The task is guaranteed to exist after it was found in the @c tasks
70 * dictionary as long as:
71 *
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 */
78odict_t tasks;
79
80static task_id_t task_counter = 0;
81
82static slab_cache_t *task_cache;
83
84/* Forward declarations. */
85static void task_kill_internal(task_t *);
86static errno_t tsk_constructor(void *, unsigned int);
87static size_t tsk_destructor(void *);
88
89static void *tasks_getkey(odlink_t *);
90static int tasks_cmp(void *, void *);
91
92/** Initialize kernel tasks support.
93 *
94 */
95void task_init(void)
96{
97 TASK = NULL;
98 odict_initialize(&tasks, tasks_getkey, tasks_cmp);
99 task_cache = slab_cache_create("task_t", sizeof(task_t), 0,
100 tsk_constructor, tsk_destructor, 0);
101}
102
103/** Kill all tasks except the current task.
104 *
105 */
106void task_done(void)
107{
108 size_t tasks_left;
109 task_t *task;
110
111 if (ipc_box_0) {
112 task_t *task_0 = ipc_box_0->task;
113 ipc_box_0 = NULL;
114 /*
115 * The first task is held by kinit(), we need to release it or
116 * it will never finish cleanup.
117 */
118 task_release(task_0);
119 }
120
121 /* Repeat until there are any tasks except TASK */
122 do {
123#ifdef CONFIG_DEBUG
124 printf("Killing tasks... ");
125#endif
126 irq_spinlock_lock(&tasks_lock, true);
127 tasks_left = 0;
128
129 task = task_first();
130 while (task != NULL) {
131 if (task != TASK) {
132 tasks_left++;
133#ifdef CONFIG_DEBUG
134 printf("[%" PRIu64 "] ", task->taskid);
135#endif
136 task_kill_internal(task);
137 }
138
139 task = task_next(task);
140 }
141
142 irq_spinlock_unlock(&tasks_lock, true);
143
144 thread_sleep(1);
145
146#ifdef CONFIG_DEBUG
147 printf("\n");
148#endif
149 } while (tasks_left > 0);
150}
151
152errno_t tsk_constructor(void *obj, unsigned int kmflags)
153{
154 task_t *task = (task_t *) obj;
155
156 errno_t rc = caps_task_alloc(task);
157 if (rc != EOK)
158 return rc;
159
160 atomic_store(&task->refcount, 0);
161 atomic_store(&task->lifecount, 0);
162
163 irq_spinlock_initialize(&task->lock, "task_t_lock");
164
165 list_initialize(&task->threads);
166
167 ipc_answerbox_init(&task->answerbox, task);
168
169 spinlock_initialize(&task->active_calls_lock, "active_calls_lock");
170 list_initialize(&task->active_calls);
171
172#ifdef CONFIG_UDEBUG
173 /* Init kbox stuff */
174 task->kb.thread = NULL;
175 ipc_answerbox_init(&task->kb.box, task);
176 mutex_initialize(&task->kb.cleanup_lock, MUTEX_PASSIVE);
177#endif
178
179 return EOK;
180}
181
182size_t tsk_destructor(void *obj)
183{
184 task_t *task = (task_t *) obj;
185
186 caps_task_free(task);
187 return 0;
188}
189
190/** Create new task with no threads.
191 *
192 * @param as Task's address space.
193 * @param name Symbolic name (a copy is made).
194 *
195 * @return New task's structure.
196 *
197 */
198task_t *task_create(as_t *as, const char *name)
199{
200 task_t *task = (task_t *) slab_alloc(task_cache, FRAME_ATOMIC);
201 if (!task)
202 return NULL;
203
204 task_create_arch(task);
205
206 task->as = as;
207 str_cpy(task->name, TASK_NAME_BUFLEN, name);
208
209 task->container = CONTAINER;
210 task->perms = 0;
211 task->ucycles = 0;
212 task->kcycles = 0;
213
214 caps_task_init(task);
215
216 task->ipc_info.call_sent = 0;
217 task->ipc_info.call_received = 0;
218 task->ipc_info.answer_sent = 0;
219 task->ipc_info.answer_received = 0;
220 task->ipc_info.irq_notif_received = 0;
221 task->ipc_info.forwarded = 0;
222
223 event_task_init(task);
224
225 task->answerbox.active = true;
226
227#ifdef CONFIG_UDEBUG
228 /* Init debugging stuff */
229 udebug_task_init(&task->udebug);
230
231 /* Init kbox stuff */
232 task->kb.box.active = true;
233 task->kb.finished = false;
234#endif
235
236 if ((ipc_box_0) &&
237 (container_check(ipc_box_0->task->container, task->container))) {
238 cap_phone_handle_t phone_handle;
239 errno_t rc = phone_alloc(task, true, &phone_handle, NULL);
240 if (rc != EOK) {
241 task->as = NULL;
242 task_destroy_arch(task);
243 slab_free(task_cache, task);
244 return NULL;
245 }
246
247 kobject_t *phone_obj = kobject_get(task, phone_handle,
248 KOBJECT_TYPE_PHONE);
249 (void) ipc_phone_connect(phone_obj->phone, ipc_box_0);
250 }
251
252 irq_spinlock_lock(&tasks_lock, true);
253
254 task->taskid = ++task_counter;
255 odlink_initialize(&task->ltasks);
256 odict_insert(&task->ltasks, &tasks, NULL);
257
258 irq_spinlock_unlock(&tasks_lock, true);
259
260 return task;
261}
262
263/** Destroy task.
264 *
265 * @param task Task to be destroyed.
266 *
267 */
268void task_destroy(task_t *task)
269{
270 /*
271 * Remove the task from the task odict.
272 */
273 irq_spinlock_lock(&tasks_lock, true);
274 odict_remove(&task->ltasks);
275 irq_spinlock_unlock(&tasks_lock, true);
276
277 /*
278 * Perform architecture specific task destruction.
279 */
280 task_destroy_arch(task);
281
282 /*
283 * Drop our reference to the address space.
284 */
285 as_release(task->as);
286
287 slab_free(task_cache, task);
288}
289
290/** Hold a reference to a task.
291 *
292 * Holding a reference to a task prevents destruction of that task.
293 *
294 * @param task Task to be held.
295 *
296 */
297void task_hold(task_t *task)
298{
299 atomic_inc(&task->refcount);
300}
301
302/** Release a reference to a task.
303 *
304 * The last one to release a reference to a task destroys the task.
305 *
306 * @param task Task to be released.
307 *
308 */
309void task_release(task_t *task)
310{
311 if ((atomic_predec(&task->refcount)) == 0)
312 task_destroy(task);
313}
314
315#ifdef __32_BITS__
316
317/** Syscall for reading task ID from userspace (32 bits)
318 *
319 * @param uspace_taskid Pointer to user-space buffer
320 * where to store current task ID.
321 *
322 * @return Zero on success or an error code from @ref errno.h.
323 *
324 */
325sys_errno_t sys_task_get_id(uspace_ptr_sysarg64_t uspace_taskid)
326{
327 /*
328 * No need to acquire lock on TASK because taskid remains constant for
329 * the lifespan of the task.
330 */
331 return (sys_errno_t) copy_to_uspace(uspace_taskid, &TASK->taskid,
332 sizeof(TASK->taskid));
333}
334
335#endif /* __32_BITS__ */
336
337#ifdef __64_BITS__
338
339/** Syscall for reading task ID from userspace (64 bits)
340 *
341 * @return Current task ID.
342 *
343 */
344sysarg_t sys_task_get_id(void)
345{
346 /*
347 * No need to acquire lock on TASK because taskid remains constant for
348 * the lifespan of the task.
349 */
350 return TASK->taskid;
351}
352
353#endif /* __64_BITS__ */
354
355/** Syscall for setting the task name.
356 *
357 * The name simplifies identifying the task in the task list.
358 *
359 * @param name The new name for the task. (typically the same
360 * as the command used to execute it).
361 *
362 * @return 0 on success or an error code from @ref errno.h.
363 *
364 */
365sys_errno_t sys_task_set_name(const uspace_ptr_char uspace_name, size_t name_len)
366{
367 char namebuf[TASK_NAME_BUFLEN];
368
369 /* Cap length of name and copy it from userspace. */
370 if (name_len > TASK_NAME_BUFLEN - 1)
371 name_len = TASK_NAME_BUFLEN - 1;
372
373 errno_t rc = copy_from_uspace(namebuf, uspace_name, name_len);
374 if (rc != EOK)
375 return (sys_errno_t) rc;
376
377 namebuf[name_len] = '\0';
378
379 /*
380 * As the task name is referenced also from the
381 * threads, lock the threads' lock for the course
382 * of the update.
383 */
384
385 irq_spinlock_lock(&tasks_lock, true);
386 irq_spinlock_lock(&TASK->lock, false);
387 irq_spinlock_lock(&threads_lock, false);
388
389 /* Set task name */
390 str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf);
391
392 irq_spinlock_unlock(&threads_lock, false);
393 irq_spinlock_unlock(&TASK->lock, false);
394 irq_spinlock_unlock(&tasks_lock, true);
395
396 return EOK;
397}
398
399/** Syscall to forcefully terminate a task
400 *
401 * @param uspace_taskid Pointer to task ID in user space.
402 *
403 * @return 0 on success or an error code from @ref errno.h.
404 *
405 */
406sys_errno_t sys_task_kill(uspace_ptr_task_id_t uspace_taskid)
407{
408 task_id_t taskid;
409 errno_t rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(taskid));
410 if (rc != EOK)
411 return (sys_errno_t) rc;
412
413 return (sys_errno_t) task_kill(taskid);
414}
415
416/** Find task structure corresponding to task ID.
417 *
418 * The tasks_lock must be already held by the caller of this function and
419 * interrupts must be disabled.
420 *
421 * @param id Task ID.
422 *
423 * @return Task structure address or NULL if there is no such task ID.
424 *
425 */
426task_t *task_find_by_id(task_id_t id)
427{
428 assert(interrupts_disabled());
429 assert(irq_spinlock_locked(&tasks_lock));
430
431 odlink_t *odlink = odict_find_eq(&tasks, &id, NULL);
432 if (odlink != NULL)
433 return odict_get_instance(odlink, task_t, ltasks);
434
435 return NULL;
436}
437
438/** Get count of tasks.
439 *
440 * @return Number of tasks in the system
441 */
442size_t task_count(void)
443{
444 assert(interrupts_disabled());
445 assert(irq_spinlock_locked(&tasks_lock));
446
447 return odict_count(&tasks);
448}
449
450/** Get first task (task with lowest ID).
451 *
452 * @return Pointer to first task or @c NULL if there are none.
453 */
454task_t *task_first(void)
455{
456 odlink_t *odlink;
457
458 assert(interrupts_disabled());
459 assert(irq_spinlock_locked(&tasks_lock));
460
461 odlink = odict_first(&tasks);
462 if (odlink == NULL)
463 return NULL;
464
465 return odict_get_instance(odlink, task_t, ltasks);
466}
467
468/** Get next task (with higher task ID).
469 *
470 * @param cur Current task
471 * @return Pointer to next task or @c NULL if there are no more tasks.
472 */
473task_t *task_next(task_t *cur)
474{
475 odlink_t *odlink;
476
477 assert(interrupts_disabled());
478 assert(irq_spinlock_locked(&tasks_lock));
479
480 odlink = odict_next(&cur->ltasks, &tasks);
481 if (odlink == NULL)
482 return NULL;
483
484 return odict_get_instance(odlink, task_t, ltasks);
485}
486
487/** Get accounting data of given task.
488 *
489 * Note that task lock of 'task' must be already held and interrupts must be
490 * already disabled.
491 *
492 * @param task Pointer to the task.
493 * @param ucycles Out pointer to sum of all user cycles.
494 * @param kcycles Out pointer to sum of all kernel cycles.
495 *
496 */
497void task_get_accounting(task_t *task, uint64_t *ucycles, uint64_t *kcycles)
498{
499 assert(interrupts_disabled());
500 assert(irq_spinlock_locked(&task->lock));
501
502 /* Accumulated values of task */
503 uint64_t uret = task->ucycles;
504 uint64_t kret = task->kcycles;
505
506 /* Current values of threads */
507 list_foreach(task->threads, th_link, thread_t, thread) {
508 irq_spinlock_lock(&thread->lock, false);
509
510 /* Process only counted threads */
511 if (!thread->uncounted) {
512 if (thread == THREAD) {
513 /* Update accounting of current thread */
514 thread_update_accounting(false);
515 }
516
517 uret += thread->ucycles;
518 kret += thread->kcycles;
519 }
520
521 irq_spinlock_unlock(&thread->lock, false);
522 }
523
524 *ucycles = uret;
525 *kcycles = kret;
526}
527
528static void task_kill_internal(task_t *task)
529{
530 irq_spinlock_lock(&task->lock, false);
531 irq_spinlock_lock(&threads_lock, false);
532
533 /*
534 * Interrupt all threads.
535 */
536
537 list_foreach(task->threads, th_link, thread_t, thread) {
538 bool sleeping = false;
539
540 irq_spinlock_lock(&thread->lock, false);
541
542 thread->interrupted = true;
543 if (thread->state == Sleeping)
544 sleeping = true;
545
546 irq_spinlock_unlock(&thread->lock, false);
547
548 if (sleeping)
549 waitq_interrupt_sleep(thread);
550 }
551
552 irq_spinlock_unlock(&threads_lock, false);
553 irq_spinlock_unlock(&task->lock, false);
554}
555
556/** Kill task.
557 *
558 * This function is idempotent.
559 * It signals all the task's threads to bail it out.
560 *
561 * @param id ID of the task to be killed.
562 *
563 * @return Zero on success or an error code from errno.h.
564 *
565 */
566errno_t task_kill(task_id_t id)
567{
568 if (id == 1)
569 return EPERM;
570
571 irq_spinlock_lock(&tasks_lock, true);
572
573 task_t *task = task_find_by_id(id);
574 if (!task) {
575 irq_spinlock_unlock(&tasks_lock, true);
576 return ENOENT;
577 }
578
579 task_kill_internal(task);
580 irq_spinlock_unlock(&tasks_lock, true);
581
582 return EOK;
583}
584
585/** Kill the currently running task.
586 *
587 * @param notify Send out fault notifications.
588 *
589 * @return Zero on success or an error code from errno.h.
590 *
591 */
592void task_kill_self(bool notify)
593{
594 /*
595 * User space can subscribe for FAULT events to take action
596 * whenever a task faults (to take a dump, run a debugger, etc.).
597 * The notification is always available, but unless udebug is enabled,
598 * that's all you get.
599 */
600 if (notify) {
601 /* Notify the subscriber that a fault occurred. */
602 if (event_notify_3(EVENT_FAULT, false, LOWER32(TASK->taskid),
603 UPPER32(TASK->taskid), (sysarg_t) THREAD) == EOK) {
604#ifdef CONFIG_UDEBUG
605 /* Wait for a debugging session. */
606 udebug_thread_fault();
607#endif
608 }
609 }
610
611 irq_spinlock_lock(&tasks_lock, true);
612 task_kill_internal(TASK);
613 irq_spinlock_unlock(&tasks_lock, true);
614
615 thread_exit();
616}
617
618/** Process syscall to terminate the current task.
619 *
620 * @param notify Send out fault notifications.
621 *
622 */
623sys_errno_t sys_task_exit(sysarg_t notify)
624{
625 task_kill_self(notify);
626 unreachable();
627}
628
629static void task_print(task_t *task, bool additional)
630{
631 irq_spinlock_lock(&task->lock, false);
632
633 uint64_t ucycles;
634 uint64_t kcycles;
635 char usuffix, ksuffix;
636 task_get_accounting(task, &ucycles, &kcycles);
637 order_suffix(ucycles, &ucycles, &usuffix);
638 order_suffix(kcycles, &kcycles, &ksuffix);
639
640#ifdef __32_BITS__
641 if (additional)
642 printf("%-8" PRIu64 " %9zu", task->taskid,
643 atomic_load(&task->refcount));
644 else
645 printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %10p %10p"
646 " %9" PRIu64 "%c %9" PRIu64 "%c\n", task->taskid,
647 task->name, task->container, task, task->as,
648 ucycles, usuffix, kcycles, ksuffix);
649#endif
650
651#ifdef __64_BITS__
652 if (additional)
653 printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
654 "%9zu\n", task->taskid, ucycles, usuffix, kcycles,
655 ksuffix, atomic_load(&task->refcount));
656 else
657 printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %18p %18p\n",
658 task->taskid, task->name, task->container, task, task->as);
659#endif
660
661 irq_spinlock_unlock(&task->lock, false);
662}
663
664/** Print task list
665 *
666 * @param additional Print additional information.
667 *
668 */
669void task_print_list(bool additional)
670{
671 /* Messing with task structures, avoid deadlock */
672 irq_spinlock_lock(&tasks_lock, true);
673
674#ifdef __32_BITS__
675 if (additional)
676 printf("[id ] [threads] [calls] [callee\n");
677 else
678 printf("[id ] [name ] [ctn] [address ] [as ]"
679 " [ucycles ] [kcycles ]\n");
680#endif
681
682#ifdef __64_BITS__
683 if (additional)
684 printf("[id ] [ucycles ] [kcycles ] [threads] [calls]"
685 " [callee\n");
686 else
687 printf("[id ] [name ] [ctn] [address ]"
688 " [as ]\n");
689#endif
690
691 task_t *task;
692
693 task = task_first();
694 while (task != NULL) {
695 task_print(task, additional);
696 task = task_next(task);
697 }
698
699 irq_spinlock_unlock(&tasks_lock, true);
700}
701
702/** Get key function for the @c tasks ordered dictionary.
703 *
704 * @param odlink Link
705 * @return Pointer to task ID cast as 'void *'
706 */
707static void *tasks_getkey(odlink_t *odlink)
708{
709 task_t *task = odict_get_instance(odlink, task_t, ltasks);
710 return (void *) &task->taskid;
711}
712
713/** Key comparison function for the @c tasks ordered dictionary.
714 *
715 * @param a Pointer to thread A ID
716 * @param b Pointer to thread B ID
717 * @return -1, 0, 1 iff ID A is less than, equal to, greater than B
718 */
719static int tasks_cmp(void *a, void *b)
720{
721 task_id_t ida = *(task_id_t *)a;
722 task_id_t idb = *(task_id_t *)b;
723
724 if (ida < idb)
725 return -1;
726 else if (ida == idb)
727 return 0;
728 else
729 return +1;
730}
731
732/** @}
733 */
Note: See TracBrowser for help on using the repository browser.