Skip to content

Commit 197c362

Browse files
author
adrerl
committed
fixing ABBA deadlocks yay
1 parent 252b7c9 commit 197c362

7 files changed

Lines changed: 236 additions & 118 deletions

File tree

hwinit/src/process/scheduler.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ pub(crate) static mut PROCESS_TABLE: [Option<Process>; MAX_PROCESSES] =
3939
/// The timer ISR on every core calls scheduler_tick() which must not
4040
/// race against itself or syscall handlers modifying the same process.
4141
/// Not reentrant — never acquire from code that already holds it.
42-
/// IF must be disabled before acquiring (IA32_FMASK handles this for syscalls,
43-
/// hardware handles it for ISRs).
44-
pub(crate) static PROCESS_TABLE_LOCK: crate::sync::RawSpinLock = crate::sync::RawSpinLock::new();
42+
/// Disables IF on acquire, restores on release — safe from both ISR
43+
/// and non-ISR contexts (BSP idle loop, task manager, etc).
44+
pub(crate) static PROCESS_TABLE_LOCK: crate::sync::IsrSafeRawSpinLock =
45+
crate::sync::IsrSafeRawSpinLock::new();
4546

4647
/// PID of the currently executing process on core 0 (BSP).
4748
/// For SMP, each core tracks its own current_pid via gs:[0x0C].

hwinit/src/sync.rs

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,6 @@ impl<T> Drop for SpinLockGuard<'_, T> {
130130
}
131131

132132
// RAW SPINLOCK (no interrupt disable, for when you manage it yourself)
133-
134-
/// Raw spinlock without interrupt management.
135-
///
136-
/// Use when you're already in an interrupt-disabled context or
137-
/// don't need interrupt safety.
138133
pub struct RawSpinLock {
139134
locked: AtomicBool,
140135
}
@@ -167,6 +162,93 @@ impl RawSpinLock {
167162
}
168163
}
169164

165+
// ISR-SAFE RAW SPINLOCK — disables interrupts before acquiring, restores on release.
166+
// Uses per-core saved-IF storage so nested/cross-core usage is correct.
167+
// Max 64 cores. Core index from gs:[0x00] (per-CPU area set up by ap_boot).
168+
169+
pub struct IsrSafeRawSpinLock {
170+
locked: AtomicBool,
171+
/// Per-core saved IF state. Index = core index (0..MAX_CORES).
172+
/// Only the core that holds the lock reads/writes its own slot.
173+
saved_if: [core::cell::UnsafeCell<bool>; 64],
174+
}
175+
176+
// multiple cores touch disjoint slots — safe by construction.
177+
unsafe impl Sync for IsrSafeRawSpinLock {}
178+
unsafe impl Send for IsrSafeRawSpinLock {}
179+
180+
impl IsrSafeRawSpinLock {
181+
pub const fn new() -> Self {
182+
// const init: can't use array::from_fn in const, unroll via macro
183+
const CELL_FALSE: core::cell::UnsafeCell<bool> = core::cell::UnsafeCell::new(false);
184+
Self {
185+
locked: AtomicBool::new(false),
186+
saved_if: [CELL_FALSE; 64],
187+
}
188+
}
189+
190+
#[inline(always)]
191+
fn core_index() -> usize {
192+
// before per-CPU area is set up, gs base is 0 and this reads 0 (BSP)
193+
let idx: u32;
194+
unsafe {
195+
core::arch::asm!(
196+
"mov {0:e}, gs:[0x00]",
197+
out(reg) idx,
198+
options(nostack, readonly, preserves_flags)
199+
);
200+
}
201+
(idx as usize) & 63
202+
}
203+
204+
pub fn lock(&self) {
205+
let was_enabled = interrupts_enabled();
206+
disable_interrupts();
207+
208+
while self
209+
.locked
210+
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
211+
.is_err()
212+
{
213+
core::hint::spin_loop();
214+
}
215+
216+
// save per-core IF state AFTER acquiring — only holder touches its slot
217+
let ci = Self::core_index();
218+
unsafe { *self.saved_if[ci].get() = was_enabled; }
219+
}
220+
221+
pub fn unlock(&self) {
222+
let ci = Self::core_index();
223+
let was_enabled = unsafe { *self.saved_if[ci].get() };
224+
self.locked.store(false, Ordering::Release);
225+
226+
if was_enabled {
227+
enable_interrupts();
228+
}
229+
}
230+
231+
pub fn try_lock(&self) -> bool {
232+
let was_enabled = interrupts_enabled();
233+
disable_interrupts();
234+
235+
if self
236+
.locked
237+
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
238+
.is_ok()
239+
{
240+
let ci = Self::core_index();
241+
unsafe { *self.saved_if[ci].get() = was_enabled; }
242+
true
243+
} else {
244+
if was_enabled {
245+
enable_interrupts();
246+
}
247+
false
248+
}
249+
}
250+
}
251+
170252
// ONCE (run-once initialization)
171253

172254
/// Run-once initialization primitive.
@@ -218,8 +300,6 @@ impl Once {
218300
}
219301
}
220302

221-
// LAZY (lazily initialized value)
222-
223303
/// Lazily initialized value.
224304
pub struct Lazy<T, F = fn() -> T> {
225305
once: Once,
@@ -259,8 +339,6 @@ impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
259339
}
260340
}
261341

262-
// INTERRUPT GUARD
263-
264342
/// RAII guard that disables interrupts and restores on drop.
265343
pub struct InterruptGuard {
266344
was_enabled: bool,

hwinit/src/syscall/handler/compositor.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ pub struct SurfaceEntry {
2828
/// Returns EBUSY if another compositor is already registered.
2929
pub unsafe fn sys_compositor_set() -> u64 {
3030
use crate::serial::{puts, put_hex32};
31+
use core::sync::atomic::Ordering::Relaxed;
3132
let pid = SCHEDULER.current_pid();
32-
if COMPOSITOR_PID != 0 && COMPOSITOR_PID != pid {
33+
let cur = COMPOSITOR_PID.load(Relaxed);
34+
if cur != 0 && cur != pid {
3335
puts("[COMP] compositor_set EBUSY — already held by pid ");
34-
put_hex32(COMPOSITOR_PID);
36+
put_hex32(cur);
3537
puts("\n");
3638
return EBUSY;
3739
}
38-
COMPOSITOR_PID = pid;
40+
COMPOSITOR_PID.store(pid, Relaxed);
3941
puts("[COMP] compositor_set: pid ");
4042
put_hex32(pid);
4143
puts(" registered\n");
@@ -54,12 +56,14 @@ pub unsafe fn sys_compositor_set() -> u64 {
5456
/// surfaces written (or total count if buf_ptr is 0).
5557
pub unsafe fn sys_win_surface_list(buf_ptr: u64, max_count: u64) -> u64 {
5658
// no compositor registered yet. u64::MAX = "try again later".
57-
if COMPOSITOR_PID == 0 {
59+
use core::sync::atomic::Ordering::Relaxed;
60+
let cpid = COMPOSITOR_PID.load(Relaxed);
61+
if cpid == 0 {
5862
return u64::MAX;
5963
}
6064

6165
let pid = SCHEDULER.current_pid();
62-
if pid != COMPOSITOR_PID {
66+
if pid != cpid {
6367
// compositor is alive but you're not it. return 0 so shelld stops waiting.
6468
return 0;
6569
}
@@ -142,7 +146,7 @@ pub unsafe fn sys_win_surface_list(buf_ptr: u64, max_count: u64) -> u64 {
142146
/// EINVAL if the target has no surface, or EPERM if caller isn't compositor.
143147
pub unsafe fn sys_win_surface_map(target_pid: u64) -> u64 {
144148
let pid = SCHEDULER.current_pid();
145-
if pid != COMPOSITOR_PID {
149+
if pid != COMPOSITOR_PID.load(core::sync::atomic::Ordering::Relaxed) {
146150
return EPERM;
147151
}
148152

@@ -178,7 +182,7 @@ pub unsafe fn sys_win_surface_map(target_pid: u64) -> u64 {
178182
/// packed_state: bits [15:0] = dx (i16), [31:16] = dy (i16), [39:32] = buttons.
179183
pub unsafe fn sys_mouse_forward(target_pid: u64, packed: u64) -> u64 {
180184
let pid = SCHEDULER.current_pid();
181-
if pid != COMPOSITOR_PID {
185+
if pid != COMPOSITOR_PID.load(core::sync::atomic::Ordering::Relaxed) {
182186
return EPERM;
183187
}
184188

@@ -208,7 +212,7 @@ pub unsafe fn sys_mouse_forward(target_pid: u64, packed: u64) -> u64 {
208212
/// Only callable by the compositor (after it has read the surface).
209213
pub unsafe fn sys_win_surface_dirty_clear(target_pid: u64) -> u64 {
210214
let pid = SCHEDULER.current_pid();
211-
if pid != COMPOSITOR_PID {
215+
if pid != COMPOSITOR_PID.load(core::sync::atomic::Ordering::Relaxed) {
212216
return EPERM;
213217
}
214218

@@ -257,7 +261,7 @@ pub unsafe fn sys_try_wait(pid: u64) -> u64 {
257261
pub unsafe fn sys_forward_input(target_pid: u64, ptr: u64, len: u64) -> u64 {
258262
// Only the compositor gets to play input router.
259263
let pid = SCHEDULER.current_pid();
260-
if pid != COMPOSITOR_PID {
264+
if pid != COMPOSITOR_PID.load(core::sync::atomic::Ordering::Relaxed) {
261265
return EPERM;
262266
}
263267

0 commit comments

Comments
 (0)