kernel
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);
18
19// Idle thread, runs when no other is ready.
20// Stack for idle thread
21extern void kernel_idle_checks(void);
22#define IDLE_STACK_SIZE 4096
23
25
26// In Scheduler.c
27void InitScheduler(void) {
31
32 TRAP_FRAME cfm;
33 kmemset(&cfm, 0, sizeof(cfm)); // Start with a clean, all-zero context
34
35 // Set only the essential registers for starting the thread
36 void* idleStack = MiCreateKernelStack(false);
37 assert(idleStack != NULL);
38 cfm.rsp = (uint64_t)idleStack;
39 cfm.rip = (uint64_t)kernel_idle_checks;
40
41 // Enable Interrupts on its RFLAGS.
42 cfm.rflags |= (1 << 9ULL);
43
44 // Assign the clean context to the idle thread
45 idleThread->InternalThread.TrapRegisters = cfm;
47 idleThread->InternalThread.TimeSlice = 1; // 1ms
48 idleThread->InternalThread.TimeSliceAllocated = 1;
50 idleThread->TID = 0; // Idle thread, TID is 0.
51 idleThread->InternalThread.StackBase = (void*)cfm.rsp;
52 idleThread->InternalThread.IsLargeStack = false;
53 idleThread->InternalThread.KernelStack = idleStack;
54 MeGetCurrentProcessor()->currentThread = NULL; // The idle thread would be chosen
55 idleThread->CurrentEvent = NULL; // No event.
57 idleThread->SystemThread = true;
58 PsInitialSystemProcess.MainThread = idleThread;
60 InsertHeadList(&PsInitialSystemProcess.AllThreads, &idleThread->ThreadListEntry);
62
63 // The ready queue starts empty
65}
66
67// Enqueue the thread if it's still RUNNING.
68static void enqueue_runnable(PITHREAD t) {
69 assert((t) != 0);
70 if (t->ThreadState == THREAD_RUNNING) {
73 MeEnqueueThreadWithLock(&MeGetCurrentProcessor()->readyQueue, PsGetEThreadFromIThread(t)); // Insert into CPU ready queue
74 }
75}
76
77extern uint32_t g_cpuCount; // extern the global cpu count. (gotten from smp)
78extern bool smpInitialized;
79
80// 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.
81static PITHREAD MeAcquireNextScheduledThread(void) {
82 // First, lets try to get from our own queue.
83 PETHREAD chosenThread = MeDequeueThreadWithLock(&MeGetCurrentProcessor()->readyQueue);
84 if (chosenThread) return &chosenThread->InternalThread;
85
86#ifndef MT_UP
87 if (smpInitialized) {
88 // Our own CPU queue is empty, steal from others.
89 for (uint32_t i = 0; i < g_cpuCount; i++) {
90 if (cpus[i].lapic_ID == MeGetCurrentProcessor()->lapic_ID) continue; // skip ourselves.
91
92 // 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,
93 // which is defined at the kernel main, so we access it through self, view SMP.C prepare_percpu for more info.
94 Queue* victimQueue = &cpus[i].self->readyQueue;
95 if (!victimQueue->head) continue; // skip empty queues
96
97 chosenThread = MeDequeueThreadWithLock(victimQueue);
98 // Found a suitable thread, return it.
99 if (chosenThread) return &chosenThread->InternalThread;
100 }
101 }
102#endif
103
104 // No thread found.
105 return NULL;
106}
107
109void
110Schedule(void) {
111 //gop_printf(COLOR_PURPLE, "**In scheduler, IRQL: %d**\n", MeGetCurrentIrql());
112 IRQL oldIrql;
113 MeRaiseIrql(DISPATCH_LEVEL, &oldIrql); // Prevents scheduling re-entrance.
114
118
119 // Check if we need to delete another thread's (safe now, we are at a separate stack)
120 if (cpu->ZombieThread) {
121 // Drop the reference, we are on another thread's stack.
123 cpu->ZombieThread = NULL;
124 }
125
126 // All thread's that weren't RUNNING are ignored by the Scheduler. (like BLOCKED threads when waiting or an event, ZOMBIE threads, TERMINATED, etc..)
127 if (prev && prev != IdleThread && prev->ThreadState == THREAD_TERMINATING) {
128 cpu->ZombieThread = prev;
129 prev = NULL;
130 }
131 else if (prev && prev != IdleThread && prev->ThreadState == THREAD_RUNNING) {
132 // The current thread's registers were already saved in isr_stub. (look after the pushes) (also saved in MtSleepCurrentThread)
133 enqueue_runnable(prev);
134 }
135
136 PITHREAD next = MeAcquireNextScheduledThread();
137
138 if (!next) {
139 next = IdleThread;
140 }
141
144
145 // Disable interrupts, we must not scheduled away now.
147
148 // Lower IRQL back to its original value.
149 MeLowerIrql(oldIrql);
150
151 // Hi matanel, if you ever encounter failures here, like if it goes to restore_user_context as a system thread
152 // please check that you made the same changed to InitScheduler as you made in PsCreateSystemThread, for example, Thread->SystemThread was false in the idle thread, because I forgot to set
153 // that flag in its initilization, even though I was sure its on (for normal threads that is), because in PsCreateSystemThreads it was indeed = true.
156 }
157 else {
158 // User thread - Check if we should execute swapgs, because if we will execute it when we return to kernel RIP (like in a syscall for example), then GS would point to user mode.
159 // Check RIP, if its in kernel then WE DO NOT swap.
160 // This works ONLY when there is a CLI call before doing swapgs, since we could prepare to return to user mode, and then we return with an opposite GS.
161 // ACTUALLY DO NOT create a trap frame GS, (only the offset), this should be handled carefully
162 // I do not know what the fuck do i do..
163
164 // Note that this uses MmSystemRangeStart which is PhysicalMemoryOffset (which is the start of the kernel space in the 64bit addr space)
165 // It's fine. (no need to use KernelVaStart)
166 if (next->TrapRegisters.rip >= MmSystemRangeStart) {
168 }
169 else {
171 }
172 }
173 __builtin_unreachable();
174}
#define NORETURN
Definition annotations.h:14
#define assert(...)
Definition assert.h:57
bool smpInitialized
Definition kernel.c:149
struct _EPROCESS EPROCESS
Definition core.h:51
struct _TRAP_FRAME TRAP_FRAME
Definition core.h:55
struct _PROCESSOR PROCESSOR
Definition core.h:47
@ DISPATCH_LEVEL
Definition core.h:17
PROCESSOR * PPROCESSOR
Definition core.h:48
enum _IRQL IRQL
ITHREAD * PITHREAD
Definition core.h:36
struct _ETHREAD ETHREAD
Definition core.h:43
ETHREAD * PETHREAD
Definition core.h:44
bool MeDisableInterrupts(void)
Definition irql.c:203
void MeRaiseIrql(IN IRQL NewIrql, OUT PIRQL OldIrql)
Definition irql.c:62
void MeLowerIrql(IN IRQL NewIrql)
Definition irql.c:102
FORCEINLINE PPROCESSOR MeGetCurrentProcessor(void)
Definition me.h:369
@ NonPagedPool
Definition mm.h:355
FORCEINLINE void * kmemset(void *dest, int64_t val, uint64_t len)
Definition mm.h:655
void * MiCreateKernelStack(IN bool LargeStack)
Definition mmproc.c:26
FORCEINLINE void InitializeListHead(PDOUBLY_LINKED_LIST Head)
Definition ms.h:223
struct _Queue Queue
FORCEINLINE void InsertHeadList(PDOUBLY_LINKED_LIST Head, PDOUBLY_LINKED_LIST Entry)
Definition ms.h:253
void ObDereferenceObject(IN void *Object)
Definition ob.c:554
void * MmAllocatePoolWithTag(IN enum _POOL_TYPE PoolType, IN size_t NumberOfBytes, IN uint32_t Tag)
Definition pool.c:443
uintptr_t MmSystemRangeStart
Definition process.c:31
FORCEINLINE void MeEnqueueThreadWithLock(Queue *queue, PETHREAD thread)
Definition ps.h:385
FORCEINLINE PETHREAD PsGetEThreadFromIThread(IN PITHREAD IThread)
Definition ps.h:315
FORCEINLINE PETHREAD MeDequeueThreadWithLock(Queue *q)
Definition ps.h:415
@ 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:335
void MsAcquirePushLockExclusive(IN PUSH_LOCK *Lock)
Definition pushlock.c:80
void MsReleasePushLockExclusive(IN PUSH_LOCK *Lock)
Definition pushlock.c:99
NORETURN void Schedule(void)
Definition scheduler.c:110
void restore_context(TRAP_FRAME *regs)
void restore_user_context_withoutswapgs(PETHREAD thread)
void kernel_idle_checks(void)
Definition kernel.c:100
PROCESSOR cpus[]
Definition smp.c:16
EPROCESS PsInitialSystemProcess
Definition kernel.c:165
void restore_user_context_withswapgs(PETHREAD thread)
void InitScheduler(void)
Definition scheduler.c:27
uint32_t g_cpuCount
Definition smp.c:128
struct _EPROCESS * ParentProcess
Definition ps.h:190
struct _ITHREAD InternalThread
Definition ps.h:183
HANDLE TID
Definition ps.h:187
bool SystemThread
Definition ps.h:200
struct _EVENT * CurrentEvent
Definition ps.h:189
struct _DOUBLY_LINKED_LIST ThreadListEntry
Definition ps.h:191
uint32_t ThreadState
Definition me.h:268
void * StackBase
Definition me.h:269
bool IsLargeStack
Definition me.h:270
enum _TimeSliceTicks TimeSlice
Definition me.h:272
void * KernelStack
Definition me.h:271
struct _TRAP_FRAME TrapRegisters
Definition me.h:267
enum _TimeSliceTicks TimeSliceAllocated
Definition me.h:273
struct _Queue readyQueue
Definition me.h:286
volatile bool schedulerEnabled
Definition me.h:284
PITHREAD ZombieThread
Definition me.h:341
struct _ETHREAD * idleThread
Definition me.h:298
struct _ITHREAD * currentThread
Definition me.h:285
PETHREAD tail
Definition ms.h:54
PETHREAD head
Definition ms.h:53
uint64_t rsp
Definition me.h:163
uint64_t rip
Definition me.h:160
uint64_t rflags
Definition me.h:162