1#include <console/printf.h>
2#include <mem/alloc.h>
3#include <sch/sched.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7#include <thread/reaper.h>
8#include <thread/workqueue.h>
9
10#include "internal.h"
11
12static size_t scheduler_thread_get_data(struct rbt_node *n) {
13 return thread_from_rq_rbt_node(n)->virtual_runtime_left;
14}
15
16static int32_t scheduler_cmp_threads(const struct rbt_node *a,
17 const struct rbt_node *b) {
18 int32_t vrla = thread_from_rq_rbt_node(a)->virtual_runtime_left;
19 int32_t vrlb = thread_from_rq_rbt_node(b)->virtual_runtime_left;
20 return vrla - vrlb;
21}
22
23static void scheduler_tick(struct timer *t) {
24 struct scheduler *self = smp_core_scheduler();
25 scheduler_mark_self_needs_resched(true);
26
27 if (scheduler_tick_enabled(sched: self)) {
28 timer_modify(timer: t, new: timer_delta_us(MS_TO_US(self->tick_duration_ms)));
29 }
30}
31
32void scheduler_init(void) {
33 scheduler_data.max_concurrent_stealers = global.core_count / 4;
34
35 /* I mean, if we have one core and that core wants
36 * to steal work from itself, go ahead? */
37 if (scheduler_data.max_concurrent_stealers == 0)
38 scheduler_data.max_concurrent_stealers = 1;
39
40 global.schedulers = kmalloc(sizeof(struct scheduler *) * global.core_count);
41 if (!global.schedulers)
42 panic("Could not allocate scheduler pointer array");
43
44 size_t i;
45 for_each_cpu_id(i) {
46 struct scheduler *s =
47 kmalloc(sizeof(struct scheduler), ALLOC_FLAGS_ZERO);
48 if (!s)
49 panic("Could not allocate scheduler %lu", i);
50
51 spinlock_init(&s->lock, LOCK_UNCHKD);
52 rbt_init(t: &s->thread_rbt, get_data: scheduler_thread_get_data,
53 compare: scheduler_cmp_threads);
54 rbt_init(t: &s->completed_rbt, get_data: scheduler_thread_get_data,
55 compare: scheduler_cmp_threads);
56 rbt_init(t: &s->climb_threads, get_data: climb_get_thread_data, compare: climb_cmp_threads);
57 s->tick_enabled = false;
58 s->current_period = 1; /* Start at period 1 to avoid
59 * starting at 0 because
60 * that would lead to threads
61 * being mistakenly identified
62 * as completed */
63
64 s->core_id = i;
65 s->tick.func = scheduler_tick;
66 s->tick.flags =
67 TIMER_FLAG_IRQ | TIMER_FLAG_CPU(s->core_id) | TIMER_FLAG_PINNED;
68 struct thread *idle_thread =
69 thread_create(name: "idle_thread_%u", entry_point: scheduler_idle_main, NULL, i);
70 idle_thread->flags |= THREAD_FLAG_PINNED;
71 idle_thread->state = THREAD_STATE_IDLE_THREAD;
72 s->idle_thread = idle_thread;
73
74 INIT_LIST_HEAD(list: &s->rt_threads);
75 INIT_LIST_HEAD(list: &s->urgent_threads);
76 INIT_LIST_HEAD(list: &s->bg_threads);
77
78 if (!i) {
79 struct thread *t = thread_create(name: "main_thread", entry_point: k_sch_main, NULL);
80 t->flags |= THREAD_FLAG_PINNED;
81 scheduler_add_thread(sched: s, thread: t, false);
82 }
83
84 global.schedulers[i] = s;
85 }
86}
87