1#include <irq/idt.h>
2#include <kassert.h>
3#include <sch/sched.h>
4#include <smp/core.h>
5#include <stdatomic.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdint.h>
9#include <sync/spinlock.h>
10
11#include "internal.h"
12
13void scheduler_add_thread(struct scheduler *sched, struct thread *task,
14 bool lock_held) {
15 kassert(task->state != THREAD_STATE_IDLE_THREAD);
16
17 enum irql irql = IRQL_NONE;
18 if (!lock_held)
19 irql = spin_lock_irq_disable(&sched->lock);
20
21 enum thread_prio_class prio = task->perceived_prio_class;
22
23 /* Put it on the tree since this is timesharing */
24 if (prio == THREAD_PRIO_CLASS_TIMESHARE) {
25 /* This will be a new thread this period */
26 task->completed_period = sched->current_period - 1;
27 if (task->virtual_budget == 0) {
28 task->virtual_budget = sched->period_ms ? sched->period_ms : 50;
29 task->virtual_runtime_left = task->virtual_budget;
30 }
31 enqueue_to_tree(sched, thread: task);
32 } else {
33 struct list_head *q = scheduler_get_this_thread_queue(sched, prio);
34 list_add_tail(new: &task->rq_list_node, head: q);
35 }
36
37 thread_set_runqueue(t: task, s: sched);
38 scheduler_increment_thread_count(sched, t: task);
39
40 bool period_disabled = !sched->period_enabled;
41
42 if (period_disabled && sched->total_thread_count >= 1) {
43 sched->period_enabled = true;
44 scheduler_period_start(s: sched, now_ms: time_get_ms());
45 }
46
47 if (!lock_held)
48 spin_unlock(&sched->lock, irql);
49}
50
51void scheduler_remove_thread(struct scheduler *sched, struct thread *t,
52 bool lock_held) {
53 enum irql irql = IRQL_NONE;
54 if (!lock_held)
55 irql = spin_lock_irq_disable(&sched->lock);
56 else
57 SPINLOCK_ASSERT_HELD(&sched->lock);
58
59 kassert(thread_get_state(t) == THREAD_STATE_READY);
60
61 if (t->perceived_prio_class == THREAD_PRIO_CLASS_TIMESHARE) {
62 dequeue_from_tree(sched, thread: t);
63 } else {
64 list_del_init(entry: &t->rq_list_node);
65 }
66
67 scheduler_decrement_thread_count(sched, t);
68 if (!lock_held)
69 spin_unlock(&sched->lock, irql);
70}
71
72void thread_enqueue(struct thread *t) {
73 kassert(!cpu_mask_empty(&t->allowed_cpus));
74
75 struct scheduler *s = scheduler_select_best_for_thread(t);
76
77 /* hold the lock to prevent that thread from being ran
78 * while we are going to signal the other core */
79 enum irql irql = spin_lock_irq_disable(&s->lock);
80
81 scheduler_add_thread(sched: s, task: t, /* lock_held = */ true);
82 scheduler_force_resched(sched: s);
83
84 spin_unlock(&s->lock, irql);
85}
86
87void thread_enqueue_on_core(struct thread *t, uint64_t core_id) {
88 struct scheduler *s = global.schedulers[core_id];
89 enum irql irql = spin_lock_irq_disable(&s->lock);
90 scheduler_add_thread(sched: s, task: t, /* lock_held = */ true);
91 scheduler_force_resched(sched: s);
92 spin_unlock(&s->lock, irql);
93}
94