| 1 | #include "internal.h" |
| 2 | #include <thread/apc.h> |
| 3 | |
| 4 | /* file implements `thread_migrate`. |
| 5 | * |
| 6 | * NOTE: when we acquire two `struct scheduler` locks, |
| 7 | * we ALWAYS ALWAYS acquire the lock for the LOWER |
| 8 | * memory address first, same for unlock, |
| 9 | * and we ALWAYS acquire thread |
| 10 | * locks AFTER any scheduler locks! */ |
| 11 | |
| 12 | void thread_post_migrate(struct thread *t, cpu_id_t old_cpu, cpu_id_t new_cpu) { |
| 13 | /* assert that both scheduler/runqueue locks are held, |
| 14 | * migrate relevant things */ |
| 15 | SPINLOCK_ASSERT_HELD(&global.schedulers[old_cpu]->lock); |
| 16 | SPINLOCK_ASSERT_HELD(&global.schedulers[new_cpu]->lock); |
| 17 | climb_post_migrate_hook(t, old_cpu, new_cpu); |
| 18 | wroost_post_migrate_hook(t, from: old_cpu, to: new_cpu); |
| 19 | } |
| 20 | |
| 21 | void thread_migrate(struct thread *t, size_t dest_core) TSA_NO_ANALYSIS { |
| 22 | /* first acquire both the lock of the thread's scheduler |
| 23 | * and the destination core's scheduler */ |
| 24 | |
| 25 | enum irql sirql, dirql, tirql; |
| 26 | |
| 27 | struct scheduler *to_force_resched = NULL; |
| 28 | struct scheduler *src; |
| 29 | struct scheduler *dst = global.schedulers[dest_core]; |
| 30 | |
| 31 | thread_lock_thread_and_rq(t, other_rq: dst, out_thread_rq: &src, irq_first: &sirql, irq_second: &dirql); |
| 32 | |
| 33 | if (src == dst) { |
| 34 | thread_unlock_thread_and_rq(thread_rq: src, other_rq: dst, irq_first: sirql, irq_second: dirql); |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | bool ok; |
| 39 | tirql = thread_acquire(t, success: &ok); |
| 40 | if (!ok) { |
| 41 | goto end; |
| 42 | } |
| 43 | |
| 44 | /* bro cannot be migrated */ |
| 45 | if (!scheduler_can_take_thread(core: dest_core, target: t)) { |
| 46 | goto out; |
| 47 | } |
| 48 | |
| 49 | /* finally, we can do something here. */ |
| 50 | if (thread_get_state(t) == THREAD_STATE_RUNNING || |
| 51 | !(thread_get_flags(t) & THREAD_FLAG_YIELDED)) { |
| 52 | thread_set_migration_target(t, new: dest_core); |
| 53 | to_force_resched = dst; |
| 54 | } else if (thread_get_state(t) == THREAD_STATE_READY) { |
| 55 | scheduler_remove_thread_locked(sched: src, t); |
| 56 | scheduler_add_thread_locked(sched: dst, thread: t); |
| 57 | to_force_resched = dst; |
| 58 | } |
| 59 | |
| 60 | thread_set_runqueue(t, s: dst); |
| 61 | |
| 62 | out: |
| 63 | |
| 64 | thread_release(t, irql: tirql); |
| 65 | |
| 66 | end: |
| 67 | thread_unlock_thread_and_rq(thread_rq: src, other_rq: dst, irq_first: sirql, irq_second: dirql); |
| 68 | |
| 69 | if (to_force_resched) |
| 70 | scheduler_force_resched(sched: to_force_resched); |
| 71 | } |
| 72 | |