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