My Project
Loading...
Searching...
No Matches
scheduler.c
Go to the documentation of this file.
1/*
2 * PROJECT: MatanelOS Kernel
3 * LICENSE: GPLv3
4 * PURPOSE: Scheduler Implementation.
5 */
6
7#include "../../includes/me.h"
8#include "../../assert.h"
9#include "../../includes/ps.h"
10#include "../../includes/mg.h"
11#include "../../includes/ob.h"
12extern PROCESSOR cpus[];
13
14// assembly stubs to save and restore register contexts.
15extern void restore_context(TRAP_FRAME* regs);
16extern void restore_user_context(PETHREAD thread);
17
18// Idle thread, runs when no other is ready.
19// Stack for idle thread
20extern void kernel_idle_checks(void);
21#define IDLE_STACK_SIZE 4096
22
24
25// In Scheduler.c
26void InitScheduler(void) {
30
31 TRAP_FRAME cfm;
32 kmemset(&cfm, 0, sizeof(cfm)); // Start with a clean, all-zero context
33
34 // Set only the essential registers for starting the thread
35 void* idleStack = MiCreateKernelStack(false);
36 assert(idleStack != NULL);
37 cfm.rsp = (uint64_t)idleStack;
38 cfm.rip = (uint64_t)kernel_idle_checks;
39
40 // Enable Interrupts on its RFLAGS.
41 cfm.rflags |= (1 << 9ULL);
42
43 // Assign the clean context to the idle thread
44 idleThread->InternalThread.TrapRegisters = cfm;
46 idleThread->InternalThread.TimeSlice = 1; // 1ms
47 idleThread->InternalThread.TimeSliceAllocated = 1;
49 idleThread->TID = 0; // Idle thread, TID is 0.
50 idleThread->InternalThread.StackBase = (void*)cfm.rsp;
51 idleThread->InternalThread.IsLargeStack = false;
52 MeGetCurrentProcessor()->currentThread = NULL; // The idle thread would be chosen
53 idleThread->CurrentEvent = NULL; // No event.
55 PsInitialSystemProcess.MainThread = idleThread;
56 InsertHeadList(&PsInitialSystemProcess.AllThreads, &idleThread->ThreadListEntry);
57
58 // The ready queue starts empty
60}
61
62// Enqueue the thread if it's still RUNNING.
63static void enqueue_runnable(PITHREAD t) {
64 assert((t) != 0);
65 if (t->ThreadState == THREAD_RUNNING) {
68 MeEnqueueThreadWithLock(&MeGetCurrentProcessor()->readyQueue, PsGetEThreadFromIThread(t)); // Insert into CPU ready queue
69 }
70}
71
72extern uint32_t g_cpuCount; // extern the global cpu count. (gotten from smp)
73extern bool smpInitialized;
74
75// The following function uses CPU Work stealing to steal other CPUs thread (in a queue), if the current thread has no scheduled threads in the queue.
76static PITHREAD MeAcquireNextScheduledThread(void) {
77 // First, lets try to get from our own queue.
78 PETHREAD chosenThread = MeDequeueThreadWithLock(&MeGetCurrentProcessor()->readyQueue);
79 if (chosenThread) return &chosenThread->InternalThread;
80
81#ifndef MT_UP
82 if (smpInitialized) {
83 // Our own CPU queue is empty, steal from others.
84 for (uint32_t i = 0; i < g_cpuCount; i++) {
85 if (cpus[i].lapic_ID == MeGetCurrentProcessor()->lapic_ID) continue; // skip ourselves.
86
87 // The reason I used the self pointer here, is because the BSP in the cpus array, is empty except for 4 fields, as its main struct is cpu0,
88 // which is defined at the kernel main, so we access it through self, view SMP.C prepare_percpu for more info.
89 Queue* victimQueue = &cpus[i].self->readyQueue;
90 if (!victimQueue->head) continue; // skip empty queues
91
92 chosenThread = MeDequeueThreadWithLock(victimQueue);
93 // Found a suitable thread, return it.
94 if (chosenThread) return &chosenThread->InternalThread;
95 }
96 }
97#endif
98
99 // No thread found.
100 return NULL;
101}
102
104void
105Schedule(void) {
106 //gop_printf(COLOR_PURPLE, "**In scheduler, IRQL: %d**\n", MeGetCurrentIrql());
107 IRQL oldIrql;
108 MeRaiseIrql(DISPATCH_LEVEL, &oldIrql); // Prevents scheduling re-entrance.
109
113
114 // Check if we need to delete another thread's (safe now, we are at a separate stack)
115 if (cpu->ZombieThread) {
116 // Drop the reference, we are on another thread's stack.
118 cpu->ZombieThread = NULL;
119 }
120
121 // All thread's that weren't RUNNING are ignored by the Scheduler. (like BLOCKED threads when waiting or an event, ZOMBIE threads, TERMINATED, etc..)
122 if (prev && prev != IdleThread && prev->ThreadState == THREAD_TERMINATING) {
123 cpu->ZombieThread = prev;
124 prev = NULL;
125 }
126 else if (prev && prev != IdleThread && prev->ThreadState == THREAD_RUNNING) {
127 // The current thread's registers were already saved in isr_stub. (look after the pushes) (also saved in MtSleepCurrentThread)
128 enqueue_runnable(prev);
129 }
130
131 PITHREAD next = MeAcquireNextScheduledThread();
132
133 if (!next) {
134 next = IdleThread;
135 }
136
139 MeLowerIrql(oldIrql);
142 }
143 else {
144 // User thread
146 }
147 __builtin_unreachable();
148}
#define NORETURN
Definition annotations.h:13
#define assert(...)
Definition assert.h:57
bool smpInitialized
Definition kernel.c:146
struct _EPROCESS EPROCESS
Definition core.h:49
struct _TRAP_FRAME TRAP_FRAME
Definition core.h:53
struct _PROCESSOR PROCESSOR
Definition core.h:45
@ DISPATCH_LEVEL
Definition core.h:15
PROCESSOR * PPROCESSOR
Definition core.h:46
enum _IRQL IRQL
ITHREAD * PITHREAD
Definition core.h:34
struct _ETHREAD ETHREAD
Definition core.h:41
ETHREAD * PETHREAD
Definition core.h:42
void MeRaiseIrql(IN IRQL NewIrql, OUT PIRQL OldIrql)
Definition irql.c:57
void MeLowerIrql(IN IRQL NewIrql)
Definition irql.c:97
FORCEINLINE PPROCESSOR MeGetCurrentProcessor(void)
Definition me.h:356
@ NonPagedPool
Definition mm.h:316
FORCEINLINE void * kmemset(void *dest, int64_t val, uint64_t len)
Definition mm.h:540
void * MiCreateKernelStack(IN bool LargeStack)
Definition mmproc.c:25
FORCEINLINE void InitializeListHead(PDOUBLY_LINKED_LIST Head)
Definition ms.h:166
struct _Queue Queue
FORCEINLINE void InsertHeadList(PDOUBLY_LINKED_LIST Head, PDOUBLY_LINKED_LIST Entry)
Definition ms.h:196
void ObDereferenceObject(IN void *Object)
Definition ob.c:446
void * MmAllocatePoolWithTag(IN enum _POOL_TYPE PoolType, IN size_t NumberOfBytes, IN uint32_t Tag)
Definition pool.c:427
FORCEINLINE void MeEnqueueThreadWithLock(Queue *queue, PETHREAD thread)
Definition ps.h:306
FORCEINLINE PETHREAD PsGetEThreadFromIThread(IN PITHREAD IThread)
Definition ps.h:239
FORCEINLINE PETHREAD MeDequeueThreadWithLock(Queue *q)
Definition ps.h:336
@ THREAD_TERMINATING
Definition ps.h:41
@ THREAD_RUNNING
Definition ps.h:38
@ THREAD_READY
Definition ps.h:39
FORCEINLINE bool PsIsKernelThread(IN PETHREAD Thread)
Definition ps.h:259
NORETURN void Schedule(void)
Definition scheduler.c:105
void restore_context(TRAP_FRAME *regs)
void kernel_idle_checks(void)
Definition kernel.c:100
PROCESSOR cpus[]
Definition smp.c:16
void restore_user_context(PETHREAD thread)
EPROCESS PsInitialSystemProcess
Definition kernel.c:162
void InitScheduler(void)
Definition scheduler.c:26
uint32_t g_cpuCount
Definition smp.c:128
struct _EPROCESS * ParentProcess
Definition ps.h:127
struct _ITHREAD InternalThread
Definition ps.h:122
HANDLE TID
Definition ps.h:125
struct _EVENT * CurrentEvent
Definition ps.h:126
struct _DOUBLY_LINKED_LIST ThreadListEntry
Definition ps.h:128
uint32_t ThreadState
Definition me.h:263
void * StackBase
Definition me.h:264
bool IsLargeStack
Definition me.h:265
enum _TimeSliceTicks TimeSlice
Definition me.h:267
struct _TRAP_FRAME TrapRegisters
Definition me.h:262
enum _TimeSliceTicks TimeSliceAllocated
Definition me.h:268
struct _Queue readyQueue
Definition me.h:279
volatile bool schedulerEnabled
Definition me.h:277
PITHREAD ZombieThread
Definition me.h:332
struct _ETHREAD * idleThread
Definition me.h:291
struct _ITHREAD * currentThread
Definition me.h:278
PETHREAD tail
Definition ms.h:53
PETHREAD head
Definition ms.h:52
uint64_t rsp
Definition me.h:160
uint64_t rip
Definition me.h:157
uint64_t rflags
Definition me.h:159