diff options
-rw-r--r-- | kern/task.c | 89 | ||||
-rw-r--r-- | kern/task.h | 42 | ||||
-rw-r--r-- | kern/thread.c | 59 |
3 files changed, 189 insertions, 1 deletions
diff --git a/kern/task.c b/kern/task.c index 7f1c53d2..66b43bff 100644 --- a/kern/task.c +++ b/kern/task.c @@ -24,6 +24,7 @@ #include <kern/kmem.h> #include <kern/list.h> #include <kern/param.h> +#include <kern/shell.h> #include <kern/spinlock.h> #include <kern/task.h> #include <kern/thread.h> @@ -56,20 +57,61 @@ static struct spinlock task_list_lock; static void task_init(struct task *task, const char *name, struct vm_map *map) { + task->nr_refs = 1; spinlock_init(&task->lock); list_init(&task->threads); task->map = map; strlcpy(task->name, name, sizeof(task->name)); } +static void +task_shell_info(int argc, char *argv[]) +{ + struct task *task; + int error; + + if (argc == 1) { + task_info(NULL); + return; + } else { + task = task_lookup(argv[1]); + + if (task == NULL) { + error = ERROR_INVAL; + goto error; + } + + task_info(task); + task_unref(task); + } + + return; + +error: + printf("task: info: %s\n", error_str(error)); +} + +static struct shell_cmd task_shell_cmds[] = { + SHELL_CMD_INITIALIZER("task_info", task_shell_info, + "task_info", "print all tasks and threads"), +}; + void __init task_setup(void) { + unsigned int i; + int error; + kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0); list_init(&task_list); spinlock_init(&task_list_lock); task_init(kernel_task, "x15", kernel_map); list_insert_head(&task_list, &kernel_task->node); + + for (i = 0; i < ARRAY_SIZE(task_shell_cmds); i++) { + error = shell_cmd_register(&task_shell_cmds[i]); + error_check(error, __func__); + } } int @@ -107,6 +149,31 @@ error_task: return error; } +struct task * +task_lookup(const char *name) +{ + struct task *task; + + spinlock_lock(&task_list_lock); + + list_for_each_entry(&task_list, task, node) { + spinlock_lock(&task->lock); + + if (strcmp(task->name, name) == 0) { + task_ref(task); + spinlock_unlock(&task->lock); + spinlock_unlock(&task_list_lock); + return task; + } + + spinlock_unlock(&task->lock); + } + + spinlock_unlock(&task_list_lock); + + return NULL; +} + void task_add_thread(struct task *task, struct thread *thread) { @@ -123,6 +190,26 @@ task_remove_thread(struct task *task, struct thread *thread) spinlock_unlock(&task->lock); } +struct thread * +task_lookup_thread(struct task *task, const char *name) +{ + struct thread *thread; + + spinlock_lock(&task->lock); + + list_for_each_entry(&task->threads, thread, task_node) { + if (strcmp(thread->name, name) == 0) { + thread_ref(thread); + spinlock_unlock(&task->lock); + return thread; + } + } + + spinlock_unlock(&task->lock); + + return NULL; +} + void task_info(struct task *task) { @@ -149,6 +236,8 @@ task_info(struct task *task) * can be used to debug in the middle of most critical sections. * Threads are only destroyed after being removed from their task * so holding the task lock is enough to guarantee existence. + * + * TODO Handle tasks and threads names modifications. */ list_for_each_entry(&task->threads, thread, task_node) { printf(TASK_INFO_ADDR_FMT " %c %8s:" TASK_INFO_ADDR_FMT diff --git a/kern/task.h b/kern/task.h index 67599399..cfe694bf 100644 --- a/kern/task.h +++ b/kern/task.h @@ -18,6 +18,7 @@ #ifndef _KERN_TASK_H #define _KERN_TASK_H +#include <kern/atomic.h> #include <kern/list.h> #include <kern/spinlock.h> #include <kern/thread.h> @@ -32,6 +33,7 @@ * Task structure. */ struct task { + unsigned long nr_refs; struct spinlock lock; struct list node; struct list threads; @@ -44,6 +46,28 @@ struct task { */ extern struct task *kernel_task; +static inline void +task_ref(struct task *task) +{ + unsigned long nr_refs; + + nr_refs = atomic_fetch_add(&task->nr_refs, 1, ATOMIC_RELAXED); + assert(nr_refs != (unsigned long)-1); +} + +static inline void +task_unref(struct task *task) +{ + unsigned long nr_refs; + + nr_refs = atomic_fetch_sub_acq_rel(&task->nr_refs, 1); + assert(nr_refs != 0); + + if (nr_refs == 1) { + /* TODO Task destruction */ + } +} + /* * Initialize the task module. */ @@ -55,6 +79,15 @@ void task_setup(void); int task_create(struct task **taskp, const char *name); /* + * Look up a task from its name. + * + * If a task is found, it gains a reference. Otherwise, NULL is returned. + * + * This function is meant for debugging only. + */ +struct task * task_lookup(const char *name); + +/* * Add a thread to a task. */ void task_add_thread(struct task *task, struct thread *thread); @@ -65,6 +98,15 @@ void task_add_thread(struct task *task, struct thread *thread); void task_remove_thread(struct task *task, struct thread *thread); /* + * Look up a thread in a task from its name. + * + * If a thread is found, it gains a reference, Otherwise, NULL is returned. + * + * This function is meant for debugging only. + */ +struct thread * task_lookup_thread(struct task *task, const char *name); + +/* * Display task information. * * If task is NULL, this function displays all tasks. diff --git a/kern/thread.c b/kern/thread.c index 68aa7285..17ac10bd 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -101,6 +101,7 @@ #include <kern/panic.h> #include <kern/param.h> #include <kern/percpu.h> +#include <kern/shell.h> #include <kern/sleepq.h> #include <kern/spinlock.h> #include <kern/sref.h> @@ -2228,10 +2229,61 @@ thread_setup_runq(struct thread_runq *runq) thread_setup_idler(runq); } +/* + * This function is meant for debugging only. As a result, it uses a weak + * locking policy which allows tracing threads which state may mutate during + * tracing. + */ +static void +thread_shell_trace(int argc, char *argv[]) +{ + const char *task_name, *thread_name; + struct thread *thread; + struct task *task; + int error; + + if (argc != 3) { + error = ERROR_INVAL; + goto error; + } + + task_name = argv[1]; + thread_name = argv[2]; + + task = task_lookup(task_name); + + if (task == NULL) { + error = ERROR_SRCH; + goto error; + } + + thread = task_lookup_thread(task, thread_name); + task_unref(task); + + if (thread == NULL) { + error = ERROR_SRCH; + goto error; + } + + tcb_trace(&thread->tcb); + thread_unref(thread); + return; + +error: + printf("thread: trace: %s\n", error_str(error)); +} + +static struct shell_cmd thread_shell_cmds[] = { + SHELL_CMD_INITIALIZER("thread_trace", thread_shell_trace, + "thread_trace <task_name> <thread_name>", + "print the stack trace of a given thread"), +}; + void __init thread_setup(void) { - int cpu; + unsigned int i; + int cpu, error; for (cpu = 1; (unsigned int)cpu < cpu_count(); cpu++) { thread_bootstrap_common(cpu); @@ -2249,6 +2301,11 @@ thread_setup(void) cpumap_for_each(&thread_active_runqs, cpu) { thread_setup_runq(percpu_ptr(thread_runq, cpu)); } + + for (i = 0; i < ARRAY_SIZE(thread_shell_cmds); i++) { + error = shell_cmd_register(&thread_shell_cmds[i]); + error_check(error, __func__); + } } int |