From 51bff7b18de9329d7bd77324cc597e72173fae74 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 12 Dec 2012 21:56:44 +0100 Subject: kern/thread: support multiprocessor scheduling In practice, this merely means an idle thread now exists for each CPU, and threads can be preempted and rescheduled on each of them. There is currently no migration between processors. --- kern/thread.c | 107 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 31 deletions(-) (limited to 'kern/thread.c') diff --git a/kern/thread.c b/kern/thread.c index 463c4416..dcf038c3 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -50,12 +52,14 @@ thread_runq_init(struct thread_runq *runq, struct thread *idle) idle->flags = 0; idle->preempt = 1; runq->current = idle; + runq->idle = idle; list_init(&runq->threads); } static void thread_runq_enqueue(struct thread_runq *runq, struct thread *thread) { + assert(!cpu_intr_enabled()); list_insert_tail(&runq->threads, &thread->runq_node); } @@ -64,6 +68,8 @@ thread_runq_dequeue(struct thread_runq *runq) { struct thread *thread; + assert(!cpu_intr_enabled()); + if (list_empty(&runq->threads)) thread = NULL; else { @@ -106,15 +112,36 @@ thread_main(void) thread->fn(thread->arg); + /* TODO Thread destruction */ for (;;) cpu_idle(); } +static void +thread_init(struct thread *thread, struct task *task, void *stack, + const char *name, void (*fn)(void *), void *arg) +{ + tcb_init(&thread->tcb, stack, thread_main); + + if (name == NULL) + name = task->name; + + thread->flags = 0; + thread->preempt = 0; + thread->task = task; + thread->stack = stack; + strlcpy(thread->name, name, sizeof(thread->name)); + thread->fn = fn; + thread->arg = arg; + task_add_thread(task, thread); +} + int -thread_create(struct thread **threadp, const char *name, struct task *task, +thread_create(struct thread **threadp, struct task *task, const char *name, void (*fn)(void *), void *arg) { struct thread *thread; + unsigned long flags; void *stack; int error; @@ -132,22 +159,11 @@ thread_create(struct thread **threadp, const char *name, struct task *task, goto error_stack; } - tcb_init(&thread->tcb, stack, thread_main); - - if (name == NULL) - name = task->name; - - thread->flags = 0; - thread->preempt = 0; - thread->task = task; - thread->stack = stack; - strlcpy(thread->name, name, sizeof(thread->name)); - thread->fn = fn; - thread->arg = arg; + thread_init(thread, task, stack, name, fn, arg); - /* XXX Assign all threads to the main processor for now */ - thread_runq_enqueue(&thread_runqs[0], thread); - task_add_thread(task, thread); + flags = cpu_intr_save(); + thread_runq_enqueue(&thread_runqs[cpu_id()], thread); + cpu_intr_restore(flags); *threadp = thread; return 0; @@ -158,18 +174,48 @@ error_thread: return error; } +static void +thread_idle(void *arg) +{ + (void)arg; + + for (;;) + cpu_idle(); +} + +static void __init +thread_setup_idle(void) +{ + char name[THREAD_NAME_SIZE]; + struct thread_runq *runq; + void *stack; + + stack = kmem_cache_alloc(&thread_stack_cache); + + if (stack == NULL) + panic("thread: unable to allocate idle thread stack"); + + snprintf(name, sizeof(name), "idle%u", cpu_id()); + runq = thread_runq_local(); + thread_init(runq->idle, kernel_task, stack, name, thread_idle, NULL); +} + void __init thread_run(void) { struct thread_runq *runq; struct thread *thread; - runq = thread_runq_local(); + assert(cpu_intr_enabled()); + thread_setup_idle(); + + cpu_intr_disable(); + runq = thread_runq_local(); thread = thread_runq_dequeue(runq); - /* TODO Idle thread */ - assert(thread != NULL); + if (thread == NULL) + thread = runq->idle; runq->current = thread; tcb_load(&thread->tcb); @@ -182,20 +228,24 @@ thread_schedule(void) struct thread *prev, *next; unsigned long flags; + assert(thread_preempt_enabled()); + flags = cpu_intr_save(); runq = thread_runq_local(); prev = runq->current; - thread_runq_enqueue(runq, prev); + assert(prev != NULL); + + if (prev != runq->idle) + thread_runq_enqueue(runq, prev); + next = thread_runq_dequeue(runq); - /* TODO Idle thread */ - assert(next != NULL); + if (next == NULL) + next = runq->idle; - if (prev != next) { - runq->current = next; + if (prev != next) tcb_switch(&prev->tcb, &next->tcb); - } cpu_intr_restore(flags); } @@ -210,11 +260,9 @@ thread_reschedule(void) runq = thread_runq_local(); thread = runq->current; - - /* TODO Idle thread */ assert(thread != NULL); - if (thread->flags & THREAD_RESCHEDULE) + if ((thread->preempt == 0) && (thread->flags & THREAD_RESCHEDULE)) thread_schedule(); } @@ -228,9 +276,6 @@ thread_tick(void) runq = thread_runq_local(); thread = runq->current; - - /* TODO Idle thread */ assert(thread != NULL); - thread->flags |= THREAD_RESCHEDULE; } -- cgit v1.2.3