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}