Two-sided fix for the lock-ordering deadlock discovered by
Oracle review (Issue 24):
1. wakeup_contexts (this fn) held IDLE_CONTEXTS while
waiting for SchedQueuesLock on its own CPU via
SchedQueuesLock::new(&percpu.sched). If another CPU's
steal_work was holding that SchedQueuesLock (via a victim
SchedQueuesLock) and waiting for IDLE_CONTEXTS, both
threads spin forever.
Fix: drop idle_contexts immediately after building the
wakeups Vec. The Vec is the only data we need; releasing
the lock here means steal_work on another CPU can proceed
while this CPU acquires its own SchedQueuesLock.
2. steal_work held a victim's SchedQueuesLock (victim_lock)
while calling idle_contexts(token.downgrade()).push_back
on a context that turned out to be Blocked. This is the
matching side of the deadlock: CPU A held IDLE_CONTEXTS and
waited for its own SchedQueuesLock; CPU B (steal_work) held
CPU A's SchedQueuesLock and waited for IDLE_CONTEXTS.
Fix: use idle_contexts_try (try_lock) instead of
idle_contexts (blocking lock). If IDLE_CONTEXTS is busy
(owned by wakeup_contexts on another CPU), skip the
push-back; the context will be re-checked on the next
wakeup round because it was not removed from IDLE_CONTEXTS
(the Blocked status was set, but it stayed in IDLE_CONTEXTS
because we never re-pushed it).
The original code at line 429 used idle_contexts (blocking)
which is what makes this a real deadlock. try_lock is safe
because:
- If try_lock succeeds, the context is correctly pushed
- If try_lock fails, the context is still in IDLE_CONTEXTS
(we never removed it), so the next wakeup_contexts will
find it again
Phase 0c, plan orders #3, #4, #7.
P5-context-mod-sched: re-export SchedPolicy from context::mod (one-line
change to the use statement). The type is defined in context::context
by the previous P7-cache-affine-context commit; this just makes it
available as crate::context::SchedPolicy.
P8-percpu-sched: adds PerCpuSched struct to percpu.rs with SyncUnsafeCell-
wrapped run_queues, balance/last_queue/last_balance_time cells, and
take_lock/release_lock methods. Refactors PercpuBlock to embed
PerCpuSched as 'sched' field instead of standalone 'balance'/'last_queue'
fields. Adds get_percpu_block() helper.
P8-percpu-wiring: rewrites src/context/switch.rs to consume PerCpuSched:
- select_next_context reads from percpublock.sched.queues() instead
of the global RunContextData.set
- Initial placement chooses least-loaded CPU via PercpuSched.balance
- Load balance trigger fires periodically and migrates contexts
between per-CPU queues respecting sched_affinity
- Adds pub const fn to access per-cpu sched state safely
After this commit, the kernel builds with per-CPU run queues wired
into the scheduler. cargo check still has 1 pre-existing unrelated
error (src/acpi/fadt.rs:110 type mismatch) that predates the threading
work.
Combined with the P6-futex-sharding commit, this completes the
foundation for Phase 1 (Futex Completeness) and Phase 2 (SMP Scheduling
Quality).