1#include <atomic.h>
2#include <enforce.h>
3#include <irq/idt.h>
4#include <sch/sched.h>
5#include <smp/core.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdint.h>
9#include <sync/lock_chk_assert.h>
10#include <sync/spinlock.h>
11
12#include "internal.h"
13
14void scheduler_add_thread_locked(struct scheduler *sched, struct thread *task) {
15 enforce(atomic_load_rlx(&task->state) != THREAD_STATE_IDLE_THREAD);
16
17 /* Runqueue logic uses queued_prio_class */
18 enum thread_prio_class prio = task->perceived_prio_class;
19
20 /* Put it on the tree since this is timesharing */
21 if (prio == THREAD_PRIO_CLASS_TIMESHARE) {
22 /* This will be a new thread this period */
23 task->completed_period = sched->current_period - 1;
24 if (task->virtual_budget == 0) {
25 task->virtual_budget = sched->period_ms ? sched->period_ms : 50;
26 task->virtual_runtime_left = task->virtual_budget;
27 }
28 enqueue_to_tree(sched, thread: task);
29 } else {
30 struct list_head *q = scheduler_get_this_thread_queue(sched, prio);
31 list_head_push_back(h: q, n: &task->rq_list_node);
32 }
33
34 thread_set_runqueue(t: task, s: sched);
35 scheduler_increment_thread_count(sched, t: task);
36
37 bool period_disabled = !sched->period_enabled;
38
39 if (period_disabled && sched->total_thread_count >= 1) {
40 sched->period_enabled = true;
41 scheduler_period_start(s: sched, now_ms: time_get_ms());
42 }
43
44 scheduler_debug_validate(sched);
45}
46
47void scheduler_add_thread(struct scheduler *sched, struct thread *task) {
48 enum irql irql = spin_lock_high(&sched->lock);
49 scheduler_add_thread_locked(sched, task);
50 spin_unlock(&sched->lock, irql);
51}
52
53void scheduler_remove_thread_locked(struct scheduler *sched, struct thread *t) {
54 enforce(thread_get_state(t) == THREAD_STATE_READY);
55
56 if (t->perceived_prio_class == THREAD_PRIO_CLASS_TIMESHARE) {
57 dequeue_from_tree(sched, thread: t);
58 } else {
59 list_node_delete_init(n: &t->rq_list_node);
60 }
61
62 scheduler_decrement_thread_count(sched, t);
63 scheduler_debug_validate(sched);
64}
65
66void scheduler_remove_thread(struct scheduler *sched, struct thread *t) {
67 enum irql irql = spin_lock_high(&sched->lock);
68 scheduler_remove_thread_locked(sched, t);
69 spin_unlock(&sched->lock, irql);
70}
71
72void thread_enqueue(struct thread *t) {
73 enforce(!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_high(&s->lock);
80
81 scheduler_add_thread_locked(sched: s, task: t);
82
83 spin_unlock(&s->lock, irql);
84 scheduler_force_resched(sched: s);
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_high(&s->lock);
90 scheduler_add_thread_locked(sched: s, task: t);
91 spin_unlock(&s->lock, irql);
92
93 scheduler_force_resched(sched: s);
94}
95