diff options
Diffstat (limited to 'kern')
-rw-r--r-- | kern/kernel.c | 7 | ||||
-rw-r--r-- | kern/thread.c | 157 | ||||
-rw-r--r-- | kern/thread.h | 37 |
3 files changed, 154 insertions, 47 deletions
diff --git a/kern/kernel.c b/kern/kernel.c index 7c83d8d1..9e7f0435 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -27,10 +27,6 @@ kernel_setup(void *arg) { (void)arg; - cpu_mp_setup(); - - cpu_intr_enable(); - for (;;) cpu_idle(); } @@ -43,13 +39,14 @@ kernel_main(void) 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"); - thread_load(thread); + thread_run(); /* Never reached */ } diff --git a/kern/thread.c b/kern/thread.c index 24498b01..e8ac54e2 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -28,20 +28,13 @@ #include <machine/tcb.h> /* - * Make sure thread stacks are properly aligned. - */ -#define THREAD_STACK_ALIGN 8 - -/* * Per processor run queue. */ struct thread_runq { + struct thread *current; struct list threads; } __aligned(CPU_L1_SIZE); -/* - * Per-processor run queues. - */ static struct thread_runq thread_runqs[MAX_CPUS]; /* @@ -53,31 +46,74 @@ static struct kmem_cache thread_stack_cache; static void __init thread_runq_init(struct thread_runq *runq) { + runq->current = NULL; list_init(&runq->threads); } +static void +thread_runq_enqueue(struct thread_runq *runq, struct thread *thread) +{ + list_insert_tail(&runq->threads, &thread->runq_node); +} + +static struct thread * +thread_runq_dequeue(struct thread_runq *runq) +{ + struct thread *thread; + + if (list_empty(&runq->threads)) + thread = NULL; + else { + thread = list_first_entry(&runq->threads, struct thread, runq_node); + list_remove(&thread->runq_node); + } + + return thread; +} + +static inline struct thread_runq * +thread_runq_local(void) +{ + return &thread_runqs[cpu_id()]; +} + void __init thread_setup(void) { size_t i; - tcb_setup(); - kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), - 0, NULL, NULL, NULL, 0); + CPU_L1_SIZE, NULL, NULL, NULL, 0); kmem_cache_init(&thread_stack_cache, "thread_stack", STACK_SIZE, - THREAD_STACK_ALIGN, NULL, NULL, NULL, 0); + CPU_L1_SIZE, NULL, NULL, NULL, 0); for (i = 0; i < ARRAY_SIZE(thread_runqs); i++) thread_runq_init(&thread_runqs[i]); } +static void +thread_main(void) +{ + struct thread_runq *runq; + struct thread *thread; + + assert(!cpu_intr_enabled()); + + runq = thread_runq_local(); + thread = runq->current; + cpu_intr_enable(); + + thread->fn(thread->arg); + + for (;;) + cpu_idle(); +} + int thread_create(struct thread **threadp, const char *name, struct task *task, - thread_run_fn_t run_fn, void *arg) + void (*fn)(void *), void *arg) { struct thread *thread; - struct tcb *tcb; void *stack; int error; @@ -95,28 +131,25 @@ thread_create(struct thread **threadp, const char *name, struct task *task, goto error_stack; } - error = tcb_create(&tcb, stack, thread); - - if (error) - goto error_tcb; + tcb_init(&thread->tcb, stack, thread_main); if (name == NULL) name = task->name; - /* XXX Assign all threads to the main processor for now */ - thread->tcb = tcb; - list_insert_tail(&thread_runqs[0].threads, &thread->runq_node); - task_add_thread(task, thread); + thread->flags = 0; thread->task = task; thread->stack = stack; strlcpy(thread->name, name, sizeof(thread->name)); - thread->run_fn = run_fn; + thread->fn = fn; thread->arg = arg; + + /* XXX Assign all threads to the main processor for now */ + thread_runq_enqueue(&thread_runqs[0], thread); + task_add_thread(task, thread); + *threadp = thread; return 0; -error_tcb: - kmem_cache_free(&thread_stack_cache, stack); error_stack: kmem_cache_free(&thread_cache, thread); error_thread: @@ -124,16 +157,78 @@ error_thread: } void __init -thread_load(struct thread *thread) +thread_run(void) { - tcb_load(thread->tcb); + struct thread_runq *runq; + struct thread *thread; + + runq = thread_runq_local(); + + thread = thread_runq_dequeue(runq); + + /* TODO Idle thread */ + assert(thread != NULL); + + runq->current = thread; + tcb_load(&thread->tcb); } void -thread_main(struct thread *thread) +thread_schedule(void) { - thread->run_fn(thread->arg); + struct thread_runq *runq; + struct thread *prev, *next; + unsigned long flags; - for (;;) - cpu_idle(); + flags = cpu_intr_save(); + + runq = thread_runq_local(); + prev = runq->current; + thread_runq_enqueue(runq, prev); + next = thread_runq_dequeue(runq); + + /* TODO Idle thread */ + assert(next != NULL); + + if (prev != next) { + runq->current = next; + tcb_switch(&prev->tcb, &next->tcb); + } + + cpu_intr_restore(flags); +} + +void +thread_reschedule(void) +{ + struct thread_runq *runq; + struct thread *thread; + + assert(!cpu_intr_enabled()); + + runq = thread_runq_local(); + thread = runq->current; + + /* TODO Idle thread */ + assert(thread != NULL); + + if (thread->flags & THREAD_RESCHEDULE) + thread_schedule(); +} + +void +thread_tick(void) +{ + struct thread_runq *runq; + struct thread *thread; + + assert(!cpu_intr_enabled()); + + 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 15851ba3..52c0f813 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -29,21 +29,22 @@ #define THREAD_NAME_SIZE 32 /* - * Type for thread entry point. + * Thread flags. */ -typedef void (*thread_run_fn_t)(void *); +#define THREAD_RESCHEDULE 0x1 /* Thread marked for reschedule */ /* * Thread structure. */ struct thread { - struct tcb *tcb; + struct tcb tcb; + int flags; struct list runq_node; struct list task_node; struct task *task; void *stack; char name[THREAD_NAME_SIZE]; - thread_run_fn_t run_fn; + void (*fn)(void *); void *arg; }; @@ -58,19 +59,33 @@ 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, - thread_run_fn_t run_fn, void *arg); + void (*fn)(void *), void *arg); /* - * Transform into a thread. + * Start running threads on the local processor. * - * This function is used during system initialization by code in "boot context" - * when creating the first thread on their processor. + * Interrupts are implicitely enabled when the first thread is dispatched. */ -void __noreturn thread_load(struct thread *thread); +void __noreturn thread_run(void); /* - * Thread entry point. + * Invoke the scheduler. */ -void __noreturn thread_main(struct thread *thread); +void thread_schedule(void); + +/* + * Invoke the scheduler if the current thread is marked for reschedule. + * + * Called from interrupt context. + */ +void thread_reschedule(void); + +/* + * Report a periodic timer interrupt on the thread currently running on + * the local processor. + * + * Called from interrupt context. + */ +void thread_tick(void); #endif /* _KERN_THREAD_H */ |