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

422 lines
12 KiB
C
Raw Normal View History

2019-01-14 14:31:49 +01:00
//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
2019-02-16 23:36:33 +01:00
// Desc: Process scheduler //
2019-01-14 14:31:49 +01:00
// //
2019-02-16 23:36:33 +01:00
// //
// 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/>. //
2019-01-14 14:31:49 +01:00
//----------------------------------------------------------------------------//
2019-02-16 23:36:33 +01:00
#include <extras/list.h>
#include <kernel/proc.h>
#include <kernel/sched.h>
#include <kernel/iomisc.h>
2019-02-06 14:07:38 +01:00
2019-01-14 14:31:49 +01:00
//
// For test purpose only
//
2019-01-21 09:53:54 +01:00
int procslen = 10;
2019-01-14 14:31:49 +01:00
Process_t procs[] = {
2019-04-06 07:53:58 +02:00
{ 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, 3, 3, 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 },
{ 9, 2, 2, 21, 21, STATE_RUNNABLE, DEF_PROC_TSLICE, DEF_PROC_TSLICE, NULL },
2019-01-14 14:31:49 +01:00
};
2019-03-25 17:33:51 +01:00
//------------------------------------------//
2019-04-05 10:20:10 +02:00
#define ReSchedFlag (KeCurCPU->needReSched)
#define PreemptCount (KeCurCPU->preemptCount)
2019-03-25 17:33:51 +01:00
2019-04-05 10:20:10 +02:00
#define IdlePrioProcs (KeCurCPU->idlePrioProcs)
#define ReglPrioProcs (KeCurCPU->reglPrioProcs)
#define ServPrioProcs (KeCurCPU->servPrioProcs)
#define TimeCritProcs (KeCurCPU->timeCritProcs)
2019-03-25 17:33:51 +01:00
//------------------------------------------//
2019-01-14 14:31:49 +01:00
//
// Set current process
//
2019-02-16 23:36:33 +01:00
static void SetCurProc(Process_t *proc)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
PsCurProc = proc;
if (PsCurProc) PsCurProc->procState = STATE_RUNNING;
2019-01-14 14:31:49 +01:00
}
//
// (Un)Lock priority class list heads
//
static inline
2019-03-25 17:33:51 +01:00
void PsLockSched(void) {
KeDisableIRQs();
2019-01-14 14:31:49 +01:00
}
static inline
2019-03-25 17:33:51 +01:00
void PsUnlockSched(void) {
//KeEnableIRQs();
2019-01-14 14:31:49 +01:00
}
//
// The four priority classes of OS/2
//
2019-03-25 17:33:51 +01:00
const char *PsPrioClassesNames[] = {
2019-02-16 23:36:33 +01:00
"Time-critical class",
"Server priority class",
"Regular priority class",
"Idle priority class",
2019-01-14 14:31:49 +01:00
};
//
// Get priority class list head
//
2019-02-16 23:36:33 +01:00
static ListHead_t *GetPrioClassHead(int prioClass)
2019-01-14 14:31:49 +01:00
{
switch (prioClass) {
2019-03-25 17:33:51 +01:00
case TIME_CRIT_PROC: return TimeCritProcs;
case SERV_PRIO_PROC: return ServPrioProcs;
case REGL_PRIO_PROC: return ReglPrioProcs;
case IDLE_PRIO_PROC: return IdlePrioProcs;
2019-01-14 14:31:49 +01:00
default: KalAssert(FALSE && "Unknown priority class");
}
return NULL;
}
//
// Determine which process is going to run first
// Return NULL for "equal" processes
//
2019-02-16 23:36:33 +01:00
static Process_t *CompareProcs(Process_t *proc1, Process_t *proc2)
2019-01-14 14:31:49 +01:00
{
KalAssert(proc1 && proc2);
2019-01-21 09:53:54 +01:00
if (proc1->prioClass < proc2->prioClass) return proc1;
if (proc1->prioClass > proc2->prioClass) return proc2;
2019-01-14 14:31:49 +01:00
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)
//
2019-02-16 23:36:33 +01:00
static void SchedThisProcUnlocked(Process_t *proc)
2019-01-14 14:31:49 +01:00
{
2019-01-21 09:53:54 +01:00
KalAssert(proc && proc->procState == STATE_RUNNABLE && !proc->schedNode);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
bool found = 0;
2019-01-14 14:31:49 +01:00
ListNode_t *iterNode = NULL;
2019-03-25 17:33:51 +01:00
ListNode_t *procNode = ExCreateNode(proc);
2019-01-14 14:31:49 +01:00
ListHead_t *head = GetPrioClassHead(proc->prioClass);
2019-03-25 17:33:51 +01:00
KalAssert(head);
KalAssert(procNode);
2019-01-14 14:31:49 +01:00
proc->schedNode = procNode;
// Find a process with lesser priority
for (iterNode = head->first; iterNode; iterNode = iterNode->next) {
2019-03-25 17:33:51 +01:00
if (proc->prioLevel > ExGetNodeData(iterNode, Process_t *)->prioLevel) {
2019-01-21 09:53:54 +01:00
// Detect double insertions
2019-03-25 17:33:51 +01:00
KalAssert(proc->pid != ExGetNodeData(iterNode, Process_t *)->pid);
2019-01-21 09:53:54 +01:00
// Add process to schedule
2019-03-25 17:33:51 +01:00
ExAddNodeBefore(head, iterNode, procNode);
2019-01-14 14:31:49 +01:00
found = true;
break;
}
}
// Didn't find any process with lesser priority
if (found == false) {
2019-03-25 17:33:51 +01:00
ExAppendNode(head, procNode);
2019-01-14 14:31:49 +01:00
}
}
//
// Add process to schedule lists
//
2019-03-25 17:33:51 +01:00
void PsSchedThisProc(Process_t *proc)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
PsLockSched();
2019-01-14 14:31:49 +01:00
SchedThisProcUnlocked(proc);
2019-03-25 17:33:51 +01:00
PsUnlockSched();
2019-01-14 14:31:49 +01:00
}
//
// Selects process to schedule next
//
2019-02-16 23:36:33 +01:00
static Process_t *SelectSchedNext(void)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
if (TimeCritProcs->length > 0)
return ExGetNodeData(TimeCritProcs->first, Process_t *);
if (ServPrioProcs->length > 0)
return ExGetNodeData(ServPrioProcs->first, Process_t *);
if (ReglPrioProcs->length > 0)
return ExGetNodeData(ReglPrioProcs->first, Process_t *);
if (IdlePrioProcs->length > 0)
return ExGetNodeData(IdlePrioProcs->first, Process_t *);
2019-01-14 14:31:49 +01:00
return NULL;
}
//
// Remove running process from schedule lists
// and schedule next runnable process
//
2019-03-25 17:33:51 +01:00
void PsBlockCurProc(void)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
KalAssert(PsCurProc && PsCurProc->procState == STATE_RUNNING);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
ListNode_t *procNode = PsCurProc->schedNode;
2019-01-14 14:31:49 +01:00
2019-01-21 09:53:54 +01:00
KalAssert(procNode && "Blocking non-scheduled process");
2019-03-25 17:33:51 +01:00
PsCurProc->procState = STATE_BLOCKED;
ExRemoveNode(procNode->head, procNode);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
PsCurProc->schedNode = NULL;
2019-01-14 14:31:49 +01:00
SetCurProc(SelectSchedNext());
}
2019-02-16 23:36:33 +01:00
static void ReSchedCurProc(void)
2019-01-19 22:36:38 +01:00
{
2019-03-25 17:33:51 +01:00
KalAssert(PsCurProc && PsCurProc->procState == STATE_RUNNING);
KalAssert(PsCurProc->schedNode);
2019-01-19 22:36:38 +01:00
// Restore default attributes, cancelling boosts
2019-03-25 17:33:51 +01:00
PsCurProc->prioClass = PsCurProc->defPrioClass;
PsCurProc->prioLevel = PsCurProc->defPrioLevel;
PsCurProc->timeSlice = PsCurProc->defTimeSlice;
PsCurProc->procState = STATE_RUNNABLE;
2019-01-19 22:36:38 +01:00
// Remove from list
2019-03-25 17:33:51 +01:00
ExRemoveNode(PsCurProc->schedNode->head, PsCurProc->schedNode);
PsCurProc->schedNode = NULL;
2019-01-19 22:36:38 +01:00
// Schedule again, with default attributes now
2019-03-25 17:33:51 +01:00
SchedThisProcUnlocked(PsCurProc);
2019-01-19 22:36:38 +01:00
}
2019-01-14 14:31:49 +01:00
//
// Should we schedule another process?
// Called at each tick
//
2019-03-25 17:33:51 +01:00
void PsSchedOnTick(void)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
PsLockSched();
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
Process_t *procNext, *winner, *previous = PsCurProc;
2019-01-19 22:36:38 +01:00
2019-01-14 14:31:49 +01:00
// We're either idle or running something
2019-03-25 17:33:51 +01:00
KalAssert(PsCurProc == NULL || PsCurProc->procState == STATE_RUNNING);
2019-01-14 14:31:49 +01:00
2019-01-19 22:36:38 +01:00
// Have the current process spent its timeslice?
2019-01-14 14:31:49 +01:00
// (To be handled in CPU decisions function)
2019-03-25 17:33:51 +01:00
if (PsCurProc != NULL) {
if (PsCurProc->timeSlice <= 1) {
2019-01-19 22:36:38 +01:00
// Re-schedule
ReSchedCurProc();
2019-01-14 14:31:49 +01:00
2019-01-19 22:36:38 +01:00
// See next 'if' statement
2019-03-25 17:33:51 +01:00
PsCurProc = NULL;
2019-01-14 14:31:49 +01:00
}
2019-01-19 22:36:38 +01:00
// Otherwise, make it lose a tick
2019-01-14 14:31:49 +01:00
else {
2019-03-25 17:33:51 +01:00
PsCurProc->timeSlice--;
2019-01-14 14:31:49 +01:00
}
}
// Are we idle, or scheduling next process?
2019-03-25 17:33:51 +01:00
if (PsCurProc == NULL) {
2019-01-14 14:31:49 +01:00
SetCurProc(SelectSchedNext());
goto leave;
}
2019-02-06 14:07:38 +01:00
// Is preemption on and a re-schedule is needed?
2019-03-25 17:33:51 +01:00
if (PreemptCount == PREEMPT_ON && ReSchedFlag) {
2019-02-06 14:07:38 +01:00
// Is there a higher priority process that is runnable?
procNext = SelectSchedNext();
2019-03-25 17:33:51 +01:00
winner = CompareProcs(PsCurProc, procNext);
2019-02-06 14:07:38 +01:00
// Yes, procNext should preempt current process
if (winner == procNext) {
// Re-schedule
ReSchedCurProc();
// Switch to procNext
SetCurProc(procNext);
}
2019-01-14 14:31:49 +01:00
}
// Current process won't be preempted and has time remaining
leave:
2019-03-25 17:33:51 +01:00
PsUnlockSched();
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
if (PsCurProc != NULL && PsCurProc != previous) {
2019-04-13 09:27:36 +02:00
// dispatch & context switch
2019-01-14 14:31:49 +01:00
}
}
//
// Initialize scheduler
//
2019-03-25 17:33:51 +01:00
void PsInitSched(void)
2019-01-14 14:31:49 +01:00
{
int pid;
2019-03-25 17:33:51 +01:00
PsLockSched();
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
TimeCritProcs = ExCreateListHead();
ServPrioProcs = ExCreateListHead();
ReglPrioProcs = ExCreateListHead();
IdlePrioProcs = ExCreateListHead();
KalAssert(IdlePrioProcs && ReglPrioProcs && ServPrioProcs && TimeCritProcs);
2019-01-14 14:31:49 +01:00
for (pid = 0; pid < procslen; pid++) {
if (procs[pid].procState == STATE_RUNNABLE) {
SchedThisProcUnlocked(&procs[pid]);
}
}
2019-03-25 17:33:51 +01:00
PsUnlockSched();
2019-01-14 14:31:49 +01:00
}
//
2019-04-04 18:47:30 +02:00
// Shutdowns scheduler
2019-01-14 14:31:49 +01:00
//
2019-03-25 17:33:51 +01:00
void PsFiniSched(void)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
KalAssert(IdlePrioProcs && ReglPrioProcs && ServPrioProcs && TimeCritProcs);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
PsLockSched();
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
while (IdlePrioProcs->length > 0)
ExRemoveNode(IdlePrioProcs, IdlePrioProcs->first);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
while (ReglPrioProcs->length > 0)
ExRemoveNode(ReglPrioProcs, ReglPrioProcs->first);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
while (ServPrioProcs->length > 0)
ExRemoveNode(ServPrioProcs, ServPrioProcs->first);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
while (TimeCritProcs->length > 0)
ExRemoveNode(TimeCritProcs, TimeCritProcs->first);
2019-01-19 22:36:38 +01:00
2019-03-25 17:33:51 +01:00
ExDestroyListHead(IdlePrioProcs); IdlePrioProcs = NULL;
ExDestroyListHead(ReglPrioProcs); ReglPrioProcs = NULL;
ExDestroyListHead(ServPrioProcs); ServPrioProcs = NULL;
ExDestroyListHead(TimeCritProcs); TimeCritProcs = NULL;
2019-01-19 22:36:38 +01:00
2019-03-25 17:33:51 +01:00
PsUnlockSched();
}
#define PrintProc(proc) KernLog("{ %d, '%s', %d , %lu}\n", (proc)->pid, \
PsPrioClassesNames[(proc)->prioClass], (proc)->prioLevel, (proc)->timeSlice);
2019-01-19 22:36:38 +01:00
//
// Print out process list
//
void PrintList(ListHead_t *head)
{
KalAssert(head);
Process_t *proc;
ListNode_t *node = head->first;
2019-03-25 17:33:51 +01:00
KernLog("len: %lu\n", head->length);
2019-01-19 22:36:38 +01:00
while (node) {
2019-03-25 17:33:51 +01:00
proc = ExGetNodeData(node, Process_t *);
2019-01-19 22:36:38 +01:00
PrintProc(proc);
node = node->next;
}
2019-03-25 17:33:51 +01:00
KernLog("");
2019-01-19 22:36:38 +01:00
}
2019-03-25 17:33:51 +01:00
void pstest(void)
2019-01-14 14:31:49 +01:00
{
2019-03-25 17:33:51 +01:00
KernLog("\nTime Critical: ");
PrintList(TimeCritProcs);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
KernLog("\nServer: ");
PrintList(ServPrioProcs);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
KernLog("\nRegular: ");
PrintList(ReglPrioProcs);
2019-01-14 14:31:49 +01:00
KernLog("\nIdle: ");
2019-03-25 17:33:51 +01:00
PrintList(IdlePrioProcs);
2019-01-14 14:31:49 +01:00
int tick = 0;
2019-05-08 20:45:28 +02:00
KernLog("\n");
while (tick < 100) {
//if (tick%25==0)ClearTerm(StdOut);
2019-05-09 11:33:07 +02:00
if (tick > 0 && tick != 50 && tick % 10 == 0 && PsCurProc) {
2019-03-25 17:33:51 +01:00
KernLog("Blocking current process\n");
PsBlockCurProc();
2019-01-19 22:36:38 +01:00
}
if (tick == 50) {
2019-01-21 09:53:54 +01:00
procs[0].procState = STATE_RUNNABLE;
2019-03-25 17:33:51 +01:00
PsSchedThisProc(&procs[0]);
2019-01-19 22:36:38 +01:00
}
2019-03-25 17:33:51 +01:00
KernLog("Tick %d - Running: ", tick);
2019-01-14 14:31:49 +01:00
2019-03-25 17:33:51 +01:00
if (PsCurProc == NULL) {
2019-04-13 09:27:36 +02:00
KernLog("IDLE\n");
2019-01-14 14:31:49 +01:00
}
else {
2019-03-25 17:33:51 +01:00
PrintProc(PsCurProc);
2019-01-14 14:31:49 +01:00
}
2019-03-25 17:33:51 +01:00
PsSchedOnTick();
2019-01-14 14:31:49 +01:00
2019-01-21 09:53:54 +01:00
if (tick == 50) // already done
2019-04-13 09:27:36 +02:00
KernLog("Re-scheduling process 0\n");
2019-01-21 09:53:54 +01:00
2019-01-14 14:31:49 +01:00
tick++;
}
}
2019-01-19 22:36:38 +01:00