From df457e8097f9e964dc7a04c1af8baf6fbccd68b3 Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Fri, 1 Mar 2024 22:56:50 +0200 Subject: [PATCH] Refactor task scheduler --- include/scheduler.hpp | 150 ++++++++ include/task.hpp | 115 ++---- tasking/process.cpp | 6 +- .../{scheduler.cpp => scheduler/custom.cpp} | 346 ++++++++++++------ tasking/task.cpp | 332 +++-------------- 5 files changed, 472 insertions(+), 477 deletions(-) create mode 100644 include/scheduler.hpp rename tasking/{scheduler.cpp => scheduler/custom.cpp} (73%) diff --git a/include/scheduler.hpp b/include/scheduler.hpp new file mode 100644 index 0000000..b23aa0b --- /dev/null +++ b/include/scheduler.hpp @@ -0,0 +1,150 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#pragma once + +#include +#include + +namespace Tasking::Scheduler +{ + class Base + { + public: + Task *ctx = nullptr; + std::atomic_size_t SchedulerTicks = 0; + std::atomic_size_t LastTaskTicks = 0; + std::atomic_int LastCore = 0; + std::atomic_bool StopScheduler = false; + std::atomic_bool SchedulerUpdateTrapFrame = false; + + /** + * Remove a thread from the scheduler + * + * @note This function is NOT thread safe + * @note This function does not check if + * the thread is valid nor if it has + * Terminated status + */ + virtual bool RemoveThread(TCB *tcb) + { + assert(!"RemoveThread not implemented"); + }; + + /** + * @note This function is NOT thread safe + */ + virtual bool RemoveProcess(PCB *pcb) + { + assert(!"RemoveProcess not implemented"); + } + + virtual PCB *GetProcessByID(TID ID) + { + assert(!"GetProcessByID not implemented"); + } + + virtual TCB *GetThreadByID(TID ID) + { + assert(!"GetThreadByID not implemented"); + } + + virtual std::list &GetProcessList() + { + assert(!"GetProcessList not implemented"); + } + + virtual void StartIdleProcess() + { + assert(!"StartIdleProcess not implemented"); + } + + virtual void StartScheduler() + { + assert(!"StartScheduler not implemented"); + } + + virtual void Yield() + { + assert(!"Yield not implemented"); + } + + virtual void PushProcess(PCB *pcb) + { + assert(!"PushProcess not implemented"); + } + + virtual void PopProcess(PCB *pcb) + { + assert(!"PopProcess not implemented"); + } + + Base(Task *_ctx) + : ctx(_ctx) {} + + ~Base() {} + }; + + class Custom : public Base, + public Interrupts::Handler + { + private: + NewLock(SchedulerLock); + + public: + std::list ProcessList; + + PCB *IdleProcess = nullptr; + TCB *IdleThread = nullptr; + + bool RemoveThread(TCB *tcb) final; + bool RemoveProcess(PCB *pcb) final; + PCB *GetProcessByID(TID ID) final; + TCB *GetThreadByID(TID ID) final; + std::list &GetProcessList() final; + void StartIdleProcess() final; + void StartScheduler() final; + void Yield() final; + void PushProcess(PCB *pcb) final; + void PopProcess(PCB *pcb) final; + + void OneShot(int TimeSlice); + + void UpdateUsage(TaskInfo *Info, + TaskExecutionMode Mode, + int Core); + + bool FindNewProcess(void *CPUDataPointer); + bool GetNextAvailableThread(void *CPUDataPointer); + bool GetNextAvailableProcess(void *CPUDataPointer); + bool SchedulerSearchProcessThread(void *CPUDataPointer); + void UpdateProcessState(); + void WakeUpThreads(); + void CleanupTerminated(); + + void Schedule(CPU::TrapFrame *Frame); + void OnInterruptReceived(CPU::TrapFrame *Frame) final; + + Custom(Task *ctx); + virtual ~Custom(); + }; + + class RoundRobin : public Base, + public Interrupts::Handler + { + }; +} diff --git a/include/task.hpp b/include/task.hpp index 22dd91c..d6835d6 100644 --- a/include/task.hpp +++ b/include/task.hpp @@ -467,7 +467,7 @@ namespace Tasking ~PCB(); }; - class Task : public Interrupts::Handler + class Task { private: NewLock(SchedulerLock); @@ -476,114 +476,43 @@ namespace Tasking PID NextPID = 0; TID NextTID = 0; - std::list ProcessList; PCB *KernelProcess = nullptr; - PCB *IdleProcess = nullptr; - TCB *IdleThread = nullptr; - std::atomic_size_t SchedulerTicks = 0; - std::atomic_size_t LastTaskTicks = 0; - std::atomic_int LastCore = 0; - std::atomic_bool StopScheduler = false; - std::atomic_bool SchedulerUpdateTrapFrame = false; - bool InvalidPCB(PCB *pcb); - bool InvalidTCB(TCB *tcb); + void *Scheduler = nullptr; + void *__sched_ctx = nullptr; - /** - * Remove a thread from the scheduler - * - * @note This function is NOT thread safe - * @note This function does not check if - * the thread is valid nor if it has - * Terminated status - */ - bool RemoveThread(TCB *tcb); + constexpr TaskArchitecture GetKArch() + { +#if defined(a64) + return x64; +#elif defined(a32) + return x32; +#elif defined(aa64) + return ARM64; +#endif + } - /** - * @note This function is NOT thread safe - */ - bool RemoveProcess(PCB *pcb); - - void UpdateUsage(TaskInfo *Info, - TaskExecutionMode Mode, - int Core); - - /** - * @note This function is NOT thread safe - */ - bool FindNewProcess(void *CPUDataPointer); - - /** - * @note This function is NOT thread safe - */ - bool GetNextAvailableThread(void *CPUDataPointer); - - /** - * @note This function is NOT thread safe - */ - bool GetNextAvailableProcess(void *CPUDataPointer); - - /** - * @note This function is NOT thread safe - */ - bool SchedulerSearchProcessThread(void *CPUDataPointer); - - /** - * @note This function is NOT thread safe - */ - void UpdateProcessState(); - - /** - * @note This function is NOT thread safe - */ - void WakeUpThreads(); - - /** - * @note This function is NOT thread safe - */ - void CleanupTerminated(); - - /** - * @note This function is NOT thread safe - */ - void Schedule(CPU::TrapFrame *Frame); - - void OnInterruptReceived(CPU::TrapFrame *Frame) final; + void PushProcess(PCB *pcb); + void PopProcess(PCB *pcb); public: + void *GetScheduler() { return Scheduler; } PCB *GetKernelProcess() { return KernelProcess; } - size_t GetSchedulerTicks() { return SchedulerTicks.load(); } - size_t GetLastTaskTicks() { return LastTaskTicks.load(); } - int GetLastCore() { return LastCore.load(); } - std::list GetProcessList() { return ProcessList; } - void Panic() { StopScheduler = true; } - bool IsPanic() { return StopScheduler; } + std::list GetProcessList(); + void Panic(); + bool IsPanic(); /** * Yield the current thread and switch * to another thread if available */ - __always_inline inline void Yield() - { - /* This will trigger the IRQ16 - instantly so we won't execute - the next instruction */ -#if defined(a86) - asmv("int $0x30"); -#elif defined(aa64) - asmv("svc #0x30"); -#endif - } + void Yield(); /** * Update the current thread's trap frame * without switching to another thread */ - __always_inline inline void UpdateFrame() - { - SchedulerUpdateTrapFrame = true; - Yield(); - } + void UpdateFrame(); void SignalShutdown(); @@ -670,6 +599,4 @@ namespace Tasking #define thisProcess GetCurrentCPU()->CurrentProcess.load() #define thisThread GetCurrentCPU()->CurrentThread.load() -extern "C" void TaskingScheduler_OneShot(int TimeSlice); - #endif // !__FENNIX_KERNEL_TASKING_H__ diff --git a/tasking/process.cpp b/tasking/process.cpp index 7d331b4..734252e 100644 --- a/tasking/process.cpp +++ b/tasking/process.cpp @@ -228,7 +228,7 @@ namespace Tasking if (Parent) Parent->Children.push_back(this); - ctx->ProcessList.push_back(this); + ctx->PushProcess(this); } PCB::~PCB() @@ -240,9 +240,7 @@ namespace Tasking debug("Removing from process list"); /* Remove us from the process list so we don't get scheduled anymore */ - ctx->ProcessList.erase(std::find(ctx->ProcessList.begin(), - ctx->ProcessList.end(), - this)); + ctx->PopProcess(this); debug("Freeing signals"); delete this->Signals; diff --git a/tasking/scheduler.cpp b/tasking/scheduler/custom.cpp similarity index 73% rename from tasking/scheduler.cpp rename to tasking/scheduler/custom.cpp index 933c5df..f224fd2 100644 --- a/tasking/scheduler.cpp +++ b/tasking/scheduler/custom.cpp @@ -15,7 +15,7 @@ along with Fennix Kernel. If not, see . */ -#include +#include #include #include @@ -103,26 +103,187 @@ #define wut_schedbg(m, ...) #endif -extern "C" nsa NIF void TaskingScheduler_OneShot(int TimeSlice) +__naked __used nsa void __custom_sched_idle_loop() { - if (TimeSlice == 0) - TimeSlice = Tasking::TaskPriority::Normal; - -#ifdef DEBUG - if (DebuggerIsAttached) - TimeSlice += 10; -#endif - #if defined(a86) - ((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice); + asmv("IdleLoop:"); + asmv("hlt"); + asmv("jmp IdleLoop"); #elif defined(aa64) + asmv("IdleLoop:"); + asmv("wfe"); + asmv("b IdleLoop"); #endif } -namespace Tasking +namespace Tasking::Scheduler { + bool Custom::RemoveThread(TCB *Thread) + { + debug("Thread \"%s\"(%d) removed from process \"%s\"(%d)", + Thread->Name, Thread->ID, Thread->Parent->Name, + Thread->Parent->ID); + + delete Thread; + return true; + } + + bool Custom::RemoveProcess(PCB *Process) + { + if (Process->State == Terminated) + { + delete Process; + return true; + } + + foreach (TCB *Thread in Process->Threads) + { + if (Thread->State == Terminated) + RemoveThread(Thread); + } + + return true; + } + + PCB *Custom::GetProcessByID(TID ID) + { + foreach (auto p in ProcessList) + { + if (p->ID == ID) + return p; + } + return nullptr; + } + + TCB *Custom::GetThreadByID(TID ID) + { + foreach (auto p in ProcessList) + { + foreach (auto t in p->Threads) + { + if (t->ID == ID) + return t; + } + } + return nullptr; + } + + void Custom::StartIdleProcess() + { + IdleProcess = ctx->CreateProcess(nullptr, (char *)"Idle", + TaskExecutionMode::Kernel, true); + for (int i = 0; i < SMP::CPUCores; i++) + { + TCB *thd = ctx->CreateThread(IdleProcess, IP(__custom_sched_idle_loop)); + char IdleName[16]; + sprintf(IdleName, "Idle Thread %d", i); + thd->Rename(IdleName); + thd->SetPriority(Idle); + for (int j = 0; j < MAX_CPU; j++) + thd->Info.Affinity[j] = false; + thd->Info.Affinity[i] = true; + + if (unlikely(i == 0)) + IdleThread = thd; + } + } + + std::list &Custom::GetProcessList() + { + return ProcessList; + } + + void Custom::StartScheduler() + { #if defined(a86) - nsa NIF bool Task::FindNewProcess(void *CPUDataPointer) + if (Interrupts::apicTimer[0]) + { + ((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x86::IRQ16, 100); + + /* FIXME: The kernel is not ready for multi-core tasking. */ + return; + + APIC::InterruptCommandRegister icr{}; + bool x2APIC = ((APIC::APIC *)Interrupts::apic[0])->x2APIC; + + if (likely(x2APIC)) + { + icr.x2.VEC = s_cst(uint8_t, CPU::x86::IRQ16); + icr.x2.MT = APIC::Fixed; + icr.x2.L = APIC::Assert; + icr.x2.DES = 0xFFFFFFFF; /* Broadcast IPI to all local APICs. */ + ((APIC::APIC *)Interrupts::apic[0])->ICR(icr); + } + else + { + icr.VEC = s_cst(uint8_t, CPU::x86::IRQ16); + icr.MT = APIC::Fixed; + icr.L = APIC::Assert; + + for (int i = 0; i < SMP::CPUCores; i++) + { + icr.DES = uint8_t(i); + ((APIC::APIC *)Interrupts::apic[i])->ICR(icr); + } + } + } +#endif + } + + void Custom::Yield() + { + /* This will trigger the IRQ16 + instantly so we won't execute + the next instruction */ +#if defined(a86) + asmv("int $0x30"); +#elif defined(aa64) + asmv("svc #0x30"); +#endif + } + + void Custom::PushProcess(PCB *pcb) + { + this->ProcessList.push_back(pcb); + } + + void Custom::PopProcess(PCB *pcb) + { + this->ProcessList.remove(pcb); + } + + /* --------------------------------------------------------------- */ + + nsa void Custom::OneShot(int TimeSlice) + { + if (TimeSlice == 0) + TimeSlice = Tasking::TaskPriority::Normal; + +#ifdef DEBUG + if (DebuggerIsAttached) + TimeSlice += 10; +#endif + +#if defined(a86) + ((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice); +#elif defined(aa64) +#endif + } + + nsa void Custom::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core) + { + UNUSED(Core); + uint64_t CurrentTime = TimeManager->GetCounter(); + uint64_t TimePassed = Info->LastUpdateTime - CurrentTime; + Info->LastUpdateTime = CurrentTime; + + if (Mode == TaskExecutionMode::User) + Info->UserTime += TimePassed; + else + Info->KernelTime += TimePassed; + } + + nsa NIF bool Custom::FindNewProcess(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; fnp_schedbg("%d processes", ProcessList.size()); @@ -133,9 +294,6 @@ namespace Tasking #endif foreach (auto process in ProcessList) { - if (unlikely(InvalidPCB(process))) - continue; - switch (process->State.load()) { case TaskState::Ready: @@ -157,9 +315,6 @@ namespace Tasking foreach (auto thread in process->Threads) { - if (unlikely(InvalidTCB(thread))) - continue; - if (thread->State.load() != TaskState::Ready) continue; @@ -175,7 +330,7 @@ namespace Tasking return false; } - nsa NIF bool Task::GetNextAvailableThread(void *CPUDataPointer) + nsa NIF bool Custom::GetNextAvailableThread(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; @@ -192,16 +347,6 @@ namespace Tasking TCB *nextThread = CurrentCPU->CurrentProcess->Threads[TempIndex + 1]; - if (unlikely(InvalidTCB(nextThread))) - { - if (TempIndex > ThreadsSize) - break; - TempIndex++; - - gnat_schedbg("Thread %#lx is invalid", nextThread); - goto RetryAnotherThread; - } - gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)", CurrentCPU->CurrentProcess->Threads[i]->Name, CurrentCPU->CurrentProcess->Threads[i]->ID, @@ -234,7 +379,7 @@ namespace Tasking return false; } - nsa NIF bool Task::GetNextAvailableProcess(void *CPUDataPointer) + nsa NIF bool Custom::GetNextAvailableProcess(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; @@ -254,12 +399,6 @@ namespace Tasking continue; } - if (unlikely(InvalidPCB(process))) - { - gnap_schedbg("Invalid process %#lx", process); - continue; - } - if (process->State.load() != TaskState::Ready) { gnap_schedbg("Process %d is not ready", process->ID); @@ -268,12 +407,6 @@ namespace Tasking foreach (auto thread in process->Threads) { - if (unlikely(InvalidTCB(thread))) - { - gnap_schedbg("Invalid thread %#lx", thread); - continue; - } - if (thread->State.load() != TaskState::Ready) { gnap_schedbg("Thread %d is not ready", thread->ID); @@ -294,18 +427,12 @@ namespace Tasking return false; } - nsa NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) + nsa NIF bool Custom::SchedulerSearchProcessThread(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; foreach (auto process in ProcessList) { - if (unlikely(InvalidPCB(process))) - { - sspt_schedbg("Invalid process %#lx", process); - continue; - } - if (process->State.load() != TaskState::Ready) { sspt_schedbg("Process %d is not ready", process->ID); @@ -314,12 +441,6 @@ namespace Tasking foreach (auto thread in process->Threads) { - if (unlikely(InvalidTCB(thread))) - { - sspt_schedbg("Invalid thread %#lx", thread); - continue; - } - if (thread->State.load() != TaskState::Ready) { sspt_schedbg("Thread %d is not ready", thread->ID); @@ -339,13 +460,10 @@ namespace Tasking return false; } - nsa NIF void Task::UpdateProcessState() + nsa NIF void Custom::UpdateProcessState() { foreach (auto process in ProcessList) { - if (unlikely(InvalidPCB(process))) - continue; - if (process->State.load() == TaskState::Terminated) continue; @@ -375,13 +493,10 @@ namespace Tasking } } - nsa NIF void Task::WakeUpThreads() + nsa NIF void Custom::WakeUpThreads() { foreach (auto process in ProcessList) { - if (unlikely(InvalidPCB(process))) - continue; - Tasking::TaskState pState = process->State.load(); if (pState != TaskState::Ready && pState != TaskState::Sleeping && @@ -390,9 +505,6 @@ namespace Tasking foreach (auto thread in process->Threads) { - if (unlikely(InvalidTCB(thread))) - continue; - if (likely(thread->State.load() != TaskState::Sleeping)) continue; @@ -415,7 +527,7 @@ namespace Tasking } } - nsa NIF void Task::CleanupTerminated() + nsa NIF void Custom::CleanupTerminated() { foreach (auto pcb in ProcessList) { @@ -433,7 +545,7 @@ namespace Tasking } } - nsa NIF void Task::Schedule(CPU::TrapFrame *Frame) + nsa NIF void Custom::Schedule(CPU::TrapFrame *Frame) { if (unlikely(StopScheduler)) { @@ -450,8 +562,8 @@ namespace Tasking this->LastCore.store(CurrentCPU->ID); schedbg("Scheduler called on CPU %d.", CurrentCPU->ID); - if (unlikely(InvalidPCB(CurrentCPU->CurrentProcess.load()) || - InvalidTCB(CurrentCPU->CurrentThread.load()))) + if (unlikely(!CurrentCPU->CurrentProcess.load() || + !CurrentCPU->CurrentThread.load())) { schedbg("Invalid process or thread. Finding a new one."); ProcessNotChanged = true; @@ -524,9 +636,7 @@ namespace Tasking } } - warn("Unwanted reach!"); - TaskingScheduler_OneShot(100); - goto End; + assert(!"Unwanted code execution"); Idle: ProcessNotChanged = true; @@ -586,7 +696,7 @@ namespace Tasking if (!ProcessNotChanged) (&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter(); (&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter(); - TaskingScheduler_OneShot(CurrentCPU->CurrentThread->Info.Priority); + this->OneShot(CurrentCPU->CurrentThread->Info.Priority); if (CurrentCPU->CurrentThread->Security.IsDebugEnabled && CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled) @@ -627,42 +737,74 @@ namespace Tasking Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); schedbg("================================================================"); - End: this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks)); CurrentCPU->CurrentProcess->PageTable->Update(); } - nsa NIF void Task::OnInterruptReceived(CPU::TrapFrame *Frame) + nsa NIF void Custom::OnInterruptReceived(CPU::TrapFrame *Frame) { SmartCriticalSection(SchedulerLock); this->Schedule(Frame); } -#elif defined(aa64) - nsa bool Task::FindNewProcess(void *CPUDataPointer) - { - fixme("unimplemented"); - } - nsa bool Task::GetNextAvailableThread(void *CPUDataPointer) + Custom::Custom(Task *ctx) : Base(ctx), Interrupts::Handler(16) /* IRQ16 */ { - fixme("unimplemented"); - } - - nsa bool Task::GetNextAvailableProcess(void *CPUDataPointer) - { - fixme("unimplemented"); - } - - nsa bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) - { - fixme("unimplemented"); - } - - nsa void Task::Schedule(CPU::TrapFrame *Frame) - { - fixme("unimplemented"); - } - - nsa void Task::OnInterruptReceived(CPU::TrapFrame *Frame) { this->Schedule(Frame); } +#if defined(a86) + // Map the IRQ16 to the first CPU. + ((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1); #endif + } + + Custom::~Custom() + { + foreach (PCB *Process in ProcessList) + { + foreach (TCB *Thread in Process->Threads) + { + if (Thread == GetCurrentCPU()->CurrentThread.load()) + continue; + ctx->KillThread(Thread, KILL_SCHEDULER_DESTRUCTION); + } + + if (Process == GetCurrentCPU()->CurrentProcess.load()) + continue; + ctx->KillProcess(Process, KILL_SCHEDULER_DESTRUCTION); + } + + debug("Waiting for processes to terminate"); + uint64_t timeout = TimeManager->CalculateTarget(20, Time::Units::Seconds); + while (this->GetProcessList().size() > 0) + { + trace("Waiting for %d processes to terminate", this->GetProcessList().size()); + int NotTerminated = 0; + foreach (PCB *Process in this->GetProcessList()) + { + trace("Process %s(%d) is still running (or waiting to be removed state %#lx)", + Process->Name, Process->ID, Process->State); + + if (Process->State == TaskState::Terminated) + { + debug("Process %s(%d) terminated", Process->Name, Process->ID); + continue; + } + + NotTerminated++; + } + if (NotTerminated == 1) + break; + + ctx->Sleep(1000); + debug("Current working process is %s(%d)", + ctx->GetCurrentProcess()->Name, + ctx->GetCurrentProcess()->ID); + + if (TimeManager->GetCounter() > timeout) + { + error("Timeout waiting for processes to terminate"); + break; + } + + this->OneShot(100); + } + } } diff --git a/tasking/task.cpp b/tasking/task.cpp index 94acafd..808d242 100644 --- a/tasking/task.cpp +++ b/tasking/task.cpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -46,104 +47,6 @@ namespace Tasking { -#if defined(a86) - __naked __used __no_stack_protector void IdleProcessLoop() - { - asmv("IdleLoop:"); - asmv("hlt"); - asmv("jmp IdleLoop"); -#elif defined(aa64) - __used __no_stack_protector void IdleProcessLoop() - { - asmv("IdleLoop:"); - asmv("wfe"); - asmv("b IdleLoop"); -#endif - } - - nsa bool Task::InvalidPCB(PCB *pcb) - { - if (!pcb) - return true; - - /* Uninitialized pointers may have uintptr_t max value instead of nullptr. */ - if (pcb >= (PCB *)(UINTPTR_MAX - 0x1FFE)) - return true; - - /* In this section of the memory is reserved by the kernel. */ - if (pcb < (PCB *)(0xFFFFF)) - return true; - - /* Check if it's mapped. */ - if (!Memory::Virtual().Check((void *)pcb)) - return true; - - return false; - } - - nsa bool Task::InvalidTCB(TCB *tcb) - { - if (!tcb) - return true; - - /* Uninitialized pointers may have uintptr_t max value instead of nullptr. */ - if (tcb >= (TCB *)(UINTPTR_MAX - 0x1FFE)) - return true; - - /* In this section of the memory is reserved by the kernel. */ - if (tcb < (TCB *)(0xFFFFF)) - return true; - - /* Check if it's mapped. */ - if (!Memory::Virtual().Check((void *)tcb)) - return true; - - return false; - } - - nsa bool Task::RemoveThread(TCB *Thread) - { - debug("Thread \"%s\"(%d) removed from process \"%s\"(%d)", - Thread->Name, Thread->ID, Thread->Parent->Name, - Thread->Parent->ID); - - delete Thread; - return true; - } - - nsa bool Task::RemoveProcess(PCB *Process) - { - if (unlikely(InvalidPCB(Process))) - return false; - - if (Process->State == Terminated) - { - delete Process; - return true; - } - - foreach (TCB *Thread in Process->Threads) - { - if (Thread->State == Terminated) - RemoveThread(Thread); - } - - return true; - } - - nsa void Task::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core) - { - UNUSED(Core); - uint64_t CurrentTime = TimeManager->GetCounter(); - uint64_t TimePassed = CurrentTime - Info->LastUpdateTime; - // Info->LastUpdateTime = CurrentTime; - - if (Mode == TaskExecutionMode::User) - Info->UserTime += TimePassed; - else - Info->KernelTime += TimePassed; - } - PCB *Task::GetCurrentProcess() { return GetCurrentCPU()->CurrentProcess.load(); @@ -156,34 +59,52 @@ namespace Tasking PCB *Task::GetProcessByID(TID ID) { - SmartLock(TaskingLock); - foreach (auto p in ProcessList) - { - if (p->ID == ID) - return p; - } - return nullptr; + return ((Scheduler::Base *)Scheduler)->GetProcessByID(ID); } TCB *Task::GetThreadByID(TID ID) { - SmartLock(TaskingLock); - foreach (auto p in ProcessList) - { - foreach (auto t in p->Threads) - { - if (t->ID == ID) - return t; - } - } - return nullptr; + return ((Scheduler::Base *)Scheduler)->GetThreadByID(ID); + } + + std::list Task::GetProcessList() + { + return ((Scheduler::Base *)Scheduler)->GetProcessList(); + } + + void Task::Panic() + { + ((Scheduler::Base *)Scheduler)->StopScheduler.store(true); + } + + bool Task::IsPanic() + { + return ((Scheduler::Base *)Scheduler)->StopScheduler; + } + + void Task::Yield() + { + ((Scheduler::Base *)Scheduler)->Yield(); + } + + void Task::UpdateFrame() + { + ((Scheduler::Base *)Scheduler)->SchedulerUpdateTrapFrame = true; + ((Scheduler::Base *)Scheduler)->Yield(); + } + + void Task::PushProcess(PCB *pcb) + { + ((Scheduler::Base *)Scheduler)->PushProcess(pcb); + } + + void Task::PopProcess(PCB *pcb) + { + ((Scheduler::Base *)Scheduler)->PopProcess(pcb); } void Task::WaitForProcess(PCB *pcb) { - if (unlikely(InvalidPCB(pcb))) - return; - if (pcb->State == TaskState::UnknownStatus) return; @@ -198,9 +119,6 @@ namespace Tasking void Task::WaitForThread(TCB *tcb) { - if (unlikely(InvalidTCB(tcb))) - return; - if (tcb->State == TaskState::UnknownStatus) return; @@ -215,9 +133,6 @@ namespace Tasking void Task::WaitForProcessStatus(PCB *pcb, TaskState status) { - if (unlikely(InvalidPCB(pcb))) - return; - if (pcb->State == TaskState::UnknownStatus) return; @@ -230,9 +145,6 @@ namespace Tasking void Task::WaitForThreadStatus(TCB *tcb, TaskState status) { - if (unlikely(InvalidTCB(tcb))) - return; - if (tcb->State == TaskState::UnknownStatus) return; @@ -277,7 +189,7 @@ namespace Tasking GetCurrentProcess()->Name, GetCurrentProcess()->ID, GetCurrentThread()->Name, GetCurrentThread()->ID); - foreach (auto pcb in ProcessList) + foreach (auto pcb in((Scheduler::Base *)Scheduler)->GetProcessList()) { if (pcb->State == TaskState::Terminated || pcb->State == TaskState::Zombie) @@ -295,13 +207,10 @@ namespace Tasking } __no_sanitize("undefined") - TCB *Task::CreateThread(PCB *Parent, - IP EntryPoint, - const char **argv, - const char **envp, + TCB *Task::CreateThread(PCB *Parent, IP EntryPoint, + const char **argv, const char **envp, const std::vector &auxv, - TaskArchitecture arch, - TaskCompatibility Compatibility, + TaskArchitecture arch, TaskCompatibility Compatibility, bool ThreadNotReady) { SmartLock(TaskingLock); @@ -310,10 +219,8 @@ namespace Tasking Compatibility, ThreadNotReady); } - PCB *Task::CreateProcess(PCB *Parent, - const char *Name, - TaskExecutionMode ExecutionMode, - bool UseKernelPageTable, + PCB *Task::CreateProcess(PCB *Parent, const char *Name, + TaskExecutionMode ExecutionMode, bool UseKernelPageTable, uint16_t UserID, uint16_t GroupID) { SmartLock(TaskingLock); @@ -323,169 +230,40 @@ namespace Tasking void Task::StartScheduler() { -#if defined(a86) - if (Interrupts::apicTimer[0]) - { - ((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x86::IRQ16, 100); - - /* FIXME: The kernel is not ready for multi-core tasking. */ - return; - - APIC::InterruptCommandRegister icr{}; - bool x2APIC = ((APIC::APIC *)Interrupts::apic[0])->x2APIC; - - if (likely(x2APIC)) - { - icr.x2.VEC = s_cst(uint8_t, CPU::x86::IRQ16); - icr.x2.MT = APIC::Fixed; - icr.x2.L = APIC::Assert; - icr.x2.DES = 0xFFFFFFFF; /* Broadcast IPI to all local APICs. */ - ((APIC::APIC *)Interrupts::apic[0])->ICR(icr); - } - else - { - icr.VEC = s_cst(uint8_t, CPU::x86::IRQ16); - icr.MT = APIC::Fixed; - icr.L = APIC::Assert; - - for (int i = 0; i < SMP::CPUCores; i++) - { - icr.DES = uint8_t(i); - ((APIC::APIC *)Interrupts::apic[i])->ICR(icr); - } - } - } -#elif defined(aa64) -#endif + ((Scheduler::Base *)Scheduler)->StartScheduler(); debug("Tasking Started"); } - Task::Task(const IP EntryPoint) : Interrupts::Handler(16) /* IRQ16 */ + Task::Task(const IP EntryPoint) { -#if defined(a64) - // Map the IRQ16 to the first CPU. - ((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1); -#elif defined(a32) -#elif defined(aa64) -#endif - KPrint("Starting tasking instance %#lx with ip: %p (\e666666%s\eCCCCCC)", - this, EntryPoint, KernelSymbolTable->GetSymbol(EntryPoint)); - -#if defined(a64) - TaskArchitecture Arch = TaskArchitecture::x64; -#elif defined(a32) - TaskArchitecture Arch = TaskArchitecture::x32; -#elif defined(aa64) - TaskArchitecture Arch = TaskArchitecture::ARM64; -#endif + /* I don't know if this is the best way to do this. */ + Scheduler::Custom *custom_sched = new Scheduler::Custom(this); + Scheduler::Base *sched = r_cst(Scheduler::Base *, custom_sched); + __sched_ctx = custom_sched; + Scheduler = sched; KernelProcess = CreateProcess(nullptr, "Kernel", TaskExecutionMode::Kernel, true); KernelProcess->PageTable = KernelPageTable; TCB *kthrd = CreateThread(KernelProcess, EntryPoint, nullptr, nullptr, - std::vector(), Arch); + std::vector(), GetKArch()); kthrd->Rename("Main Thread"); debug("Created Kernel Process: %s and Thread: %s", KernelProcess->Name, kthrd->Name); - bool MONITORSupported = false; - if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0) - { - CPU::x86::AMD::CPUID0x00000001 cpuid; - MONITORSupported = cpuid.ECX.MONITOR; - } - else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0) - { - CPU::x86::Intel::CPUID0x00000001 cpuid; - MONITORSupported = cpuid.ECX.MONITOR; - } - - if (MONITORSupported) - { - trace("CPU has MONITOR/MWAIT support."); - } - if (!CPU::Interrupts(CPU::Check)) { error("Interrupts are not enabled."); CPU::Interrupts(CPU::Enable); } - IdleProcess = CreateProcess(nullptr, (char *)"Idle", - TaskExecutionMode::Kernel, true); - for (int i = 0; i < SMP::CPUCores; i++) - { - TCB *thd = CreateThread(IdleProcess, IP(IdleProcessLoop)); - char IdleName[16]; - sprintf(IdleName, "Idle Thread %d", i); - thd->Rename(IdleName); - thd->SetPriority(Idle); - for (int j = 0; j < MAX_CPU; j++) - thd->Info.Affinity[j] = false; - thd->Info.Affinity[i] = true; - - if (unlikely(i == 0)) - IdleThread = thd; - } + ((Scheduler::Base *)Scheduler)->StartIdleProcess(); debug("Tasking is ready"); } Task::~Task() { - debug("Destructor called"); - { - SmartLock(TaskingLock); - foreach (PCB *Process in ProcessList) - { - foreach (TCB *Thread in Process->Threads) - { - if (Thread == GetCurrentCPU()->CurrentThread.load()) - continue; - this->KillThread(Thread, KILL_SCHEDULER_DESTRUCTION); - } - - if (Process == GetCurrentCPU()->CurrentProcess.load()) - continue; - this->KillProcess(Process, KILL_SCHEDULER_DESTRUCTION); - } - } - - debug("Waiting for processes to terminate"); - uint64_t timeout = TimeManager->CalculateTarget(20, Time::Units::Seconds); - while (ProcessList.size() > 0) - { - trace("Waiting for %d processes to terminate", ProcessList.size()); - int NotTerminated = 0; - foreach (PCB *Process in ProcessList) - { - trace("Process %s(%d) is still running (or waiting to be removed state %#lx)", - Process->Name, Process->ID, Process->State); - - if (Process->State == TaskState::Terminated) - { - debug("Process %s(%d) terminated", Process->Name, Process->ID); - continue; - } - - NotTerminated++; - } - if (NotTerminated == 1) - break; - - this->Sleep(1000); - debug("Current working process is %s(%d)", - GetCurrentProcess()->Name, GetCurrentProcess()->ID); - - if (TimeManager->GetCounter() > timeout) - { - error("Timeout waiting for processes to terminate"); - break; - } - - TaskingScheduler_OneShot(100); - } - - debug("Tasking stopped"); + delete (Scheduler::Custom *)__sched_ctx; } }