diff options
| author | Ada Christine <adachristine18@gmail.com> | 2022-02-18 21:19:27 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2022-02-18 21:19:27 +0000 |
| commit | d07a644f542a7344941d1e4fed28c09531ba12c2 (patch) | |
| tree | 2faf0ddb229759bb8da8955ac7a3fa062c62324b /kc/core | |
| parent | 21bcfa6b707403a43ea8ac4a285114951cf29059 (diff) | |
thread switching and sleeping appears to be working
Diffstat (limited to 'kc/core')
| -rw-r--r-- | kc/core/kc_main.c | 2 | ||||
| -rw-r--r-- | kc/core/task.c | 365 | ||||
| -rw-r--r-- | kc/core/task.h | 7 |
3 files changed, 289 insertions, 85 deletions
diff --git a/kc/core/kc_main.c b/kc/core/kc_main.c index de5f3fe..bdd7519 100644 --- a/kc/core/kc_main.c +++ b/kc/core/kc_main.c @@ -6,6 +6,7 @@ #include "task.h" #include "pic8259.h" #include "pit8253.h" +#include "cpu/irq.h" #include <kernel/entry.h> @@ -25,7 +26,6 @@ unsigned long kc_main(struct kc_boot_data *data) cpu_init(); serial_init(); kprintf("sophia starting, boot data %#lx\n", boot_data); - kprintf("integer test %d %u\n", 3, 3); memory_init(); pic8259_init(); pit8253_timer_source.init(); diff --git a/kc/core/task.c b/kc/core/task.c index f24850c..f9a8fc8 100644 --- a/kc/core/task.c +++ b/kc/core/task.c @@ -5,83 +5,148 @@ #include "cpu.h" #include "kprint.h" #include "cpu/irq.h" - +#include "cpu/mmu.h" #include "pit8253.h" #include <stdatomic.h> #include <stdbool.h> -struct kc_thread *current_task = NULL; -struct kc_thread *first_ready_task = NULL; -struct kc_thread *last_ready_task = NULL; -struct kc_thread *sleeping_tasks = NULL; -struct kc_thread *blocked_tasks = NULL; +extern uint64_t *get_tss_rsp0(void); static void lock_scheduler(void); static void unlock_scheduler(void); -static void set_thread_status(enum kc_thread_status status); -static void idle_task_thread(void); +static void lock_preempt(void); +static void unlock_preempt(void); -extern uint64_t *get_tss_rsp0(void); -static uint64_t last_count = 0; +static void update_time(void); + +static struct kc_thread *create_thread(void (*thread_entry)(void)); +static void destroy_thread(struct kc_thread *thread); +static void set_thread(struct kc_thread *thread); +static void block_thread(enum kc_thread_status reason); +static void unblock_thread(struct kc_thread *thread); +static void sleep_thread(uint64_t nanoseconds); +static void sleep_thread_until(uint64_t nanoseconds); + +static int sleeping_thread_callback(uint64_t nanoseconds); + +static struct kc_thread *ready_thread_pop(void); +static void ready_thread_push(struct kc_thread *thread); +static void ready_thread_push_back(struct kc_thread *thread); const struct timer_source * timesource; -static atomic_int preempt_switch_count = 0; +/* static threads that are always present + * TODO: make this a per-CPU thing at some point + */ +static struct kc_thread *idle_thread; + +// thread lists for the scheduler to manipulate +static struct kc_thread *current_thread; +static struct kc_thread *first_ready_thread; +static struct kc_thread *last_ready_thread; +static struct kc_thread *sleeping_threads; + +static volatile atomic_uint_fast64_t preempt_switch_count = 0; static volatile atomic_bool preempt_switch_flag = false; -void update_time_used(void) +static void idle_thread_entry(void) { - uint64_t current_count = timesource->nanoseconds_elapsed(); - uint64_t elapsed = current_count - last_count; - last_count = current_count; - current_task->time_elapsed += elapsed; -} + kprintf("idle thread started\n"); + unlock_scheduler(); -void task_set_thread(struct kc_thread *task) -{ - // wrapper around cpu_set_thread - if (preempt_switch_count) + while (true) { - preempt_switch_flag = true; - return; + __asm__ volatile ("hlt;"); } - uint64_t *rsp0 = get_tss_rsp0(); - cpu_set_thread(¤t_task->state, &task->state, rsp0); +} + +noreturn void task_init(void) +{ + lock_scheduler(); + timesource = &pit8253_timer_source; + timesource->append_callback(sleeping_thread_callback); + timesource->start(); + kprintf( + "starting task management, timesource delta %luns\n", + timesource->nanoseconds_delta()); + + // XXX: unfuck this mess at some point + // XXX: make this also not demand-allocated or it's gonna fail + char *kernel_rsp0 = vm_alloc(4096); + kernel_rsp0[1] = 0; + *get_tss_rsp0() = (uintptr_t)kernel_rsp0 + 4096; + + // initalize static threads + idle_thread = create_thread(idle_thread_entry); + + current_thread = idle_thread; + + idle_thread->status = RUNNING; + cpu_set_thread(&idle_thread->state, NULL, get_tss_rsp0()); + + // shouldn't ever get here + PANIC(DEAD_END); } void task_schedule(void) { - if (preempt_switch_count) + if (atomic_load(&preempt_switch_count)) { preempt_switch_flag = true; return; } - update_time_used(); - if (first_ready_task) + if (first_ready_thread) { - struct kc_thread *task = first_ready_task; - first_ready_task = task->next; - task_set_thread(task); + struct kc_thread *this_thread = ready_thread_pop(); + + if (this_thread == idle_thread) + { + if (first_ready_thread) + { + this_thread->status = READY; + this_thread = ready_thread_pop(); + ready_thread_push(idle_thread); + } + else if (current_thread->status == RUNNING) + { + return; + } + else + { + // NULL statement????? idon't like + } + } + set_thread(this_thread); } } - static void lock_scheduler(void) { irq_lock(); +} + +static void lock_preempt(void) +{ + irq_lock(); preempt_switch_count++; } static void unlock_scheduler(void) { - if (preempt_switch_count >= 1) + irq_unlock(); +} + +static void unlock_preempt(void) +{ + if (atomic_load(&preempt_switch_count) >= 1) { preempt_switch_count--; } - if (!preempt_switch_count && preempt_switch_flag) + if (!atomic_load(&preempt_switch_count) && + atomic_load(&preempt_switch_flag)) { preempt_switch_flag = false; task_schedule(); @@ -90,75 +155,213 @@ static void unlock_scheduler(void) irq_unlock(); } +void update_time(void) +{ + static uint64_t last_elapsed = 0; + if (current_thread) + { + uint64_t current_elapsed = timesource->nanoseconds_elapsed(); + uint64_t delta = current_elapsed - last_elapsed; + last_elapsed = current_elapsed; + current_thread->time_elapsed += delta; + } +} + +static struct kc_thread *create_thread(void (*thread_f)(void)) +{ + char *task_bottom = vm_alloc(16384); + struct kc_thread *thread = + (struct kc_thread *)(task_bottom + 16384 - sizeof(*thread)); + + thread->next = NULL; + + thread->time_elapsed = 0; + thread->sleep_expiration = 0; + + thread->state.stack = (uintptr_t)thread; + thread->state.stack_top = *get_tss_rsp0(); + thread->state.page_map = mmu_get_map(); + + // set up the expected stack values for the state + struct task_register_state + { + uint64_t rbp; + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t rbx; + uint64_t rip; + } + *register_state = + (struct task_register_state *) + (thread->state.stack -= sizeof(*register_state)); + + memset(register_state, 0, sizeof(*register_state)); + + register_state->rip = (uint64_t)thread_f; + register_state->rbp = thread->state.stack; + + thread->status = READY; + + return thread; +} + +static void destroy_thread(struct kc_thread *thread) +{ + (void)thread; +} + +static void set_thread(struct kc_thread *thread) +{ + if (atomic_load(&preempt_switch_count)) + { + preempt_switch_flag = true; + return; + } + update_time(); + struct kc_thread *previous_thread = current_thread; + current_thread = thread; + + if (previous_thread->status == RUNNING) + { + previous_thread->status = READY; + ready_thread_push(previous_thread); + } + + cpu_set_thread( + ¤t_thread->state, + &previous_thread->state, + get_tss_rsp0()); + + current_thread->status = RUNNING; +} + +static void block_thread(enum kc_thread_status reason) +{ + lock_scheduler(); + current_thread->status = reason; + task_schedule(); + unlock_scheduler(); +} + static void unblock_thread(struct kc_thread *thread) { lock_scheduler(); - if (!first_ready_task) + + thread->status = READY; + + if (!first_ready_thread || (current_thread == idle_thread)) { - task_set_thread(thread); + unlock_preempt(); + set_thread(thread); } else { - first_ready_task->next = thread; - first_ready_task = thread; + ready_thread_push_back(thread); } + unlock_scheduler(); } -static void set_thread_status(enum kc_thread_status status) +static void sleep_thread(uint64_t nanoseconds) { - lock_scheduler(); - current_task->status = status; - task_schedule(); - unlock_scheduler(); + sleep_thread_until(timesource->nanoseconds_elapsed() + nanoseconds); } -static void idle_task_thread(void) +static void sleep_thread_until(uint64_t nanoseconds) { - kputs("in idle thread\n"); - while (1) + lock_preempt(); + + if (nanoseconds < timesource->nanoseconds_elapsed()) { - __asm__ volatile ("hlt;"); - lock_scheduler(); - task_schedule(); unlock_scheduler(); + return; } + + kprintf("time elapsed is now %ld\r\n", timesource->nanoseconds_elapsed()); + kprintf("thread will now sleep until %ld\r\n", nanoseconds); + current_thread->sleep_expiration = nanoseconds; + current_thread->next = sleeping_threads; + sleeping_threads = current_thread; + + unlock_preempt(); + + block_thread(SLEEPING); } -noreturn void task_init(void) +static struct kc_thread *ready_thread_pop(void) { - lock_scheduler(); - timesource = &pit8253_timer_source; - timesource->start(); - kprintf( - "starting task management, timesource delta %luns\n", - timesource->nanoseconds_delta()); - char *kernel_rsp0 = vm_alloc(4096); - // TODO: make it so this isn't demand-allocated. - // TODO: decouple the task management code from cpu-dependent task code - kernel_rsp0[1] = 0; - *get_tss_rsp0() = (uintptr_t)kernel_rsp0; - char *first_task = vm_alloc(16384); - struct kc_thread *idle_thread = - (struct kc_thread *)(first_task + 16384 - sizeof(*idle_thread)); - idle_thread->state.stack = (uintptr_t)idle_thread; - idle_thread->state.stack_top = (uintptr_t)kernel_rsp0; + struct kc_thread *thread = first_ready_thread; - // XXX: this is kindof a train wreck - __asm__ volatile - ( - "mov %%cr3, %%rax\n\t" - "mov %%rax, 0(%%rsi)\n\t" - "mov %%rcx, %%rsp\n\t" - "call unlock_scheduler\n\t" - "jmp idle_task_thread\n\t" - : "=m"(idle_thread->state.page_map) - : "S"(&idle_thread->state.page_map), - "c"(idle_thread) - : "rax" - ); + if (thread) + { + first_ready_thread = thread->next; + } - // shouldn't ever get here - PANIC(DEAD_END); + if (!first_ready_thread) + { + last_ready_thread = NULL; + } + + return thread; +} + +static void ready_thread_push(struct kc_thread *thread) +{ + thread->next = first_ready_thread; + first_ready_thread = thread; + + if (!last_ready_thread) + { + last_ready_thread = first_ready_thread; + } +} + +static void ready_thread_push_back(struct kc_thread *thread) +{ + if (last_ready_thread) + { + last_ready_thread->next = thread; + } + else + { + first_ready_thread = thread; + } + last_ready_thread = thread; +} + +static int sleeping_thread_callback(uint64_t nanoseconds) +{ + lock_preempt(); + + struct kc_thread *sleeping = sleeping_threads; + sleeping_threads = NULL; + + while (sleeping != NULL) + { + struct kc_thread *this_thread = sleeping; + sleeping = sleeping->next; + + if (this_thread->sleep_expiration <= nanoseconds) + { + kprintf("thread awakened: %p\n", this_thread); + this_thread->sleep_expiration = 0; + unblock_thread(this_thread); + } + else + { + this_thread->next = sleeping_threads; + sleeping_threads = this_thread; + } + } + + unlock_preempt(); + + lock_scheduler(); + task_schedule(); + unlock_scheduler(); + + return 0; } diff --git a/kc/core/task.h b/kc/core/task.h index 346630a..bea4756 100644 --- a/kc/core/task.h +++ b/kc/core/task.h @@ -5,8 +5,8 @@ enum kc_thread_status { READY, - STARTED, - YIELDED, + RUNNING, + SLEEPING, BLOCKED, TERMINATED }; @@ -20,9 +20,9 @@ struct kc_thread_state struct kc_thread { - struct kc_thread *prev; struct kc_thread *next; uint64_t time_elapsed; + uint64_t sleep_expiration; enum kc_thread_status status; struct kc_thread_state state; }; @@ -33,4 +33,5 @@ extern void cpu_set_thread( uint64_t *cpu_tss_rsp0); void task_init(void); +void task_schedule(void); |
