Clean up scheduler.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-02-21 17:05:10 +01:00
parent ae364db284
commit 8c2befc140
5 changed files with 97 additions and 187 deletions

View File

@ -318,8 +318,8 @@ Process* FindProcess(pid_t least_pid, pid_t max_pid)
if ( least_pid <= current_process->pid && current_process->pid <= max_pid ) if ( least_pid <= current_process->pid && current_process->pid <= max_pid )
best = current_process; best = current_process;
Thread* first_thread = current_thread; Thread* first_thread = current_thread;
for ( Thread* iter = first_thread->schedulerlistnext; for ( Thread* iter = first_thread->scheduler_list_next;
iter != first_thread; iter = iter->schedulerlistnext ) iter != first_thread; iter = iter->scheduler_list_next )
if ( least_pid <= iter->process->pid && iter->process->pid <= max_pid && if ( least_pid <= iter->process->pid && iter->process->pid <= max_pid &&
(!best || iter->process->pid < best->pid) ) (!best || iter->process->pid < best->pid) )
best = iter->process; best = iter->process;

View File

@ -45,21 +45,24 @@ enum ThreadState { NONE, RUNNABLE, BLOCKING, DEAD };
namespace Sortix { namespace Sortix {
namespace Scheduler { namespace Scheduler {
void Init();
void Switch(CPU::InterruptRegisters* regs);
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
inline static void Yield() { asm volatile ("int $129"); } static inline void Yield()
{
asm volatile ("int $129");
}
__attribute__ ((noreturn)) __attribute__ ((noreturn))
inline static void ExitThread() static inline void ExitThread()
{ {
asm volatile ("int $132"); asm volatile ("int $132");
__builtin_unreachable(); __builtin_unreachable();
} }
#endif #endif
void Init();
void Switch(CPU::InterruptRegisters* regs);
void SetThreadState(Thread* thread, ThreadState state); void SetThreadState(Thread* thread, ThreadState state);
ThreadState GetThreadState(Thread* thread); ThreadState GetThreadState(Thread* thread);
void SetIdleThread(Thread* thread); void SetIdleThread(Thread* thread);
void SetDummyThreadOwner(Process* process);
void SetInitProcess(Process* init); void SetInitProcess(Process* init);
Process* GetInitProcess(); Process* GetInitProcess();
Process* GetKernelProcess(); Process* GetKernelProcess();

View File

@ -92,15 +92,12 @@ public:
public: public:
size_t id; size_t id;
Process* process; Process* process;
bool terminated;
Thread* prevsibling; Thread* prevsibling;
Thread* nextsibling; Thread* nextsibling;
// These are some things used internally by the scheduler and should not be
// touched by anything but it. Consider it private.
public: public:
Thread* schedulerlistprev; Thread* scheduler_list_prev;
Thread* schedulerlistnext; Thread* scheduler_list_next;
volatile ThreadState state; volatile ThreadState state;
uint8_t fpuenv[512UL + 16UL]; uint8_t fpuenv[512UL + 16UL];
uint8_t* fpuenvaligned; uint8_t* fpuenvaligned;

View File

@ -27,12 +27,12 @@
#include <assert.h> #include <assert.h>
#include <msr.h> #include <msr.h>
#include <string.h> #include <string.h>
#include <timespec.h> #include <timespec.h>
#include <sortix/clock.h> #include <sortix/clock.h>
#include <sortix/timespec.h> #include <sortix/timespec.h>
#include <sortix/kernel/decl.h>
#include <sortix/kernel/interrupt.h> #include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kernel.h> #include <sortix/kernel/kernel.h>
#include <sortix/kernel/memorymanagement.h> #include <sortix/kernel/memorymanagement.h>
@ -43,116 +43,69 @@
#include <sortix/kernel/thread.h> #include <sortix/kernel/thread.h>
#include <sortix/kernel/time.h> #include <sortix/kernel/time.h>
#if defined(__i386__) || defined(__x86_64__)
#include "x86-family/gdt.h" #include "x86-family/gdt.h"
#include "x86-family/float.h" #include "x86-family/float.h"
#endif
namespace Sortix { namespace Sortix {
namespace Scheduler { namespace Scheduler {
const uint32_t SCHED_MAGIC = 0x1234567; static Thread* current_thread;
static Thread* idle_thread;
volatile unsigned long premagic; static Thread* first_runnable_thread;
static Thread* currentthread; static Thread* first_sleeping_thread;
} // namespace Scheduler static Process* init_process;
Thread* CurrentThread() { return Scheduler::currentthread; }
Process* CurrentProcess() { return CurrentThread()->process; }
namespace Scheduler {
uint8_t dummythreaddata[sizeof(Thread)] __attribute__ ((aligned (8)));
Thread* dummythread;
Thread* idlethread;
Thread* firstrunnablethread;
Thread* firstsleepingthread;
Process* initprocess;
volatile unsigned long postmagic;
static inline void SetCurrentThread(Thread* newcurrentthread)
{
currentthread = newcurrentthread;
}
void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs);
void LogSwitch(Thread* current, Thread* next);
void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs);
static Thread* PopNextThread() static Thread* PopNextThread()
{ {
if ( !firstrunnablethread ) { return idlethread; } if ( first_runnable_thread )
Thread* result = firstrunnablethread; {
firstrunnablethread = firstrunnablethread->schedulerlistnext; Thread* result = first_runnable_thread;
return result; first_runnable_thread = first_runnable_thread->scheduler_list_next;
} return result;
}
static Thread* ValidatedPopNextThread() return idle_thread;
{
assert(premagic == SCHED_MAGIC);
assert(postmagic == SCHED_MAGIC);
Thread* nextthread = PopNextThread();
if ( !nextthread ) { Panic("Had no thread to switch to."); }
if ( nextthread->terminated )
{
PanicF("Running a terminated thread 0x%p", nextthread);
}
addr_t newaddrspace = nextthread->process->addrspace;
if ( !Page::IsAligned(newaddrspace) )
{
PanicF("Thread 0x%p, process %ji (0x%p) (backup: %i), had bad "
"address space variable: 0x%zx: not page-aligned "
"(backup: 0x%zx)\n", nextthread,
(intmax_t) nextthread->process->pid, nextthread->process,
-1/*nextthread->pidbackup*/, newaddrspace,
(addr_t)-1 /*nextthread->addrspacebackup*/);
}
return nextthread;
} }
static void DoActualSwitch(CPU::InterruptRegisters* regs) static void DoActualSwitch(CPU::InterruptRegisters* regs)
{ {
Thread* current = CurrentThread(); Thread* prev = CurrentThread();
LogBeginSwitch(current, regs); Thread* next = PopNextThread();
Thread* next = ValidatedPopNextThread(); if ( prev == next )
LogSwitch(current, next); return;
if ( current == next ) { return; } prev->SaveRegisters(regs);
current->SaveRegisters(regs);
next->LoadRegisters(regs); next->LoadRegisters(regs);
Memory::SwitchAddressSpace(next->addrspace);
current_thread = next;
#if defined(__i386__) || defined(__x86_64__)
Float::NotityTaskSwitch(); Float::NotityTaskSwitch();
GDT::SetKernelStack(next->kernelstackpos, next->kernelstacksize,
addr_t newaddrspace = next->addrspace; next->kernelstackpos + next->kernelstacksize);
Memory::SwitchAddressSpace(newaddrspace);
SetCurrentThread(next);
addr_t stacklower = next->kernelstackpos;
size_t stacksize = next->kernelstacksize;
addr_t stackhigher = stacklower + stacksize;
assert(stacklower && stacksize && stackhigher);
GDT::SetKernelStack(stacklower, stacksize, stackhigher);
#if defined(__x86_64__)
current->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
current->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
wrmsr(MSRID_FSBASE, (uint64_t) next->fsbase);
wrmsr(MSRID_GSBASE, (uint64_t) next->gsbase);
#elif defined(__i386__)
current->fsbase = (unsigned long) GDT::GetFSBase();
current->gsbase = (unsigned long) GDT::GetGSBase();
GDT::SetFSBase((uint32_t) next->fsbase);
GDT::SetGSBase((uint32_t) next->gsbase);
#endif #endif
LogEndSwitch(next, regs); #if defined(__i386__)
prev->fsbase = (unsigned long) GDT::GetFSBase();
prev->gsbase = (unsigned long) GDT::GetGSBase();
GDT::SetFSBase((uint32_t) next->fsbase);
GDT::SetGSBase((uint32_t) next->gsbase);
#elif defined(__x86_64__)
prev->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
prev->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
wrmsr(MSRID_FSBASE, (uint64_t) next->fsbase);
wrmsr(MSRID_GSBASE, (uint64_t) next->gsbase);
#endif
} }
void Switch(CPU::InterruptRegisters* regs) void Switch(CPU::InterruptRegisters* regs)
{ {
assert(premagic == SCHED_MAGIC);
assert(postmagic == SCHED_MAGIC);
DoActualSwitch(regs); DoActualSwitch(regs);
assert(premagic == SCHED_MAGIC);
assert(postmagic == SCHED_MAGIC);
if ( regs->signal_pending && regs->InUserspace() ) if ( regs->signal_pending && regs->InUserspace() )
{ {
Interrupt::Enable(); Interrupt::Enable();
@ -160,49 +113,6 @@ void Switch(CPU::InterruptRegisters* regs)
} }
} }
const bool DEBUG_BEGINCTXSWITCH = false;
const bool DEBUG_CTXSWITCH = false;
const bool DEBUG_ENDCTXSWITCH = false;
void LogBeginSwitch(Thread* current, const CPU::InterruptRegisters* regs)
{
bool alwaysdebug = false;
bool isidlethread = current == idlethread;
bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread;
if ( alwaysdebug || dodebug )
{
Log::PrintF("Switching from 0x%p", current);
regs->LogRegisters();
Log::Print("\n");
}
}
void LogSwitch(Thread* current, Thread* next)
{
bool alwaysdebug = false;
bool different = current == idlethread;
bool dodebug = DEBUG_CTXSWITCH && different;
if ( alwaysdebug || dodebug )
{
Log::PrintF("switching from %ji:%u (0x%p) to %ji:%u (0x%p) \n",
(intmax_t) current->process->pid, 0, current,
(intmax_t) next->process->pid, 0, next);
}
}
void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs)
{
bool alwaysdebug = false;
bool isidlethread = current == idlethread;
bool dodebug = DEBUG_BEGINCTXSWITCH && !isidlethread;
if ( alwaysdebug || dodebug )
{
Log::PrintF("Switched to 0x%p", current);
regs->LogRegisters();
Log::Print("\n");
}
}
void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/) void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/)
{ {
Switch(regs); Switch(regs);
@ -210,9 +120,11 @@ void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/)
void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/) void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/)
{ {
#if defined(__i386__) || defined(__x86_64__)
// Can't use floating point instructions from now. // Can't use floating point instructions from now.
Float::NofityTaskExit(currentthread); Float::NofityTaskExit(current_thread);
SetThreadState(currentthread, ThreadState::DEAD); #endif
SetThreadState(current_thread, ThreadState::DEAD);
InterruptYieldCPU(regs, NULL); InterruptYieldCPU(regs, NULL);
} }
@ -220,30 +132,25 @@ void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/)
// nothing, which is only run when the system has nothing to do. // nothing, which is only run when the system has nothing to do.
void SetIdleThread(Thread* thread) void SetIdleThread(Thread* thread)
{ {
assert(!idlethread); assert(!idle_thread);
idlethread = thread; idle_thread = thread;
SetThreadState(thread, ThreadState::NONE); SetThreadState(thread, ThreadState::NONE);
SetCurrentThread(thread); current_thread = thread;
}
void SetDummyThreadOwner(Process* process)
{
dummythread->process = process;
} }
void SetInitProcess(Process* init) void SetInitProcess(Process* init)
{ {
initprocess = init; init_process = init;
} }
Process* GetInitProcess() Process* GetInitProcess()
{ {
return initprocess; return init_process;
} }
Process* GetKernelProcess() Process* GetKernelProcess()
{ {
return idlethread->process; return idle_thread->process;
} }
void SetThreadState(Thread* thread, ThreadState state) void SetThreadState(Thread* thread, ThreadState state)
@ -254,31 +161,34 @@ void SetThreadState(Thread* thread, ThreadState state)
if ( thread->state == ThreadState::RUNNABLE && if ( thread->state == ThreadState::RUNNABLE &&
state != ThreadState::RUNNABLE ) state != ThreadState::RUNNABLE )
{ {
if ( thread == firstrunnablethread ) { firstrunnablethread = thread->schedulerlistnext; } if ( thread == first_runnable_thread )
if ( thread == firstrunnablethread ) { firstrunnablethread = NULL; } first_runnable_thread = thread->scheduler_list_next;
assert(thread->schedulerlistprev); if ( thread == first_runnable_thread )
assert(thread->schedulerlistnext); first_runnable_thread = NULL;
thread->schedulerlistprev->schedulerlistnext = thread->schedulerlistnext; assert(thread->scheduler_list_prev);
thread->schedulerlistnext->schedulerlistprev = thread->schedulerlistprev; assert(thread->scheduler_list_next);
thread->schedulerlistprev = NULL; thread->scheduler_list_prev->scheduler_list_next = thread->scheduler_list_next;
thread->schedulerlistnext = NULL; thread->scheduler_list_next->scheduler_list_prev = thread->scheduler_list_prev;
thread->scheduler_list_prev = NULL;
thread->scheduler_list_next = NULL;
} }
// Insert the thread into the scheduler's carousel linked list. // Insert the thread into the scheduler's carousel linked list.
if ( thread->state != ThreadState::RUNNABLE && if ( thread->state != ThreadState::RUNNABLE &&
state == ThreadState::RUNNABLE ) state == ThreadState::RUNNABLE )
{ {
if ( firstrunnablethread == NULL ) { firstrunnablethread = thread; } if ( first_runnable_thread == NULL )
thread->schedulerlistprev = firstrunnablethread->schedulerlistprev; first_runnable_thread = thread;
thread->schedulerlistnext = firstrunnablethread; thread->scheduler_list_prev = first_runnable_thread->scheduler_list_prev;
firstrunnablethread->schedulerlistprev = thread; thread->scheduler_list_next = first_runnable_thread;
thread->schedulerlistprev->schedulerlistnext = thread; first_runnable_thread->scheduler_list_prev = thread;
thread->scheduler_list_prev->scheduler_list_next = thread;
} }
thread->state = state; thread->state = state;
assert(thread->state != ThreadState::RUNNABLE || thread->schedulerlistprev); assert(thread->state != ThreadState::RUNNABLE || thread->scheduler_list_prev);
assert(thread->state != ThreadState::RUNNABLE || thread->schedulerlistnext); assert(thread->state != ThreadState::RUNNABLE || thread->scheduler_list_next);
Interrupt::SetEnabled(wasenabled); Interrupt::SetEnabled(wasenabled);
} }
@ -321,19 +231,9 @@ static int sys_sched_yield(void)
void Init() void Init()
{ {
premagic = postmagic = SCHED_MAGIC; first_runnable_thread = NULL;
first_sleeping_thread = NULL;
// We use a dummy so that the first context switch won't crash when the idle_thread = NULL;
// current thread is accessed. This lets us avoid checking whether it is
// NULL (which it only will be once), which gives simpler code.
dummythread = (Thread*) &dummythreaddata;
memset(dummythread, 0, sizeof(*dummythread));
dummythread->schedulerlistprev = dummythread;
dummythread->schedulerlistnext = dummythread;
currentthread = dummythread;
firstrunnablethread = NULL;
firstsleepingthread = NULL;
idlethread = NULL;
Syscall::Register(SYSCALL_SLEEP, (void*) sys_sleep); Syscall::Register(SYSCALL_SLEEP, (void*) sys_sleep);
Syscall::Register(SYSCALL_USLEEP, (void*) sys_usleep); Syscall::Register(SYSCALL_USLEEP, (void*) sys_usleep);
@ -342,3 +242,17 @@ void Init()
} // namespace Scheduler } // namespace Scheduler
} // namespace Sortix } // namespace Sortix
namespace Sortix {
Thread* CurrentThread()
{
return Scheduler::current_thread;
}
Process* CurrentProcess()
{
return CurrentThread()->process;
}
} // namespace Sortix

View File

@ -52,8 +52,8 @@ Thread::Thread()
process = NULL; process = NULL;
prevsibling = NULL; prevsibling = NULL;
nextsibling = NULL; nextsibling = NULL;
schedulerlistprev = NULL; scheduler_list_prev = NULL;
schedulerlistnext = NULL; scheduler_list_next = NULL;
state = NONE; state = NONE;
memset(&registers, 0, sizeof(registers)); memset(&registers, 0, sizeof(registers));
fsbase = 0; fsbase = 0;
@ -62,7 +62,6 @@ Thread::Thread()
kernelstacksize = 0; kernelstacksize = 0;
kernelstackmalloced = false; kernelstackmalloced = false;
pledged_destruction = false; pledged_destruction = false;
terminated = false;
fpuinitialized = false; fpuinitialized = false;
// If malloc isn't 16-byte aligned, then we can't rely on offsets in // If malloc isn't 16-byte aligned, then we can't rely on offsets in
// our own class, so we'll just fix ourselves nicely up. // our own class, so we'll just fix ourselves nicely up.
@ -81,7 +80,6 @@ Thread::~Thread()
assert(CurrentThread() != this); assert(CurrentThread() != this);
if ( kernelstackmalloced ) if ( kernelstackmalloced )
delete[] (uint8_t*) kernelstackpos; delete[] (uint8_t*) kernelstackpos;
terminated = true;
} }
addr_t Thread::SwitchAddressSpace(addr_t newaddrspace) addr_t Thread::SwitchAddressSpace(addr_t newaddrspace)
@ -232,8 +230,6 @@ static int sys_exit_thread(int requested_exit_code,
continue; continue;
if ( iter->pledged_destruction ) if ( iter->pledged_destruction )
continue; continue;
if ( iter->terminated )
continue;
is_others = true; is_others = true;
} }
if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others ) if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others )