Refactor task scheduler

This commit is contained in:
EnderIce2 2024-03-01 22:56:50 +02:00
parent 66ec562751
commit df457e8097
Signed by untrusted user who does not match committer: enderice2
GPG Key ID: EACC3AD603BAB4DD
5 changed files with 472 additions and 477 deletions

150
include/scheduler.hpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <task.hpp>
#include <lock.hpp>
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<PCB *> &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<PCB *> 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<PCB *> &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
{
};
}

View File

@ -467,7 +467,7 @@ namespace Tasking
~PCB(); ~PCB();
}; };
class Task : public Interrupts::Handler class Task
{ {
private: private:
NewLock(SchedulerLock); NewLock(SchedulerLock);
@ -476,114 +476,43 @@ namespace Tasking
PID NextPID = 0; PID NextPID = 0;
TID NextTID = 0; TID NextTID = 0;
std::list<PCB *> ProcessList;
PCB *KernelProcess = nullptr; 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); void *Scheduler = nullptr;
bool InvalidTCB(TCB *tcb); void *__sched_ctx = nullptr;
/** constexpr TaskArchitecture GetKArch()
* Remove a thread from the scheduler {
* #if defined(a64)
* @note This function is NOT thread safe return x64;
* @note This function does not check if #elif defined(a32)
* the thread is valid nor if it has return x32;
* Terminated status #elif defined(aa64)
*/ return ARM64;
bool RemoveThread(TCB *tcb); #endif
}
/** void PushProcess(PCB *pcb);
* @note This function is NOT thread safe void PopProcess(PCB *pcb);
*/
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;
public: public:
void *GetScheduler() { return Scheduler; }
PCB *GetKernelProcess() { return KernelProcess; } PCB *GetKernelProcess() { return KernelProcess; }
size_t GetSchedulerTicks() { return SchedulerTicks.load(); } std::list<PCB *> GetProcessList();
size_t GetLastTaskTicks() { return LastTaskTicks.load(); } void Panic();
int GetLastCore() { return LastCore.load(); } bool IsPanic();
std::list<PCB *> GetProcessList() { return ProcessList; }
void Panic() { StopScheduler = true; }
bool IsPanic() { return StopScheduler; }
/** /**
* Yield the current thread and switch * Yield the current thread and switch
* to another thread if available * to another thread if available
*/ */
__always_inline inline void Yield() 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
}
/** /**
* Update the current thread's trap frame * Update the current thread's trap frame
* without switching to another thread * without switching to another thread
*/ */
__always_inline inline void UpdateFrame() void UpdateFrame();
{
SchedulerUpdateTrapFrame = true;
Yield();
}
void SignalShutdown(); void SignalShutdown();
@ -670,6 +599,4 @@ namespace Tasking
#define thisProcess GetCurrentCPU()->CurrentProcess.load() #define thisProcess GetCurrentCPU()->CurrentProcess.load()
#define thisThread GetCurrentCPU()->CurrentThread.load() #define thisThread GetCurrentCPU()->CurrentThread.load()
extern "C" void TaskingScheduler_OneShot(int TimeSlice);
#endif // !__FENNIX_KERNEL_TASKING_H__ #endif // !__FENNIX_KERNEL_TASKING_H__

View File

@ -228,7 +228,7 @@ namespace Tasking
if (Parent) if (Parent)
Parent->Children.push_back(this); Parent->Children.push_back(this);
ctx->ProcessList.push_back(this); ctx->PushProcess(this);
} }
PCB::~PCB() PCB::~PCB()
@ -240,9 +240,7 @@ namespace Tasking
debug("Removing from process list"); debug("Removing from process list");
/* Remove us from the process list so we /* Remove us from the process list so we
don't get scheduled anymore */ don't get scheduled anymore */
ctx->ProcessList.erase(std::find(ctx->ProcessList.begin(), ctx->PopProcess(this);
ctx->ProcessList.end(),
this));
debug("Freeing signals"); debug("Freeing signals");
delete this->Signals; delete this->Signals;

View File

@ -15,7 +15,7 @@
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>. along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <task.hpp> #include <scheduler.hpp>
#include <dumper.hpp> #include <dumper.hpp>
#include <convert.h> #include <convert.h>
@ -103,26 +103,187 @@
#define wut_schedbg(m, ...) #define wut_schedbg(m, ...)
#endif #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) #if defined(a86)
((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice); asmv("IdleLoop:");
asmv("hlt");
asmv("jmp IdleLoop");
#elif defined(aa64) #elif defined(aa64)
asmv("IdleLoop:");
asmv("wfe");
asmv("b IdleLoop");
#endif #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<PCB *> &Custom::GetProcessList()
{
return ProcessList;
}
void Custom::StartScheduler()
{
#if defined(a86) #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; CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
fnp_schedbg("%d processes", ProcessList.size()); fnp_schedbg("%d processes", ProcessList.size());
@ -133,9 +294,6 @@ namespace Tasking
#endif #endif
foreach (auto process in ProcessList) foreach (auto process in ProcessList)
{ {
if (unlikely(InvalidPCB(process)))
continue;
switch (process->State.load()) switch (process->State.load())
{ {
case TaskState::Ready: case TaskState::Ready:
@ -157,9 +315,6 @@ namespace Tasking
foreach (auto thread in process->Threads) foreach (auto thread in process->Threads)
{ {
if (unlikely(InvalidTCB(thread)))
continue;
if (thread->State.load() != TaskState::Ready) if (thread->State.load() != TaskState::Ready)
continue; continue;
@ -175,7 +330,7 @@ namespace Tasking
return false; return false;
} }
nsa NIF bool Task::GetNextAvailableThread(void *CPUDataPointer) nsa NIF bool Custom::GetNextAvailableThread(void *CPUDataPointer)
{ {
CPUData *CurrentCPU = (CPUData *)CPUDataPointer; CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
@ -192,16 +347,6 @@ namespace Tasking
TCB *nextThread = CurrentCPU->CurrentProcess->Threads[TempIndex + 1]; 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)", gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)",
CurrentCPU->CurrentProcess->Threads[i]->Name, CurrentCPU->CurrentProcess->Threads[i]->Name,
CurrentCPU->CurrentProcess->Threads[i]->ID, CurrentCPU->CurrentProcess->Threads[i]->ID,
@ -234,7 +379,7 @@ namespace Tasking
return false; return false;
} }
nsa NIF bool Task::GetNextAvailableProcess(void *CPUDataPointer) nsa NIF bool Custom::GetNextAvailableProcess(void *CPUDataPointer)
{ {
CPUData *CurrentCPU = (CPUData *)CPUDataPointer; CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
@ -254,12 +399,6 @@ namespace Tasking
continue; continue;
} }
if (unlikely(InvalidPCB(process)))
{
gnap_schedbg("Invalid process %#lx", process);
continue;
}
if (process->State.load() != TaskState::Ready) if (process->State.load() != TaskState::Ready)
{ {
gnap_schedbg("Process %d is not ready", process->ID); gnap_schedbg("Process %d is not ready", process->ID);
@ -268,12 +407,6 @@ namespace Tasking
foreach (auto thread in process->Threads) foreach (auto thread in process->Threads)
{ {
if (unlikely(InvalidTCB(thread)))
{
gnap_schedbg("Invalid thread %#lx", thread);
continue;
}
if (thread->State.load() != TaskState::Ready) if (thread->State.load() != TaskState::Ready)
{ {
gnap_schedbg("Thread %d is not ready", thread->ID); gnap_schedbg("Thread %d is not ready", thread->ID);
@ -294,18 +427,12 @@ namespace Tasking
return false; return false;
} }
nsa NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) nsa NIF bool Custom::SchedulerSearchProcessThread(void *CPUDataPointer)
{ {
CPUData *CurrentCPU = (CPUData *)CPUDataPointer; CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
foreach (auto process in ProcessList) foreach (auto process in ProcessList)
{ {
if (unlikely(InvalidPCB(process)))
{
sspt_schedbg("Invalid process %#lx", process);
continue;
}
if (process->State.load() != TaskState::Ready) if (process->State.load() != TaskState::Ready)
{ {
sspt_schedbg("Process %d is not ready", process->ID); sspt_schedbg("Process %d is not ready", process->ID);
@ -314,12 +441,6 @@ namespace Tasking
foreach (auto thread in process->Threads) foreach (auto thread in process->Threads)
{ {
if (unlikely(InvalidTCB(thread)))
{
sspt_schedbg("Invalid thread %#lx", thread);
continue;
}
if (thread->State.load() != TaskState::Ready) if (thread->State.load() != TaskState::Ready)
{ {
sspt_schedbg("Thread %d is not ready", thread->ID); sspt_schedbg("Thread %d is not ready", thread->ID);
@ -339,13 +460,10 @@ namespace Tasking
return false; return false;
} }
nsa NIF void Task::UpdateProcessState() nsa NIF void Custom::UpdateProcessState()
{ {
foreach (auto process in ProcessList) foreach (auto process in ProcessList)
{ {
if (unlikely(InvalidPCB(process)))
continue;
if (process->State.load() == TaskState::Terminated) if (process->State.load() == TaskState::Terminated)
continue; continue;
@ -375,13 +493,10 @@ namespace Tasking
} }
} }
nsa NIF void Task::WakeUpThreads() nsa NIF void Custom::WakeUpThreads()
{ {
foreach (auto process in ProcessList) foreach (auto process in ProcessList)
{ {
if (unlikely(InvalidPCB(process)))
continue;
Tasking::TaskState pState = process->State.load(); Tasking::TaskState pState = process->State.load();
if (pState != TaskState::Ready && if (pState != TaskState::Ready &&
pState != TaskState::Sleeping && pState != TaskState::Sleeping &&
@ -390,9 +505,6 @@ namespace Tasking
foreach (auto thread in process->Threads) foreach (auto thread in process->Threads)
{ {
if (unlikely(InvalidTCB(thread)))
continue;
if (likely(thread->State.load() != TaskState::Sleeping)) if (likely(thread->State.load() != TaskState::Sleeping))
continue; continue;
@ -415,7 +527,7 @@ namespace Tasking
} }
} }
nsa NIF void Task::CleanupTerminated() nsa NIF void Custom::CleanupTerminated()
{ {
foreach (auto pcb in ProcessList) 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)) if (unlikely(StopScheduler))
{ {
@ -450,8 +562,8 @@ namespace Tasking
this->LastCore.store(CurrentCPU->ID); this->LastCore.store(CurrentCPU->ID);
schedbg("Scheduler called on CPU %d.", CurrentCPU->ID); schedbg("Scheduler called on CPU %d.", CurrentCPU->ID);
if (unlikely(InvalidPCB(CurrentCPU->CurrentProcess.load()) || if (unlikely(!CurrentCPU->CurrentProcess.load() ||
InvalidTCB(CurrentCPU->CurrentThread.load()))) !CurrentCPU->CurrentThread.load()))
{ {
schedbg("Invalid process or thread. Finding a new one."); schedbg("Invalid process or thread. Finding a new one.");
ProcessNotChanged = true; ProcessNotChanged = true;
@ -524,9 +636,7 @@ namespace Tasking
} }
} }
warn("Unwanted reach!"); assert(!"Unwanted code execution");
TaskingScheduler_OneShot(100);
goto End;
Idle: Idle:
ProcessNotChanged = true; ProcessNotChanged = true;
@ -586,7 +696,7 @@ namespace Tasking
if (!ProcessNotChanged) if (!ProcessNotChanged)
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter(); (&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
(&CurrentCPU->CurrentThread->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 && if (CurrentCPU->CurrentThread->Security.IsDebugEnabled &&
CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled) CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled)
@ -627,42 +737,74 @@ namespace Tasking
Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode);
schedbg("================================================================"); schedbg("================================================================");
End:
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks)); this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
CurrentCPU->CurrentProcess->PageTable->Update(); CurrentCPU->CurrentProcess->PageTable->Update();
} }
nsa NIF void Task::OnInterruptReceived(CPU::TrapFrame *Frame) nsa NIF void Custom::OnInterruptReceived(CPU::TrapFrame *Frame)
{ {
SmartCriticalSection(SchedulerLock); SmartCriticalSection(SchedulerLock);
this->Schedule(Frame); 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"); #if defined(a86)
} // Map the IRQ16 to the first CPU.
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1);
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); }
#endif #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);
}
}
} }

View File

@ -17,6 +17,7 @@
#include <task.hpp> #include <task.hpp>
#include <scheduler.hpp>
#include <dumper.hpp> #include <dumper.hpp>
#include <convert.h> #include <convert.h>
#include <lock.hpp> #include <lock.hpp>
@ -46,104 +47,6 @@
namespace Tasking 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() PCB *Task::GetCurrentProcess()
{ {
return GetCurrentCPU()->CurrentProcess.load(); return GetCurrentCPU()->CurrentProcess.load();
@ -156,34 +59,52 @@ namespace Tasking
PCB *Task::GetProcessByID(TID ID) PCB *Task::GetProcessByID(TID ID)
{ {
SmartLock(TaskingLock); return ((Scheduler::Base *)Scheduler)->GetProcessByID(ID);
foreach (auto p in ProcessList)
{
if (p->ID == ID)
return p;
}
return nullptr;
} }
TCB *Task::GetThreadByID(TID ID) TCB *Task::GetThreadByID(TID ID)
{ {
SmartLock(TaskingLock); return ((Scheduler::Base *)Scheduler)->GetThreadByID(ID);
foreach (auto p in ProcessList) }
{
foreach (auto t in p->Threads) std::list<PCB *> Task::GetProcessList()
{ {
if (t->ID == ID) return ((Scheduler::Base *)Scheduler)->GetProcessList();
return t; }
}
} void Task::Panic()
return nullptr; {
((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) void Task::WaitForProcess(PCB *pcb)
{ {
if (unlikely(InvalidPCB(pcb)))
return;
if (pcb->State == TaskState::UnknownStatus) if (pcb->State == TaskState::UnknownStatus)
return; return;
@ -198,9 +119,6 @@ namespace Tasking
void Task::WaitForThread(TCB *tcb) void Task::WaitForThread(TCB *tcb)
{ {
if (unlikely(InvalidTCB(tcb)))
return;
if (tcb->State == TaskState::UnknownStatus) if (tcb->State == TaskState::UnknownStatus)
return; return;
@ -215,9 +133,6 @@ namespace Tasking
void Task::WaitForProcessStatus(PCB *pcb, TaskState status) void Task::WaitForProcessStatus(PCB *pcb, TaskState status)
{ {
if (unlikely(InvalidPCB(pcb)))
return;
if (pcb->State == TaskState::UnknownStatus) if (pcb->State == TaskState::UnknownStatus)
return; return;
@ -230,9 +145,6 @@ namespace Tasking
void Task::WaitForThreadStatus(TCB *tcb, TaskState status) void Task::WaitForThreadStatus(TCB *tcb, TaskState status)
{ {
if (unlikely(InvalidTCB(tcb)))
return;
if (tcb->State == TaskState::UnknownStatus) if (tcb->State == TaskState::UnknownStatus)
return; return;
@ -277,7 +189,7 @@ namespace Tasking
GetCurrentProcess()->Name, GetCurrentProcess()->ID, GetCurrentProcess()->Name, GetCurrentProcess()->ID,
GetCurrentThread()->Name, GetCurrentThread()->ID); GetCurrentThread()->Name, GetCurrentThread()->ID);
foreach (auto pcb in ProcessList) foreach (auto pcb in((Scheduler::Base *)Scheduler)->GetProcessList())
{ {
if (pcb->State == TaskState::Terminated || if (pcb->State == TaskState::Terminated ||
pcb->State == TaskState::Zombie) pcb->State == TaskState::Zombie)
@ -295,13 +207,10 @@ namespace Tasking
} }
__no_sanitize("undefined") __no_sanitize("undefined")
TCB *Task::CreateThread(PCB *Parent, TCB *Task::CreateThread(PCB *Parent, IP EntryPoint,
IP EntryPoint, const char **argv, const char **envp,
const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv, const std::vector<AuxiliaryVector> &auxv,
TaskArchitecture arch, TaskArchitecture arch, TaskCompatibility Compatibility,
TaskCompatibility Compatibility,
bool ThreadNotReady) bool ThreadNotReady)
{ {
SmartLock(TaskingLock); SmartLock(TaskingLock);
@ -310,10 +219,8 @@ namespace Tasking
Compatibility, ThreadNotReady); Compatibility, ThreadNotReady);
} }
PCB *Task::CreateProcess(PCB *Parent, PCB *Task::CreateProcess(PCB *Parent, const char *Name,
const char *Name, TaskExecutionMode ExecutionMode, bool UseKernelPageTable,
TaskExecutionMode ExecutionMode,
bool UseKernelPageTable,
uint16_t UserID, uint16_t GroupID) uint16_t UserID, uint16_t GroupID)
{ {
SmartLock(TaskingLock); SmartLock(TaskingLock);
@ -323,169 +230,40 @@ namespace Tasking
void Task::StartScheduler() void Task::StartScheduler()
{ {
#if defined(a86) ((Scheduler::Base *)Scheduler)->StartScheduler();
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
debug("Tasking Started"); debug("Tasking Started");
} }
Task::Task(const IP EntryPoint) : Interrupts::Handler(16) /* IRQ16 */ Task::Task(const IP EntryPoint)
{ {
#if defined(a64) /* I don't know if this is the best way to do this. */
// Map the IRQ16 to the first CPU. Scheduler::Custom *custom_sched = new Scheduler::Custom(this);
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1); Scheduler::Base *sched = r_cst(Scheduler::Base *, custom_sched);
#elif defined(a32) __sched_ctx = custom_sched;
#elif defined(aa64) Scheduler = sched;
#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
KernelProcess = CreateProcess(nullptr, "Kernel", KernelProcess = CreateProcess(nullptr, "Kernel",
TaskExecutionMode::Kernel, true); TaskExecutionMode::Kernel, true);
KernelProcess->PageTable = KernelPageTable; KernelProcess->PageTable = KernelPageTable;
TCB *kthrd = CreateThread(KernelProcess, EntryPoint, TCB *kthrd = CreateThread(KernelProcess, EntryPoint,
nullptr, nullptr, nullptr, nullptr,
std::vector<AuxiliaryVector>(), Arch); std::vector<AuxiliaryVector>(), GetKArch());
kthrd->Rename("Main Thread"); kthrd->Rename("Main Thread");
debug("Created Kernel Process: %s and Thread: %s", debug("Created Kernel Process: %s and Thread: %s",
KernelProcess->Name, kthrd->Name); 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)) if (!CPU::Interrupts(CPU::Check))
{ {
error("Interrupts are not enabled."); error("Interrupts are not enabled.");
CPU::Interrupts(CPU::Enable); CPU::Interrupts(CPU::Enable);
} }
IdleProcess = CreateProcess(nullptr, (char *)"Idle", ((Scheduler::Base *)Scheduler)->StartIdleProcess();
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;
}
debug("Tasking is ready"); debug("Tasking is ready");
} }
Task::~Task() Task::~Task()
{ {
debug("Destructor called"); delete (Scheduler::Custom *)__sched_ctx;
{
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");
} }
} }