os-k/kaleid/kernel/proc/sched.c

410 lines
9.9 KiB
C

//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Authors: spectral` //
// NeoX //
// //
// Desc: Scheduling algorithm //
//----------------------------------------------------------------------------//
#include <kernel/kernsched.h>
#ifndef _KALEID_KERNEL
#include <stdio.h>
CREATE_PER_CPU(CurProc, Process_t *);
//
// For test purpose only
//
int procslen = 9;
Process_t procs[] = {
{ 0, 0, 0, 12, 12, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 1, 2, 2, 16, 16, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 2, 3, 3, 31, 31, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 3, 2, 2, 1, 1, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 4, 0, 0, 5, 5, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 5, 0, 0, 30, 30, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 6, 1, 1, 19, 19, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 7, 1, 1, 0, 0, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
{ 8, 3, 3, 12, 12, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
};
#endif
//
// Set current process
// TODO Select thread, context switch
//
static inline
void SetCurProc(Process_t *proc)
{
_SetCurProc(proc);
if (GetCurProc() != NULL) {
GetCurProc()->procState = STATE_RUNNING;
}
}
//
// (Un)Lock priority class list heads
//
static inline
void SchedLock(void) {
#ifdef _KALEID_KERNEL
DisableIRQs();
#endif
}
static inline
void SchedUnlock(void) {
#ifdef _KALEID_KERNEL
EnableIRQs();
#endif
}
//
// The four priority classes of OS/2
//
CREATE_PER_CPU(IdlePrioProcs, ListHead_t *);
CREATE_PER_CPU(ReglPrioProcs, ListHead_t *);
CREATE_PER_CPU(ServPrioProcs, ListHead_t *);
CREATE_PER_CPU(TimeCritProcs, ListHead_t *);
char *PrioClassesNames[] = {
"Idle priority class",
"Regular priority class",
"Server priority class",
"Time-critical class",
};
enum { IDLE_PRIO_PROC = 0,
REGL_PRIO_PROC = 1,
SERV_PRIO_PROC = 2,
TIME_CRIT_PROC = 3,
};
//
// Get priority class list head
//
static inline
ListHead_t *GetPrioClassHead(int prioClass)
{
switch (prioClass) {
case TIME_CRIT_PROC: return GetTimeCritProcs();
case SERV_PRIO_PROC: return GetServPrioProcs();
case REGL_PRIO_PROC: return GetReglPrioProcs();
case IDLE_PRIO_PROC: return GetIdlePrioProcs();
default: KalAssert(FALSE && "Unknown priority class");
}
return NULL;
}
//
// Determine which process is going to run first
// Return NULL for "equal" processes
//
static inline
Process_t *CompareProcs(Process_t *proc1, Process_t *proc2)
{
KalAssert(proc1 && proc2);
if (proc1->prioClass > proc2->prioClass) return proc1;
if (proc1->prioClass < proc2->prioClass) return proc2;
if (proc1->prioLevel > proc2->prioLevel) return proc1;
if (proc1->prioLevel < proc2->prioLevel) return proc2;
return NULL; // same class and level
}
//
// Add process to schedule lists (unlocked)
//
static inline
void SchedThisProcUnlocked(Process_t *proc)
{
KalAssert(proc && proc->procState == STATE_RUNNABLE);
bool found = false;
ListNode_t *iterNode = NULL;
ListNode_t *procNode = CreateNode(proc);
ListHead_t *head = GetPrioClassHead(proc->prioClass);
KalAssert(procNode && head);
proc->schedNode = procNode;
//printdbg("Adding process %d to '%s'\n", proc->pid, PrioClassesNames[proc->prioClass]);
//
// Find a process with lesser priority
//
for (iterNode = head->first; iterNode; iterNode = iterNode->next) {
if (proc->prioLevel > GetNodeData(iterNode, Process_t *)->prioLevel) {
AddNodeBefore(head, iterNode, procNode);
found = true;
break;
}
}
//
// Didn't find any process with lesser priority
//
if (found == false) {
AppendNode(head, procNode);
}
}
//
// Add process to schedule lists
//
void SchedThisProc(Process_t *proc)
{
SchedLock();
SchedThisProcUnlocked(proc);
SchedUnlock();
}
//
// Selects process to schedule next
//
// WARNING
// Does not call SchedLock()/SchedUnlock()
//
static inline
Process_t *SelectSchedNext(void)
{
if (GetTimeCritProcs()->length > 0) return GetNodeData(GetTimeCritProcs()->first, Process_t *);
if (GetServPrioProcs()->length > 0) return GetNodeData(GetServPrioProcs()->first, Process_t *);
if (GetReglPrioProcs()->length > 0) return GetNodeData(GetReglPrioProcs()->first, Process_t *);
if (GetIdlePrioProcs()->length > 0) return GetNodeData(GetIdlePrioProcs()->first, Process_t *);
return NULL;
}
//
// Remove running process from schedule lists
// and schedule next runnable process
//
static inline
void BlockCurProc(void)
{
KalAssert(GetCurProc() && GetCurProc()->procState == STATE_RUNNING);
ListNode_t *procNode = GetCurProc()->schedNode;
GetCurProc()->procState = STATE_BLOCKED;
RemoveNode(procNode->head, procNode);
SetCurProc(SelectSchedNext());
}
//
// Should we schedule another process?
// Called at each tick
//
void SchedOnTick(void)
{
Process_t *procNext;
Process_t *winner;
SchedLock();
//
// We're either idle or running something
//
KalAssert(GetCurProc() == NULL || GetCurProc()->procState == STATE_RUNNING);
//
// Has current process spent his timeslice?
// (To be handled in CPU decisions function)
//
if (GetCurProc() != NULL) {
if (GetCurProc()->timeSlice <= 1) {
// Restore default attributes, cancelling boosts
GetCurProc()->prioClass = GetCurProc()->defPrioClass;
GetCurProc()->prioLevel = GetCurProc()->defPrioLevel;
GetCurProc()->timeSlice = GetCurProc()->defTimeSlice;
GetCurProc()->procState = STATE_RUNNABLE;
// Remove from list
RemoveNode(GetCurProc()->schedNode->head, GetCurProc()->schedNode);
// Schedule again, with default attributes now
SchedThisProcUnlocked(GetCurProc());
// Mark as idle
SetCurProc(NULL);
}
//
// Otherwise, make him lose a tick
//
else {
GetCurProc()->timeSlice--;
}
}
//
// Are we idle, or scheduling next process?
//
if (GetCurProc() == NULL) {
SetCurProc(SelectSchedNext());
goto leave;
}
//
// Is there a higher priority process that is runnable?
//
procNext = SelectSchedNext();
winner = CompareProcs(GetCurProc(), procNext);
//
// Yes, procNext should preempt current process
//
if (winner == procNext) {
GetCurProc()->procState = STATE_RUNNABLE;
SchedThisProcUnlocked(GetCurProc());
SetCurProc(procNext);
}
//
// Current process won't be preempted and has time remaining
//
leave:
SchedUnlock();
}
#define PrintProc(proc) printdbg("{ %d, '%s', %d , %d}\n", (proc)->pid, \
PrioClassesNames[(proc)->prioClass], (proc)->prioLevel, (proc)->timeSlice);
//
// Print out process list
//
void PrintList(ListHead_t *head)
{
KalAssert(head);
Process_t *proc;
ListNode_t *node = head->first;
printdbg("len: %d\n", head->length);
while (node) {
proc = GetNodeData(node, Process_t *);
PrintProc(proc);
node = node->next;
}
puts("");
}
//
// Initialize scheduler
//
void InitSched(void)
{
int pid;
Process_t *proc;
SchedLock();
_SetTimeCritProcs(CreateListHead());
_SetServPrioProcs(CreateListHead());
_SetReglPrioProcs(CreateListHead());
_SetIdlePrioProcs(CreateListHead());
#ifndef _KALEID_KERNEL
for (pid = 0; pid < procslen; pid++) {
if (procs[pid].procState == STATE_RUNNABLE) {
SchedThisProcUnlocked(&procs[pid]);
}
}
#endif
SchedUnlock();
}
//
// Shutdown scheduler
//
void FiniSched(void)
{
KalAssert(GetIdlePrioProcs() && GetReglPrioProcs() && GetServPrioProcs() && GetTimeCritProcs());
SchedLock();
while (GetIdlePrioProcs()->length > 0) RemoveNode(GetIdlePrioProcs(), GetIdlePrioProcs()->first);
while (GetReglPrioProcs()->length > 0) RemoveNode(GetReglPrioProcs(), GetReglPrioProcs()->first);
while (GetServPrioProcs()->length > 0) RemoveNode(GetServPrioProcs(), GetServPrioProcs()->first);
while (GetTimeCritProcs()->length > 0) RemoveNode(GetTimeCritProcs(), GetTimeCritProcs()->first);
DestroyListHead(GetIdlePrioProcs()); _SetIdlePrioProcs(NULL);
DestroyListHead(GetReglPrioProcs()); _SetReglPrioProcs(NULL);
DestroyListHead(GetServPrioProcs()); _SetServPrioProcs(NULL);
DestroyListHead(GetTimeCritProcs()); _SetTimeCritProcs(NULL);
SchedUnlock();
}
#ifndef _KALEID_KERNEL
int main(void)
{
InitSched();
puts("---------------");
puts("Time Critical:");
PrintList(GetTimeCritProcs());
puts("Server:");
PrintList(GetServPrioProcs());
puts("Regular:");
PrintList(GetReglPrioProcs());
puts("Idle:");
PrintList(GetIdlePrioProcs());
puts("---------------");
getchar();
int tick = 0;
while (tick < 20) {
printf("Tick %d - Running: ", tick);
if (GetCurProc() == NULL) {
puts("IDLE");
}
else {
PrintProc(GetCurProc());
}
if (tick == 9 || tick == 14) {
puts("Blocking current process");
BlockCurProc();
}
SchedOnTick();
//puts("\n---------------");
tick++;
}
FiniSched();
return 0;
}
#endif