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(lock: &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 enqueue_to_tree(sched, thread: task);
28 } else {
29 struct list_head *q = scheduler_get_this_thread_queue(sched, prio);
30 list_add_tail(new: &task->rq_list_node, head: q);
31 }
32
33 thread_set_runqueue(t: task, s: sched);
34 scheduler_increment_thread_count(sched, t: task);
35
36 bool is_local = sched == smp_core_scheduler();
37 bool period_disabled = !sched->period_enabled;
38
39 if (period_disabled && (is_local ? sched->total_thread_count >= 1
40 : sched->total_thread_count > 1)) {
41 sched->period_enabled = true;
42 scheduler_period_start(s: sched, now_ms: time_get_ms());
43 }
44
45 if (!lock_held)
46 spin_unlock(lock: &sched->lock, old: irql);
47}
48
49void scheduler_remove_thread(struct scheduler *sched, struct thread *t,
50 bool lock_held) {
51 enum irql irql = IRQL_NONE;
52 if (!lock_held)
53 irql = spin_lock_irq_disable(lock: &sched->lock);
54 else
55 SPINLOCK_ASSERT_HELD(&sched->lock);
56
57 kassert(thread_get_state(t) == THREAD_STATE_READY);
58
59 if (t->perceived_prio_class == THREAD_PRIO_CLASS_TIMESHARE) {
60 dequeue_from_tree(sched, thread: t);
61 } else {
62 list_del_init(entry: &t->rq_list_node);
63 }
64
65 scheduler_decrement_thread_count(sched, t);
66 if (!lock_held)
67 spin_unlock(lock: &sched->lock, old: irql);
68}
69
70void thread_enqueue(struct thread *t) {
71 kassert(t->allowed_cpus.nbits);
72
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(lock: &s->lock);
80
81 scheduler_add_thread(sched: s, task: t, /* lock_held = */ true);
82 scheduler_force_resched(sched: s);
83
84 spin_unlock(lock: &s->lock, old: irql);
85}
86
87/* TODO: Make scheduler_add_thread an internal function so I don't need to
88 * pass in the 'false false true' here and all over the place */
89void thread_enqueue_on_core(struct thread *t, uint64_t core_id) {
90 scheduler_add_thread(sched: global.schedulers[core_id], task: t, false);
91 scheduler_force_resched(sched: global.schedulers[core_id]);
92}
93