diff options
Diffstat (limited to 'kc/core')
| -rw-r--r-- | kc/core/Makefile | 2 | ||||
| -rw-r--r-- | kc/core/cpu/cpu_task.S | 54 | ||||
| -rw-r--r-- | kc/core/task.c | 210 |
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( |
