| 1 | #include "internal.h" |
| 2 | #include <sch/sched.h> |
| 3 | #include <thread/thread.h> |
| 4 | |
| 5 | static 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 | |
| 34 | ok: |
| 35 | boosted->boost_count++; |
| 36 | return true; |
| 37 | } |
| 38 | |
| 39 | bool 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 | |
| 65 | void 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 | |
| 75 | enum 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 | |
| 84 | enum 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 | |
| 93 | void thread_remove_boost() { |
| 94 | climb_handle_remove(h: &thread_get_current()->climb_state.handle); |
| 95 | } |
| 96 | |