From fafe58280270765638cc4d2e3361b4de7cd6ff48 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Tue, 11 Sep 2018 22:37:00 -0700 Subject: [PATCH] Initial priority-based scheduler - Scheduler now has multiple linked_lists of processes at different priorities - Process structure improvements - scheduler::tick() and scheduler::schedule() separation --- assets/initrd.toml | 7 +++- src/drivers/nulldrv/main.s | 3 +- src/kernel/interrupts.cpp | 6 +++ src/kernel/loader.s | 6 +++ src/kernel/scheduler.cpp | 76 +++++++++++++++++++++++++++++++------- src/kernel/scheduler.h | 60 ++++++++++++++++++++++++++---- src/kernel/syscall.h | 1 + 7 files changed, 136 insertions(+), 23 deletions(-) diff --git a/assets/initrd.toml b/assets/initrd.toml index 19bd28b..04a8847 100644 --- a/assets/initrd.toml +++ b/assets/initrd.toml @@ -13,6 +13,11 @@ dest = "screenfont.psf" source = "assets/fonts/tamsyn8x16r.psf" [[files]] -dest = "nulldrv" +dest = "nulldrv1" +source = "build/kernel/src/drivers/nulldrv/nulldrv" +executable = true + +[[files]] +dest = "nulldrv2" source = "build/kernel/src/drivers/nulldrv/nulldrv" executable = true diff --git a/src/drivers/nulldrv/main.s b/src/drivers/nulldrv/main.s index c280013..0e27af5 100644 --- a/src/drivers/nulldrv/main.s +++ b/src/drivers/nulldrv/main.s @@ -3,7 +3,8 @@ _start: xor rbp, rbp ; Sentinel rbp .loop: - mov rax, 0 ; DEBUG syscall + ;mov rax, 1 ; DEBUG syscall + mov rax, 0 ; NOOP syscall syscall jmp .loop diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index c560cfb..49722c8 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -306,6 +306,9 @@ syscall_handler(addr_t return_rsp, cpu_state regs) syscall call = static_cast(regs.rax); switch (call) { + case syscall::noop: + break; + case syscall::debug: cons->set_color(11); cons->printf("\nReceived DEBUG syscall\n"); @@ -314,6 +317,9 @@ syscall_handler(addr_t return_rsp, cpu_state regs) break; case syscall::message: + cons->set_color(11); + cons->printf("\nReceived MESSAGE syscall\n"); + cons->set_color(); break; default: diff --git a/src/kernel/loader.s b/src/kernel/loader.s index 3849f4c..1c6a440 100644 --- a/src/kernel/loader.s +++ b/src/kernel/loader.s @@ -8,8 +8,14 @@ ramdisk_process_loader: ; create_process already pushed a cpu_state onto the stack for us, this ; acts both as the cpu_state parameter to load_process, and the saved ; state for the following iretq + ; + ; Additional parameters: + ; rax - the address of the program image + ; rbx - the size of the program image + ; rcx - the address of this process' process structure mov rdi, rax mov rsi, rbx + mov rdx, rcx call load_process pop_all_and_segments diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 4e42e29..4c4fc2c 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -20,20 +20,33 @@ const uint64_t rflags_int = 0x202; extern "C" { void ramdisk_process_loader(); - void load_process(const void *image_start, size_t butes, cpu_state state); + void load_process(const void *image_start, size_t bytes, process *proc, cpu_state state); }; scheduler::scheduler(lapic *apic) : m_apic(apic), - m_current(0), - m_next_pid(0) + m_next_pid(1) { - m_processes.ensure_capacity(50); - m_processes.append({m_next_pid++, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now + auto *idle = m_process_allocator.pop(); + + uint8_t last_pri = num_priorities - 1; + + // The kernel idle task, also the thread we're in now + idle->pid = 0; + idle->ppid = 0; + idle->priority = last_pri; + idle->rsp = 0; // This will get set when we switch away + idle->pml4 = page_manager::get_pml4(); + idle->flags = + process_flags::ready | + process_flags::const_pri; + + m_runlists[last_pri].push_back(idle); + m_current = idle; } void -load_process(const void *image_start, size_t bytes, cpu_state state) +load_process(const void *image_start, size_t bytes, process *proc, cpu_state state) { // We're now in the process space for this process, allocate memory for the // process code and load it @@ -85,6 +98,7 @@ load_process(const void *image_start, size_t bytes, cpu_state state) } state.rip = image.entrypoint(); + proc->flags &= ~process_flags::loading; log::debug(logs::task, "Loaded! New process state:"); log::debug(logs::task, " CS: %d [%d]", state.cs >> 3, state.cs & 0x07); @@ -137,14 +151,28 @@ scheduler::create_process(const char *name, const void *data, size_t size) loader_state->rbx = size; uint16_t pid = m_next_pid++; + auto *proc = m_process_allocator.pop(); + + proc->pid = pid; + proc->ppid = 0; // TODO + proc->priority = default_priority; + proc->rsp = reinterpret_cast(loader_state); + proc->pml4 = pml4; + proc->flags = + process_flags::ready | + process_flags::loading; + + m_runlists[default_priority].push_back(proc); + + loader_state->rcx = reinterpret_cast(proc); + log::debug(logs::task, "Creating process %s:", name); log::debug(logs::task, " PID %d", pid); + log::debug(logs::task, " Pri %d", pid); log::debug(logs::task, " RSP0 %016lx", state); log::debug(logs::task, " RSP3 %016lx", state->user_rsp); log::debug(logs::task, " PML4 %016lx", pml4); log::debug(logs::task, " Loading %016lx [%d]", loader_state->rax, loader_state->rbx); - - m_processes.append({pid, reinterpret_cast(loader_state), pml4}); } void @@ -155,21 +183,41 @@ scheduler::start() } addr_t -scheduler::tick(addr_t rsp0) +scheduler::schedule(addr_t rsp0) { - log::debug(logs::task, "Scheduler tick."); + m_current->rsp = rsp0; + m_runlists[m_current->priority].remove(m_current); + m_runlists[m_current->priority].push_back(m_current); - m_processes[m_current].rsp = rsp0; - m_current = (m_current + 1) % m_processes.count(); - rsp0 = m_processes[m_current].rsp; + uint8_t pri = 0; + while (m_runlists[pri].empty()) { + ++pri; + kassert(pri < num_priorities, "All runlists are empty"); + } + + m_current = m_runlists[pri].pop_front(); + rsp0 = m_current->rsp; // Set rsp0 to after the end of the about-to-be-popped cpu state tss_set_stack(0, rsp0 + sizeof(cpu_state)); // Swap page tables - page_table *pml4 = m_processes[m_current].pml4; + page_table *pml4 = m_current->pml4; page_manager::set_pml4(pml4); + bool loading = bitfield_has(m_current->flags, process_flags::loading); + log::debug(logs::task, "Scheduler switched to process %d, priority %d%s.", + m_current->pid, m_current->priority, loading ? " (loading)" : ""); + + return rsp0; +} + +addr_t +scheduler::tick(addr_t rsp0) +{ + // TODO: action based on the task using the whole quantum + + rsp0 = schedule(rsp0); m_apic->reset_timer(quantum); return rsp0; } diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 1a05e7c..6c89288 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -1,25 +1,57 @@ #pragma once /// \file scheduler.h /// The task scheduler and related definitions +#include "kutil/enum_bitfields.h" +#include "kutil/linked_list.h" #include "kutil/memory.h" +#include "kutil/slab_allocator.h" #include "kutil/vector.h" class lapic; struct page_table; +struct cpu_state; +extern "C" addr_t isr_handler(addr_t, cpu_state); + +enum class process_flags : uint32_t +{ + running = 0x00000001, + ready = 0x00000002, + loading = 0x00000004, + + const_pri = 0x80000000, + + none = 0x00000000 +}; +IS_BITFIELD(process_flags); struct process { - uint16_t pid; + uint32_t pid; + uint32_t ppid; + + uint8_t priority; + + uint8_t reserved0; + uint16_t reserved; + + process_flags flags; + addr_t rsp; page_table *pml4; }; +using process_list = kutil::linked_list; +using process_node = process_list::item_type; +using process_slab = kutil::slab_allocator; /// The task scheduler class scheduler { public: + static const uint8_t num_priorities = 8; + static const uint8_t default_priority = num_priorities / 2; + /// Constructor. /// \arg apic Pointer to the local APIC object scheduler(lapic *apic); @@ -34,20 +66,34 @@ public: /// timer interrupts or other preemption methods. void start(); - /// Handle a timer tick + /// Run the scheduler, possibly switching to a new task /// \arg rsp0 The stack pointer of the current interrupt handler - /// \returns The stack pointer to handler to switch to - addr_t tick(addr_t rsp0); + /// \returns The stack pointer to switch to + addr_t schedule(addr_t rsp0); + + /// Get the current process. + /// \returns A pointer to the current process' process struct + inline process * current() { return m_current; } /// Get a reference to the system scheduler /// \returns A reference to the global system scheduler static scheduler & get() { return s_instance; } private: + friend addr_t isr_handler(addr_t, cpu_state); + + /// Handle a timer tick + /// \arg rsp0 The stack pointer of the current interrupt handler + /// \returns The stack pointer to switch to + addr_t tick(addr_t rsp0); + lapic *m_apic; - kutil::vector m_processes; - uint16_t m_current; - uint16_t m_next_pid; + + uint32_t m_next_pid; + + process_node *m_current; + process_slab m_process_allocator; + process_list m_runlists[num_priorities]; static scheduler s_instance; }; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index 6513785..e36273c 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -2,6 +2,7 @@ enum class syscall : uint64_t { + noop, debug, message,