mirror of
https://gitlab.os-k.eu/os-k-team/os-k.git
synced 2023-08-25 14:03:10 +02:00
427 lines
12 KiB
C
427 lines
12 KiB
C
//----------------------------------------------------------------------------//
|
|
// GNU GPL OS/K //
|
|
// //
|
|
// Desc: Process scheduler //
|
|
// //
|
|
// //
|
|
// Copyright © 2018-2019 The OS/K Team //
|
|
// //
|
|
// This file is part of OS/K. //
|
|
// //
|
|
// OS/K 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 //
|
|
// any later version. //
|
|
// //
|
|
// OS/K 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 OS/K. If not, see <https://www.gnu.org/licenses/>. //
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#include <extras/list.h>
|
|
#include <kernel/proc.h>
|
|
#include <kernel/sched.h>
|
|
|
|
#ifndef _KALEID_KERNEL
|
|
|
|
#include <stdio.h>
|
|
|
|
//
|
|
// For test purpose only
|
|
//
|
|
int procslen = 10;
|
|
Process_t procs[] = {
|
|
{ 0, 0, 0, 12, 12, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 1, 2, 2, 16, 16, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 2, 3, 3, 31, 31, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 3, 2, 2, 1, 1, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 4, 3, 3, 5, 5, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 5, 0, 0, 30, 30, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 6, 1, 1, 19, 19, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 7, 1, 1, 0, 0, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 8, 3, 3, 12, 12, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
{ 9, 2, 2, 21, 21, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL, NULL, NULL },
|
|
};
|
|
|
|
#endif
|
|
|
|
//
|
|
// Set current process
|
|
// TODO Select thread, context switch
|
|
//
|
|
static 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(TimeCritProcs, ListHead_t *);
|
|
CREATE_PER_CPU(ServPrioProcs, ListHead_t *);
|
|
CREATE_PER_CPU(ReglPrioProcs, ListHead_t *);
|
|
CREATE_PER_CPU(IdlePrioProcs, ListHead_t *);
|
|
|
|
const char *PrioClassesNames[] = {
|
|
"Time-critical class",
|
|
"Server priority class",
|
|
"Regular priority class",
|
|
"Idle priority class",
|
|
};
|
|
|
|
//
|
|
// Get priority class list head
|
|
//
|
|
static 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 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 void SchedThisProcUnlocked(Process_t *proc)
|
|
{
|
|
KalAssert(proc && proc->procState == STATE_RUNNABLE && !proc->schedNode);
|
|
|
|
bool found = false;
|
|
ListNode_t *iterNode = NULL;
|
|
ListNode_t *procNode = CreateNode(proc);
|
|
ListHead_t *head = GetPrioClassHead(proc->prioClass);
|
|
|
|
KalAssert(procNode && head);
|
|
|
|
proc->schedNode = procNode;
|
|
|
|
// Find a process with lesser priority
|
|
for (iterNode = head->first; iterNode; iterNode = iterNode->next) {
|
|
if (proc->prioLevel > GetNodeData(iterNode, Process_t *)->prioLevel) {
|
|
// Detect double insertions
|
|
KalAssert(proc->pid != GetNodeData(iterNode, Process_t *)->pid);
|
|
|
|
// Add process to schedule
|
|
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 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
|
|
//
|
|
void BlockCurProc(void)
|
|
{
|
|
KalAssert(GetCurProc() && GetCurProc()->procState == STATE_RUNNING);
|
|
|
|
ListNode_t *procNode = GetCurProc()->schedNode;
|
|
|
|
KalAssert(procNode && "Blocking non-scheduled process");
|
|
|
|
GetCurProc()->procState = STATE_BLOCKED;
|
|
RemoveNode(procNode->head, procNode);
|
|
|
|
GetCurProc()->schedNode = NULL;
|
|
SetCurProc(SelectSchedNext());
|
|
}
|
|
|
|
static void ReSchedCurProc(void)
|
|
{
|
|
KalAssert(GetCurProc() && GetCurProc()->procState == STATE_RUNNING);
|
|
KalAssert(GetCurProc()->schedNode);
|
|
|
|
// 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);
|
|
GetCurProc()->schedNode = NULL;
|
|
|
|
// Schedule again, with default attributes now
|
|
SchedThisProcUnlocked(GetCurProc());
|
|
}
|
|
|
|
//
|
|
// Should we schedule another process?
|
|
// Called at each tick
|
|
//
|
|
void SchedOnTick(void)
|
|
{
|
|
SchedLock();
|
|
|
|
Process_t *procNext, *winner, *previous = GetCurProc();
|
|
|
|
// We're either idle or running something
|
|
KalAssert(GetCurProc() == NULL || GetCurProc()->procState == STATE_RUNNING);
|
|
|
|
// Have the current process spent its timeslice?
|
|
// (To be handled in CPU decisions function)
|
|
if (GetCurProc() != NULL) {
|
|
if (GetCurProc()->timeSlice <= 1) {
|
|
// Re-schedule
|
|
ReSchedCurProc();
|
|
|
|
// See next 'if' statement
|
|
_SetCurProc(NULL);
|
|
}
|
|
|
|
// Otherwise, make it lose a tick
|
|
else {
|
|
GetCurProc()->timeSlice--;
|
|
}
|
|
}
|
|
|
|
// Are we idle, or scheduling next process?
|
|
if (GetCurProc() == NULL) {
|
|
SetCurProc(SelectSchedNext());
|
|
goto leave;
|
|
}
|
|
|
|
// Is preemption on and a re-schedule is needed?
|
|
if (GetPreemptCount() == PREEMPT_ON && GetReSchedFlag()) {
|
|
|
|
// Is there a higher priority process that is runnable?
|
|
procNext = SelectSchedNext();
|
|
winner = CompareProcs(GetCurProc(), procNext);
|
|
|
|
// Yes, procNext should preempt current process
|
|
if (winner == procNext) {
|
|
// Re-schedule
|
|
ReSchedCurProc();
|
|
|
|
// Switch to procNext
|
|
SetCurProc(procNext);
|
|
}
|
|
}
|
|
|
|
// Current process won't be preempted and has time remaining
|
|
leave:
|
|
SchedUnlock();
|
|
|
|
if (GetCurProc() != NULL && GetCurProc() != previous) {
|
|
// XXX context switch
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize scheduler
|
|
//
|
|
void InitSched(void)
|
|
{
|
|
int pid;
|
|
|
|
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
|
|
|
|
#define PrintProc(proc) printf("{ %d, '%s', %d , %lu}\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;
|
|
|
|
printf("len: %lu\n", head->length);
|
|
|
|
while (node) {
|
|
proc = GetNodeData(node, Process_t *);
|
|
|
|
PrintProc(proc);
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
puts("");
|
|
}
|
|
|
|
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 < 120) {
|
|
if (tick > 0 && tick != 50 && tick % 10 == 0) {
|
|
puts("Blocking current process");
|
|
BlockCurProc();
|
|
}
|
|
|
|
if (tick == 50) {
|
|
procs[0].procState = STATE_RUNNABLE;
|
|
SchedThisProc(&procs[0]);
|
|
}
|
|
|
|
printf("Tick %d - Running: ", tick);
|
|
|
|
if (GetCurProc() == NULL) {
|
|
puts("IDLE");
|
|
}
|
|
|
|
else {
|
|
PrintProc(GetCurProc());
|
|
}
|
|
|
|
SchedOnTick();
|
|
|
|
if (tick == 50) // already done
|
|
puts("Re-scheduling process 0");
|
|
|
|
tick++;
|
|
}
|
|
|
|
FiniSched();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|