summaryrefslogtreecommitdiff
path: root/kc/core
diff options
context:
space:
mode:
authorAda Christine <adachristine18@gmail.com>2022-02-18 21:19:27 +0000
committerAda Christine <adachristine18@gmail.com>2022-02-18 21:19:27 +0000
commitd07a644f542a7344941d1e4fed28c09531ba12c2 (patch)
tree2faf0ddb229759bb8da8955ac7a3fa062c62324b /kc/core
parent21bcfa6b707403a43ea8ac4a285114951cf29059 (diff)
thread switching and sleeping appears to be working
Diffstat (limited to 'kc/core')
-rw-r--r--kc/core/kc_main.c2
-rw-r--r--kc/core/task.c365
-rw-r--r--kc/core/task.h7
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(&current_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(
+ &current_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);