1#include "internal.h"
2#include <sch/sched.h>
3#include <thread/thread.h>
4
5static bool scheduler_boost_thread_internal(struct thread *boosted,
6 struct thread *from,
7 enum thread_prio_class *old_class) {
8 if (old_class)
9 *old_class = boosted->perceived_prio_class;
10
11 /* If we only change the prio class, we can just return early */
12 if (boosted->perceived_prio_class < from->perceived_prio_class) {
13 boosted->perceived_prio_class = from->perceived_prio_class;
14 goto ok;
15 }
16
17 /* Here comes the fun part. We always assume that `from` is a thread that
18 * can be safely modified, and we use the runqueue lock to protected
19 * `boosted`'s CLIMB structures. We first assert that `from`'s CLIMB
20 * handle has either not been used or was used on `boosted`, and then
21 * we perform our boost. We initialize `from`'s CLIMB handle, and then
22 * attach it to `boosted` and apply relevant boosts */
23
24 struct climb_handle *h = &from->climb_state.handle;
25 kassert(!h->given_to || h->given_to == boosted);
26 if (h->given_to == boosted)
27 goto ok;
28
29 h->pressure = climb_thread_compute_pressure_to_apply(t: from);
30 climb_handle_apply_locked(t: boosted, h);
31
32 return false;
33
34ok:
35 boosted->boost_count++;
36 return true;
37}
38
39bool thread_inherit_priority(struct thread *boosted, struct thread *from,
40 enum thread_prio_class *old_class) {
41 struct scheduler *sched, *sched2;
42 enum irql irql, irql2;
43
44 thread_lock_two_runqueues(a: boosted, b: from, out_rq_a: &sched, out_rq_b: &sched2, irq_a: &irql, irq_b: &irql2);
45
46 bool did_boost = false;
47 if (thread_get_state(t: boosted) == THREAD_STATE_READY) {
48 /* thread is READY - we remove it from the runqueue and then we
49 * re-insert it */
50 scheduler_remove_thread(sched, t: boosted, /* lock_held = */ true);
51 did_boost = scheduler_boost_thread_internal(boosted, from, old_class);
52 scheduler_add_thread(sched, thread: boosted, /* lock_held = */ true);
53 } else {
54 /* if the thread is off doing anything else (maybe it's blocking, maybe
55 * it's running), we go ahead and just boost it. when it is saved those
56 * new values will be read and everything will be all splendid */
57 did_boost = scheduler_boost_thread_internal(boosted, from, old_class);
58 }
59
60 scheduler_release_two_locks(a: sched, b: sched2, a_irql: irql, b_irql: irql2);
61
62 return did_boost;
63}
64
65void thread_uninherit_priority(enum thread_prio_class class) {
66 struct thread *current = thread_get_current();
67
68 enum irql tirql = thread_acquire(t: current, NULL);
69
70 current->perceived_prio_class = class;
71
72 thread_release(t: current, irql: tirql);
73}
74
75enum thread_prio_class thread_unboost_self() {
76 struct thread *curr = thread_get_current();
77 enum irql irql = irql_raise(new_level: IRQL_DISPATCH_LEVEL);
78 enum thread_prio_class boosted = curr->perceived_prio_class;
79 curr->perceived_prio_class = curr->base_prio_class;
80 irql_lower(old_level: irql);
81 return boosted;
82}
83
84enum thread_prio_class thread_boost_self(enum thread_prio_class new) {
85 struct thread *curr = thread_get_current();
86 enum irql irql = irql_raise(new_level: IRQL_DISPATCH_LEVEL);
87 enum thread_prio_class old = curr->perceived_prio_class;
88 curr->perceived_prio_class = new;
89 irql_lower(old_level: irql);
90 return old;
91}
92
93void thread_remove_boost() {
94 climb_handle_remove(h: &thread_get_current()->climb_state.handle);
95}
96