Skip to main content

strat9_kernel/sync/
guardian.rs

1//! Lock guardian trait : determines the guard behaviour of [`SpinLock`].
2//!
3//! Two built-in implementations are provided:
4//!
5//! * [`IrqDisabled`]   – saves RFLAGS and clears IF before acquiring the lock.
6//!   Use this for data shared across CPUs (heap, VFS, IPC queues, …).
7//!   This corresponds to the classic Linux `spin_lock_irqsave`.
8//!
9//! * [`PreemptDisabled`] – increments the per-CPU preemption depth without
10//!   touching IF. Use this for **per-CPU** data that is never accessed from
11//!   interrupt handlers (scheduler run-queues, per-CPU frame caches, …).
12//!
13//! The trait is sealed so that only these two implementations exist inside
14//! the kernel crate, preventing accidental misuse.
15
16use super::{IrqDisabledToken, PreemptGuard};
17
18// ========== Sealed trait ==============================
19
20mod private {
21    pub trait Sealed {}
22}
23
24/// Determines how interrupts / preemption are handled while a [`SpinLock`] is
25/// held.
26///
27/// [`SpinLock`]: super::SpinLock
28pub trait Guardian: private::Sealed {
29    /// Token type that proves the CPU is in the right protection mode.
30    type Token;
31
32    /// Enter the protected mode and return the token.
33    fn enter() -> GuardianState<Self::Token>;
34    /// Exit the protected mode, restoring the previous CPU state.
35    fn exit(state: GuardianState<Self::Token>);
36}
37
38/// Opaque state returned by [`Guardian::enter`] and consumed by
39/// [`Guardian::exit`].
40pub struct GuardianState<Token> {
41    pub(crate) token: Token,
42    pub(crate) saved_flags: u64,
43    pub(crate) restore_flags: bool,
44}
45
46// ========== IrqDisabled ==============================
47
48/// Guardian that saves RFLAGS and disables IRQs before the lock is acquired.
49///
50/// Equivalent to Linux `spin_lock_irqsave`. Use this for data that may be
51/// accessed from interrupt handlers or from multiple CPUs.
52pub struct IrqDisabled;
53
54impl private::Sealed for IrqDisabled {}
55
56impl Guardian for IrqDisabled {
57    type Token = IrqDisabledToken;
58
59    #[inline]
60    fn enter() -> GuardianState<Self::Token> {
61        let saved = crate::arch::x86_64::save_flags_and_cli();
62        // SAFETY: save_flags_and_cli() just cleared IF on this CPU.
63        let token = unsafe { IrqDisabledToken::new_unchecked() };
64        GuardianState {
65            token,
66            saved_flags: saved,
67            restore_flags: true,
68        }
69    }
70
71    #[inline]
72    fn exit(state: GuardianState<Self::Token>) {
73        if state.restore_flags {
74            crate::arch::x86_64::restore_flags(state.saved_flags);
75        }
76    }
77}
78
79// ========== PreemptDisabled ==========
80
81/// Guardian that only disables preemption, leaving IRQs untouched.
82///
83/// Use this for **per-CPU** data that is never accessed from interrupt handlers.
84/// Cheaper than [`IrqDisabled`] because it avoids a RFLAGS read/write.
85pub struct PreemptDisabled;
86
87impl private::Sealed for PreemptDisabled {}
88
89impl Guardian for PreemptDisabled {
90    type Token = PreemptGuard;
91
92    #[inline]
93    fn enter() -> GuardianState<Self::Token> {
94        GuardianState {
95            token: PreemptGuard::new(),
96            saved_flags: 0,
97            restore_flags: false,
98        }
99    }
100
101    #[inline]
102    fn exit(_state: GuardianState<Self::Token>) {
103        // PreemptGuard::drop() re-enables preemption automatically.
104    }
105}