summaryrefslogtreecommitdiff
path: root/kc/core
diff options
context:
space:
mode:
Diffstat (limited to 'kc/core')
-rw-r--r--kc/core/Makefile2
-rw-r--r--kc/core/cpu/cpu_task.S54
-rw-r--r--kc/core/task.c210
3 files changed, 110 insertions, 156 deletions
diff --git a/kc/core/Makefile b/kc/core/Makefile
index 4b31853..56ddf55 100644
--- a/kc/core/Makefile
+++ b/kc/core/Makefile
@@ -10,7 +10,7 @@ CPPFLAGS += -I../../api -I../api -I../../lib -I.
CFLAGS += -mcmodel=small -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden -g
AOBJS := entry_x86_64.o reloc_x86_64.o dynamic_x86_64.o
-COBJS := mmu.o cpu_task.o cpu_thread.o irq.o exceptions.o cpu.o msr.o mmu.o port.o arch_thread.o
+COBJS := mmu.o cpu_thread.o irq.o exceptions.o cpu.o msr.o mmu.o port.o arch_thread.o
POBJS := pic8259.o pic8259_isr.o pit8253.o i8042.o
GOBJS := serial.o kc_main.o memory.o vm_tree.o panic.o task.o \
kcc_memory.o page_early.o page_stack.o video.o
diff --git a/kc/core/cpu/cpu_task.S b/kc/core/cpu/cpu_task.S
deleted file mode 100644
index a411028..0000000
--- a/kc/core/cpu/cpu_task.S
+++ /dev/null
@@ -1,54 +0,0 @@
-/* thread switching routines for x86_64
-* adapted from:
-* https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial
-*
-*
-*/
-
-.hidden cpu_set_thread
-.global cpu_set_thread
-.type cpu_set_thread, @function
-cpu_set_thread:
- /* x86_64 SysV ABI
- * must save the following registers to the stack:
- * rbx, rsp, rbp, r12, r13, r14, r15
- * the following are the parameters for switching tasks
- * rdi: pointer to control block of thread being switched to
- * rsi: pointer to control block of the current thread
- * rdx: pointer to this cpu's task state segment rsp0 field
- */
- // test for attempt to switch to same thread
- cmp %rdi,%rsi
- je .Lnothing
- // test for a NULL origin thread
- test %rsi,%rsi
- // skip saving the state for a NULL origin thread
- jz .Lnull_thread
- push %rbx
- push %r12
- push %r13
- push %r14
- push %r15
- push %rbp
- /* the current task will be in rsi */
- mov %rsp, 0(%rsi) // save the stack pointer for the current task
-.Lnull_thread:
- mov 0(%rdi),%rsp // new stack is the next's task's stack
- mov 8(%rdi),%rbx // top of the next task's kernel stack
- mov %rbx,0(%rdx) // save the esp0 to the tss
- mov 16(%rdi),%rax // page map for the next task
- mov %cr3, %rcx // in case we need to change the page map
- cmp %rax,%rcx
- je .Lfinish
- mov %rax,%cr3 // if the page maps are not the same, reload
-.Lfinish:
- pop %rbp // get all the registers off the new task's stack
- pop %r15
- pop %r14
- pop %r13
- pop %r12
- pop %rbx
-.Lnothing: // do nothing if the threads are the same
- ret // leave into the new task
-.size cpu_set_thread, . - cpu_set_thread
-
diff --git a/kc/core/task.c b/kc/core/task.c
index e5933bd..9e0e549 100644
--- a/kc/core/task.c
+++ b/kc/core/task.c
@@ -33,10 +33,6 @@ 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 threads that are always present
@@ -46,9 +42,67 @@ 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;
+
+struct thread_queue
+{
+ struct kc_thread
+ *first,
+ *last;
+};
+
+static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread);
+static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread);
+
+static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread)
+{
+ thread->next = queue->first;
+ queue->first = thread;
+
+ if (!queue->last)
+ {
+ thread_queue_push_back(queue, thread);
+ }
+}
+
+static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread)
+{
+ if (!queue->first)
+ {
+ thread_queue_push(queue, thread);
+ }
+ else if (queue->last)
+ {
+ queue->last->next = thread;
+ }
+
+ queue->last = thread;
+}
+
+struct kc_thread *thread_queue_pop(struct thread_queue *queue)
+{
+ if (!queue->first)
+ {
+ return nullptr;
+ }
+
+ struct kc_thread *thread = queue->first;
+ queue->first = thread->next;
+ thread->next = nullptr;
+ if (!queue->first)
+ {
+ queue->last = nullptr;
+ }
+
+ return thread;
+}
+
+bool thread_queue_any(struct thread_queue queue)
+{
+ return queue.first && queue.last;
+}
+
+static struct thread_queue ready_queue;
+static struct thread_queue sleep_queue;
static volatile atomic_uint_fast64_t preempt_switch_count = 0;
static volatile atomic_bool preempt_switch_flag = false;
@@ -102,10 +156,10 @@ noreturn void task_init(void)
idle_thread = create_thread(idle_thread_entry, nullptr);
struct kc_thread *sleepy_thread = create_thread(sleepy_thread_entry, (void *)3000000000);
struct kc_thread *sleepy_thread2 = create_thread(sleepy_thread_entry, (void *)5000000000);
- ready_thread_push(sleepy_thread);
- ready_thread_push(sleepy_thread2);
+ thread_queue_push(&ready_queue, sleepy_thread);
+ thread_queue_push(&ready_queue, sleepy_thread2);
__asm__ volatile ("movq %%rsp, %0" : "=g"(boot_thread.kernel_stack_pointer) :);
- ready_thread_push(idle_thread);
+ thread_queue_push(&ready_queue, idle_thread);
block_thread(BLOCKED);
// shouldn't ever get here
kprintf("the idle thread exited somehow. we're back in the boot thread. this is not good.\n");
@@ -124,33 +178,25 @@ static inline bool check_preempt_count()
void task_schedule(void)
{
- if (check_preempt_count())
- {
- preempt_switch_flag = true;
- return;
- }
+ if(check_preempt_count())
+ {
+ preempt_switch_flag = true;
+ return;
+ }
- if (first_ready_thread)
- {
- struct kc_thread *this_thread = ready_thread_pop();
+ struct kc_thread *t = thread_queue_pop(&ready_queue);
- 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;
- }
- }
- set_thread(this_thread);
- }
-}
+ if (t == idle_thread)
+ {
+ thread_queue_push_back(&ready_queue, t);
+ t = thread_queue_pop(&ready_queue);
+ }
+ if (t && current_thread->status != RUNNING)
+ {
+ set_thread(t);
+ }
+}
static void lock_scheduler(void)
{
@@ -223,11 +269,17 @@ static void destroy_thread(struct kc_thread *thread)
static void set_thread(struct kc_thread *thread)
{
+ if (thread == current_thread)
+ {
+ return;
+ }
+
if (atomic_load(&preempt_switch_count))
{
preempt_switch_flag = true;
return;
}
+
update_time();
struct kc_thread *previous_thread = current_thread;
current_thread = thread;
@@ -235,7 +287,7 @@ static void set_thread(struct kc_thread *thread)
if (previous_thread->status == RUNNING)
{
previous_thread->status = READY;
- ready_thread_push(previous_thread);
+ thread_queue_push(&ready_queue, previous_thread);
}
current_thread->status = RUNNING;
@@ -257,14 +309,14 @@ static void unblock_thread(struct kc_thread *thread)
thread->status = READY;
- if (!first_ready_thread || (current_thread == idle_thread))
+ if (!thread_queue_any(ready_queue) || (current_thread == idle_thread))
{
unlock_preempt();
set_thread(thread);
}
else
{
- ready_thread_push_back(thread);
+ thread_queue_push_back(&ready_queue, thread);
}
unlock_scheduler();
@@ -285,81 +337,38 @@ static void sleep_thread_until(uint64_t nanoseconds)
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;
+
+ thread_queue_push_back(&sleep_queue, current_thread);
unlock_preempt();
block_thread(SLEEPING);
}
-static struct kc_thread *ready_thread_pop(void)
-{
- struct kc_thread *thread = first_ready_thread;
-
- if (thread)
- {
- first_ready_thread = thread->next;
- }
-
- 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)
+static int sleeping_thread_callback(uint64_t timestamp)
{
lock_preempt();
- struct kc_thread *sleeping = sleeping_threads;
- sleeping_threads = NULL;
+ struct thread_queue queue = sleep_queue;
- while (sleeping != NULL)
- {
- struct kc_thread *this_thread = sleeping;
- sleeping = sleeping->next;
+ sleep_queue = (struct thread_queue){ nullptr, nullptr };
- if (this_thread->sleep_expiration <= nanoseconds)
- {
- this_thread->sleep_expiration = 0;
- unblock_thread(this_thread);
- }
- else
- {
- this_thread->next = sleeping_threads;
- sleeping_threads = this_thread;
- }
- }
+ {
+ struct kc_thread *t;
+ while((t = thread_queue_pop(&queue)) != nullptr)
+ {
+ if (t->sleep_expiration <= timestamp)
+ {
+ t->sleep_expiration = 0;
+ unblock_thread(t);
+ }
+ else
+ {
+ thread_queue_push(&sleep_queue, t);
+ }
+ }
+ }
unlock_preempt();
lock_scheduler();
@@ -369,7 +378,6 @@ static int sleeping_thread_callback(uint64_t nanoseconds)
return 0;
}
-
typedef void (locked_scheduler_action)(void *);
static void with_locked_scheduler(