Skip to main content

kernel/types/
irq_mutex.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Interrupt-aware spinlock (`IrqMutex`).
3//!
4//! Authors: MarioS271
5
6use core::cell::UnsafeCell;
7use core::ops::{Deref, DerefMut};
8use core::sync::atomic::{AtomicBool, Ordering};
9use x86_64::registers::rflags::RFlags;
10use crate::arch::instructions;
11
12/// Spinlock for data shared between normal kernel code and interrupt handlers;
13/// disables interrupts while held to avoid self-deadlock.
14pub struct IrqMutex<T> {
15    data: UnsafeCell<T>,
16    locked: AtomicBool,
17}
18
19unsafe impl<T: Send> Sync for IrqMutex<T> {}
20unsafe impl<T: Send> Send for IrqMutex<T> {}
21
22impl<T> IrqMutex<T> {
23    /// Create a new unlocked `IrqMutex` wrapping `val`.
24    pub const fn new(val: T) -> Self {
25        IrqMutex {
26            data: UnsafeCell::new(val),
27            locked: AtomicBool::new(false),
28        }
29    }
30
31    /// Acquire the lock, disabling interrupts first. Returns a guard that restores IF on drop.
32    pub fn lock(&self) -> IrqMutexGuard<'_, T> {
33        let rflags = x86_64::registers::rflags::read();
34        instructions::disable_interrupts();
35
36        loop {
37            let res = self.locked.compare_exchange(
38                false,
39                true,
40                Ordering::Acquire,
41                Ordering::Relaxed
42            );
43            if res.is_ok() { break; }
44        }
45
46        IrqMutexGuard::new(self, rflags)
47    }
48
49    /// Release the lock without restoring the interrupt flag, for use in panic paths.
50    ///
51    /// # Safety
52    /// Calling this while a live [`IrqMutexGuard`] still exists creates two concurrent
53    /// accessors to the protected data. Only call from a panic handler that halts the
54    /// CPU immediately after and never touches the data through the mutex again.
55    pub unsafe fn force_unlock(&self) {
56        self.locked.store(false, Ordering::Release);
57    }
58}
59
60/// RAII guard for [`IrqMutex`]; releases the lock and restores the interrupt flag on drop.
61pub struct IrqMutexGuard<'g, T> {
62    mutex: &'g IrqMutex<T>,
63    /// RFLAGS captured before interrupts were disabled; its IF bit decides whether to re-enable on drop.
64    rflags: RFlags,
65}
66
67impl<'g, T> IrqMutexGuard<'g, T> {
68    /// Construct a guard associated with `mutex`, saving `rflags` for later restore.
69    pub fn new(mutex: &'g IrqMutex<T>, rflags: RFlags) -> IrqMutexGuard<'g, T> {
70        IrqMutexGuard{ mutex, rflags }
71    }
72}
73
74impl<'g, T> Drop for IrqMutexGuard<'g, T> {
75    fn drop(&mut self) {
76        self.mutex.locked.store(false, Ordering::Release);
77        if self.rflags.contains(RFlags::INTERRUPT_FLAG) {
78            instructions::enable_interrupts();
79        }
80    }
81}
82
83impl<'g, T> Deref for IrqMutexGuard<'g, T> {
84    type Target = T;
85
86    fn deref(&self) -> &Self::Target {
87        unsafe { &*self.mutex.data.get() }
88    }
89}
90
91impl<'g, T> DerefMut for IrqMutexGuard<'g, T> {
92    fn deref_mut(&mut self) -> &mut Self::Target {
93        unsafe { &mut *self.mutex.data.get() }
94    }
95}