Voxia OS v0.0.1
Hobby Project Operating System Targeting x86-64
Loading...
Searching...
No Matches
scheduler.c
Go to the documentation of this file.
1#include "./scheduler.h"
2
3#include "hal/acpi/hpet.h"
4#include "hal/apic/apic.h"
5#include "hal/cpu/msr.h"
6#include "hal/cpu/paging.h"
7#include "hal/cpu/register.h"
8#include "init/init.h"
9#include "libk/serial.h"
10#include "memory/slab.h"
11#include "procc/thread.h"
12#include "type.h"
13#include <hal/cpu/core.h>
14#include <str.h>
15
16static struct slab_cache* scheduler_cache = nullptr;
17static scheduler_core_t scheduler[VOXIA_MAX_CORE] = {0};
18extern each_core_data core_data[VOXIA_MAX_CORE];
20
22
23INIT(Scheduler) {
25 sizeof(scheduler_queue_t), 0, 0);
26 LOG_INFO("Scheduler", "scheduler cache at 0x%x", scheduler_cache);
27}
28
32
34 bool already_locked) {
35 const uint16_t core_id = current->thread->core_affinity;
36
37 if (!already_locked)
38 spin_acquire(&scheduler[core_id].lock);
39
40 if (current->next_queue == current || current->next_queue == nullptr) {
41 scheduler[core_id].run_queue_head = nullptr;
42 scheduler[core_id].last = nullptr;
43 scheduler[core_id].current = nullptr;
44 } else {
45 if (current->prev_queue)
46 current->prev_queue->next_queue = current->next_queue;
47 if (current->next_queue)
48 current->next_queue->prev_queue = current->prev_queue;
49
50 if (scheduler[core_id].run_queue_head == current)
51 scheduler[core_id].run_queue_head = current->next_queue;
52 if (scheduler[core_id].last == current)
53 scheduler[core_id].last = current->prev_queue;
54 if (scheduler[core_id].current == current)
55 scheduler[core_id].current = current->next_queue;
56 }
57
58 current->next_queue = nullptr;
59 current->prev_queue = nullptr;
60
61 if (!already_locked)
62 spin_release(&scheduler[core_id].lock);
63}
64
66 cpu_register_t* reg) {
67 reg->rip = stack->rip;
68 reg->cs = stack->cs;
69 reg->rflags = stack->rflags;
70 reg->rsp = stack->rsp;
71 reg->ss = stack->ss;
72 reg->rax = stack->rax;
73 reg->rbx = stack->rbx;
74 reg->rcx = stack->rcx;
75 reg->rdx = stack->rdx;
76 reg->rbp = stack->rbp;
77 reg->rsi = stack->rsi;
78 reg->rdi = stack->rdi;
79 reg->r8 = stack->r8;
80 reg->r9 = stack->r9;
81 reg->r10 = stack->r10;
82 reg->r11 = stack->r11;
83 reg->r12 = stack->r12;
84 reg->r13 = stack->r13;
85 reg->r14 = stack->r14;
86 reg->r15 = stack->r15;
87}
88
90 cpu_register_t* reg) {
91 stack->rip = reg->rip;
92 stack->cs = reg->cs;
93 stack->rflags = reg->rflags;
94 stack->rsp = reg->rsp;
95 stack->ss = reg->ss;
96 stack->rax = reg->rax;
97 stack->rbx = reg->rbx;
98 stack->rcx = reg->rcx;
99 stack->rdx = reg->rdx;
100 stack->rbp = reg->rbp;
101 stack->rsi = reg->rsi;
102 stack->rdi = reg->rdi;
103 stack->r8 = reg->r8;
104 stack->r9 = reg->r9;
105 stack->r10 = reg->r10;
106 stack->r11 = reg->r11;
107 stack->r12 = reg->r12;
108 stack->r13 = reg->r13;
109 stack->r14 = reg->r14;
110 stack->r15 = reg->r15;
111}
112
114 auto current_core = get_current_core_data();
115 if (!current_core)
116 return;
117
118 const uint16_t core_id = current_core->core_id;
119
120 spin_acquire(&scheduler[core_id].lock);
121
122 if (!scheduler[core_id].run_queue_head) {
123 spin_release(&scheduler[core_id].lock);
124 return;
125 }
126
127 if (!scheduler[core_id].current)
128 scheduler[core_id].current = scheduler[core_id].run_queue_head;
129
130 scheduler_queue_t* current_node = scheduler[core_id].current;
131 if (!current_node || !current_node->thread) {
132 spin_release(&scheduler[core_id].lock);
133 return;
134 }
135
136 thread_t* thread = current_node->thread;
137 uint64_t current_tick = vxHPETGetMainCount();
138 bool needs_context_switch = false;
139
141 thread->last_run_time = current_tick;
143 }
144
145 // save previous state
148
149 if (ns2ms(current_tick - thread->last_run_time) >
150 VOXIA_MAX_SCHEDULER_TIME_MS) {
151 needs_context_switch = true;
153 }
154 } else if (thread->state == THREAD_STATE_TERMINATED) {
155 LOG2_DEBUG("SCHEDULER", "core %d terminated thread id %d",
156 core_id, thread->id);
158
159 vxDeatachFromScheduler(current_node, true);
160 needs_context_switch = true;
161
162 if (!scheduler[core_id].run_queue_head) {
163 spin_release(&scheduler[core_id].lock);
164 return;
165 }
166 }
167
168 // find next thread candidate
169 scheduler_queue_t* next_node = current_node;
170
171 if (needs_context_switch) {
172 next_node = current_node->next_queue;
173 // Fallback safety
174 if (!next_node)
175 next_node = scheduler[core_id].run_queue_head;
176
177 scheduler[core_id].current = next_node;
178 }
179
180 thread_t* next_thread = next_node->thread;
181
182 // load state
183 if (next_thread->state == THREAD_STATE_READY) {
184 next_thread->state = THREAD_STATE_RUNNING;
185
186 if (next_thread->flags & THREAD_USER) {
187 reg->rip = next_thread->entry_addr;
188 reg->rsp = next_thread->stack;
189 reg->cs = 0x48 | 3; /* user code segment (ring 3) */
190 reg->ss = 0x40 | 3; /* user stack segment (ring 3) */
191 reg->rflags = 0x202;
192 reg->rbp = 0;
193 LOG2_DEBUG("SCHEDULER",
194 "core %d ready: user mode rip=0x%x", core_id,
195 next_thread->entry_addr);
196 } else {
197 reg->rip = next_thread->entry_addr;
198 reg->rsp =
199 ((next_thread->stack + 0x1000) & ~(uint64_t)0xF) -
200 8;
201 reg->cs = 0x28; /* kernel code segment */
202 reg->ss = 0x30; /* kernel stack segment */
203 reg->rflags = 0x202;
204 reg->rbp = 0;
205 LOG2_DEBUG("SCHEDULER",
206 "core %d ready: kernel mode rip=0x%x",
207 core_id, next_thread->entry_addr);
208 }
209 } else if (next_thread->state == THREAD_STATE_RUNNING) {
210 if (needs_context_switch && next_thread != thread) {
211 vxRestoreRegister(reg, &next_thread->reg);
212 if (next_thread->fs_base)
213 msrSetFSBase(next_thread->fs_base);
214 }
215 }
216
217 // update metadata
218 next_thread->current_core_id = core_id;
219 current_core->active_thread = next_thread;
220 current_core->next_is_user = (next_thread->flags & THREAD_USER) ? 1 : 0;
221
222 if (needs_context_switch || !next_thread->has_update_run_time) {
223 next_thread->last_run_time = vxHPETGetMainCount();
224 next_thread->has_update_run_time = true;
225 }
226
227 volatile uintptr_t* reload_page = next_thread->page;
228
229 spin_release(&scheduler[core_id].lock);
230 if (reload_page) {
231 paging_reload(reload_page);
232 }
233}
234
237
240
241 if (!queue) {
243 return nullptr;
244 }
245
246 memset(queue, 0, sizeof(scheduler_queue_t));
247
248 if (!scheduler[core].run_queue_head) {
249 queue->next_queue = queue;
250 queue->prev_queue = queue;
251 scheduler[core].run_queue_head = queue;
252 scheduler[core].last = queue;
253 } else {
254 scheduler_queue_t* head = scheduler[core].run_queue_head;
255 scheduler_queue_t* last = scheduler[core].last;
256
257 last->next_queue = queue;
258 queue->prev_queue = last;
259 queue->next_queue = head;
260 head->prev_queue = queue;
261 scheduler[core].last = queue;
262 }
263
265 return queue;
266}
267
268void attach_to_scheduler(thread_t* new_thread) {
269 if (!new_thread)
270 return;
271
272 // TODO: lock
273
274 uint16_t core = 1;
275 if (new_thread->core_affinity != (uint16_t)-1)
276 core = new_thread->core_affinity;
277
279 if (!queue) {
280 LOG2_ERROR("SCHED", "error allocate scheduler for thread %d",
281 new_thread->id);
282 return;
283 }
284
285 LOG2_DEBUG("SCHED", "ATTACH thread id %d -> queue 0x%x", new_thread->id,
286 queue);
287 new_thread->state = THREAD_STATE_READY;
288 queue->thread = new_thread;
289}
290
291#define APIC_TIMER_MASKED (1 << 16)
292
294 const uint8_t core_id = get_current_core_cpuid();
295
296 serial_printf("scheduler init on core %d 0x%x\n", core_id,
298
299 irq_register(core_id, 0x45, (void*)vxSchedulerTick, true, 0x28, 0,
301
303}
304
306 spin_acquire(&scheduler[core_id].lock);
307
308 scheduler_queue_t* current_node = scheduler[core_id].current;
309 if (!current_node || !current_node->thread) {
310 spin_release(&scheduler[core_id].lock);
311 return;
312 }
313
314 thread_t* crashed = current_node->thread;
315 crashed->state = THREAD_STATE_HAL;
316
317 // Detach dari queue (already_locked = true)
318 vxDeatachFromScheduler(current_node, true);
319
320 // Tidak ada thread lain → tidak bisa recover
321 if (!scheduler[core_id].run_queue_head) {
322 spin_release(&scheduler[core_id].lock);
323 INFLOOP;
324 }
325
326 // Ambil next thread yang sudah valid
327 scheduler_queue_t* next_node = scheduler[core_id].current;
328 if (!next_node)
329 next_node = scheduler[core_id].run_queue_head;
330
331 thread_t* next = next_node->thread;
332
333 // Restore FULL register context — ini yang sebelumnya hilang
334 if (next->state == THREAD_STATE_RUNNING) {
335 vxRestoreRegister(rsp, &next->reg);
336 if (next->fs_base)
337 msrSetFSBase(next->fs_base);
338 } else if (next->state == THREAD_STATE_READY) {
339 // Thread belum pernah jalan, set up fresh context
340 next->state = THREAD_STATE_RUNNING;
341 if (next->flags & THREAD_USER) {
342 rsp->rip = next->entry_addr;
343 rsp->rsp = next->stack;
344 rsp->cs = 0x48 | 3;
345 rsp->ss = 0x40 | 3;
346 rsp->rflags = 0x202;
347 rsp->rbp = 0;
348 } else {
349 rsp->rip = next->entry_addr;
350 rsp->rsp = ((next->stack + 0x1000) & ~(uint64_t)0xF) - 8;
351 rsp->cs = 0x28;
352 rsp->ss = 0x30;
353 rsp->rflags = 0x202;
354 rsp->rbp = 0;
355 }
356 }
357
358 auto current_core = get_current_core_data();
359 next->current_core_id = core_id;
360 current_core->active_thread = next;
361 current_core->next_is_user = (next->flags & THREAD_USER) ? 1 : 0;
362
363 scheduler[core_id].current = next_node;
364
365 volatile uintptr_t* reload_page = next->page;
366 spin_release(&scheduler[core_id].lock);
367
368 // Switch page map ke milik next thread — WAJIB jika beda process
369 if (reload_page)
370 paging_reload(reload_page);
371}
#define APIC_TIMER_PERIOD
Definition apic.h:21
void vxAPICCreateTimer(uint32_t type, uint64_t freq_us, uint16_t vector)
Definition apic_timer.c:162
uint8_t lock
Definition cache.h:1
each_core_data * get_current_core_data(void)
Definition core.c:54
each_core_data core_data[VOXIA_MAX_CORE]
Definition core.c:35
struct cdev * next
Definition dev.h:4
uint64_t rsp[3]
Definition gdt.h:1
uint64_t vxHPETGetMainCount()
Definition hpet.c:82
#define ns2ms(x)
Definition hpet.h:14
uint8_t get_current_core_cpuid()
@ THREAD_USER
Definition thread.h:15
cpu_register_t reg
Definition thread.h:14
struct thread thread_t
Definition thread.h:29
uint64_t stack
Definition thread.h:13
@ THREAD_STATE_READY
Definition thread.h:23
@ THREAD_STATE_TERMINATED
Definition thread.h:26
@ THREAD_STATE_RUNNING
Definition thread.h:24
@ THREAD_STATE_HAL
Definition thread.h:25
#define INFLOOP
Definition init.h:32
#define INIT(fn)
Definition init.h:26
boolean_t g__scheduler__is__running
Definition scheduler.c:19
void irq_register(uint8_t core, int n, void *handler, boolean_t use_default_isr, uint16_t selector, uint8_t ist, uint8_t type_attr)
Definition interrupt.c:70
#define INTERRUPT_ATTR_KERNEL
Definition interrupt.h:28
void serial_printf(const char *fmt,...)
each_core_data
Definition core.h:32
void msrSetFSBase(uint64_t base)
Definition msr.c:19
void paging_reload(page_t p)
Definition paging.c:457
scheduler_core_t * vxGetSchedulerCore(uint16_t core)
Definition scheduler.c:21
static void vxRestoreRegister(volatile interrupt_stack_frame_t *stack, cpu_register_t *reg)
Definition scheduler.c:89
void attach_to_scheduler(thread_t *new_thread)
Definition scheduler.c:268
static scheduler_core_t scheduler[VOXIA_MAX_CORE]
Definition scheduler.c:17
void vxStartScheduler(void)
Definition scheduler.c:293
static void vxSchedulerTick(volatile interrupt_stack_frame_t *reg)
Definition scheduler.c:113
static void vxDeatachFromScheduler(scheduler_queue_t *current, bool already_locked)
Definition scheduler.c:33
static struct slab_cache * scheduler_cache
Definition scheduler.c:16
void sch_restore_to_next_thread(volatile interrupt_stack_frame_t *rsp, uint16_t core_id)
Definition scheduler.c:305
static scheduler_queue_t * vxAllocScheduler(const uint16_t core)
Definition scheduler.c:235
static void vxSaveRegister(volatile interrupt_stack_frame_t *stack, cpu_register_t *reg)
Definition scheduler.c:65
scheduler_queue_t * vxSchedulerGetCurrentQueue(uint16_t core)
Definition scheduler.c:29
struct scheduler_core scheduler_core_t
struct scheduler_queue scheduler_queue_t
Definition scheduler.h:9
#define LOG2_DEBUG(mod, fmt,...)
Definition serial.h:35
#define LOG2_ERROR(mod, fmt,...)
Definition serial.h:38
#define LOG_INFO(mod, fmt,...)
Definition serial.h:20
void * vxSlabAlloc(struct slab_cache *cache)
Definition slab.c:93
void vxCreateSlabCache(struct slab_cache **cache, const char *name, const size_t obj_size, size_t alignment, const uintptr_t virt_addr)
Definition slab.c:44
void spin_acquire(spinlock_t *lock)
Definition spinlock.c:8
void spin_release(spinlock_t *lock)
Definition spinlock.c:19
void memset(void *ptr, int value, size_t num)
thread_t * thread
Definition scheduler.h:11
struct scheduler_queue * next_queue
Definition scheduler.h:13
struct scheduler_queue * prev_queue
Definition scheduler.h:12
boolean_t has_update_run_time
Definition thread.h:39
uint16_t core_affinity
Definition thread.h:33
uint64_t last_run_time
Definition thread.h:37
uint16_t flags
Definition thread.h:36
uint64_t stack
Definition thread.h:44
volatile uintptr_t * page
Definition thread.h:29
cpu_register_t reg
Definition thread.h:45
uint16_t current_core_id
Definition thread.h:37
thread_id id
Definition thread.h:32
uint8_t state
Definition thread.h:34
uintptr_t entry_addr
Definition thread.h:43
uint64_t fs_base
Definition thread.h:43
uint32_t head
Definition tty.h:10
unsigned short uint16_t
Definition type.h:13
uint8_t boolean_t
Definition type.h:89
unsigned long uintptr_t
Definition type.h:73
unsigned long uint64_t
Definition type.h:25
unsigned char uint8_t
Definition type.h:7
int core
Definition vm_manager.h:5
workqueue_t * queue
Definition voxmo.h:9