summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2012-12-12 21:56:44 +0100
committerRichard Braun <rbraun@sceen.net>2012-12-12 21:56:44 +0100
commit51bff7b18de9329d7bd77324cc597e72173fae74 (patch)
tree779b279c8ccb9f1d91b4ee8055718ae62771c8a1
parent36cce230ead886a82521dd6988e2fe120eeef3fc (diff)
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.
-rw-r--r--arch/x86/machine/lapic.c4
-rw-r--r--arch/x86/machine/trap.c3
-rw-r--r--kern/kernel.c22
-rw-r--r--kern/thread.c107
-rw-r--r--kern/thread.h5
5 files changed, 83 insertions, 58 deletions
diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c
index e65d5a3b..10d6b40e 100644
--- a/arch/x86/machine/lapic.c
+++ b/arch/x86/machine/lapic.c
@@ -340,10 +340,8 @@ lapic_intr_timer(struct trap_frame *frame)
{
(void)frame;
-#if 0
- thread_tick();
-#endif
lapic_eoi();
+ thread_tick();
}
void
diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c
index a26212a2..18bff6bc 100644
--- a/arch/x86/machine/trap.c
+++ b/arch/x86/machine/trap.c
@@ -150,10 +150,7 @@ trap_main(struct trap_frame *frame)
{
assert(frame->vector < ARRAY_SIZE(trap_handlers));
trap_handlers[frame->vector].fn(frame);
-
-#if 0
thread_reschedule();
-#endif
}
#ifdef __LP64__
diff --git a/kern/kernel.c b/kern/kernel.c
index 70cc8c71..760f68e8 100644
--- a/kern/kernel.c
+++ b/kern/kernel.c
@@ -22,31 +22,16 @@
#include <kern/thread.h>
#include <machine/cpu.h>
-static void __init
-kernel_setup(void *arg)
-{
- (void)arg;
-
- for (;;)
- cpu_idle();
-}
-
void __init
kernel_main(void)
{
- struct thread *thread;
- int error;
-
assert(!cpu_intr_enabled());
task_setup();
thread_setup();
- cpu_mp_setup();
-
- error = thread_create(&thread, "core", kernel_task, kernel_setup, NULL);
- if (error)
- panic("kernel: unable to create kernel thread");
+ /* Interrupts are enabled by this call */
+ cpu_mp_setup();
thread_run();
@@ -58,8 +43,7 @@ kernel_ap_main(void)
{
assert(cpu_intr_enabled());
- for (;;)
- cpu_idle();
+ thread_run();
/* Never reached */
}
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 <kern/list.h>
#include <kern/macros.h>
#include <kern/param.h>
+#include <kern/sprintf.h>
#include <kern/stddef.h>
#include <kern/string.h>
+#include <kern/task.h>
#include <kern/thread.h>
#include <machine/cpu.h>
#include <machine/tcb.h>
@@ -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;
}
diff --git a/kern/thread.h b/kern/thread.h
index b7debddc..0022591d 100644
--- a/kern/thread.h
+++ b/kern/thread.h
@@ -57,6 +57,7 @@ struct thread {
*/
struct thread_runq {
struct thread *current;
+ struct thread *idle;
struct list threads;
} __aligned(CPU_L1_SIZE);
@@ -80,13 +81,13 @@ void thread_setup(void);
*
* If the given name is null, the task name is used instead.
*/
-int thread_create(struct thread **threadp, const char *name, struct task *task,
+int thread_create(struct thread **threadp, struct task *task, const char *name,
void (*fn)(void *), void *arg);
/*
* Start running threads on the local processor.
*
- * Interrupts are implicitely enabled when the first thread is dispatched.
+ * Interrupts must be enabled when calling this function.
*/
void __noreturn thread_run(void);