Skip to main content

ax_kernel_guard/
lib.rs

1//! RAII wrappers to create a critical section with local IRQs or preemption
2//! disabled, used to implement spin locks in kernel.
3//!
4//! The critical section is created after the guard struct is created, and is
5//! ended when the guard falls out of scope.
6//!
7//! The crate user must implement the [`KernelGuardIf`] trait using
8//! [`ax_crate_interface::impl_interface`] to provide the low-level implementantion
9//! of how to enable/disable kernel preemption, if the feature `preempt` is
10//! enabled.
11//!
12//! Available guards:
13//!
14//! - [`NoOp`]: Does nothing around the critical section.
15//! - [`IrqSave`]: Disables/enables local IRQs around the critical section.
16//! - [`NoPreempt`]: Disables/enables kernel preemption around the critical
17//!   section.
18//! - [`NoPreemptIrqSave`]: Disables/enables both kernel preemption and local
19//!   IRQs around the critical section.
20//!
21//! # Crate features
22//!
23//! - `preempt`: Use in the preemptive system. If this feature is enabled, you
24//!   need to implement the [`KernelGuardIf`] trait in other crates. Otherwise
25//!   the preemption enable/disable operations will be no-ops. This feature is
26//!   disabled by default.
27//!
28//! # Examples
29//!
30//! ```
31//! use ax_kernel_guard::{KernelGuardIf, NoPreempt};
32//!
33//! struct KernelGuardIfImpl;
34//!
35//! #[ax_crate_interface::impl_interface]
36//! impl KernelGuardIf for KernelGuardIfImpl {
37//!     fn enable_preempt() {
38//!         // Your implementation here
39//!     }
40//!     fn disable_preempt() {
41//!         // Your implementation here
42//!     }
43//! }
44//!
45//! let guard = NoPreempt::new();
46//! // The critical section starts here
47//! //
48//! // Do something that requires preemption to be disabled
49//! //
50//! // The critical section ends here
51//! drop(guard);
52//! ```
53
54#![no_std]
55
56mod arch;
57
58/// Low-level interfaces that must be implemented by the crate user.
59#[ax_crate_interface::def_interface]
60pub trait KernelGuardIf {
61    /// How to enable kernel preemption.
62    fn enable_preempt();
63
64    /// How to disable kernel preemption.
65    fn disable_preempt();
66}
67
68/// A base trait that all guards implement.
69pub trait BaseGuard {
70    /// The saved state when entering the critical section.
71    type State: Clone + Copy;
72
73    /// Something that must be done before entering the critical section.
74    fn acquire() -> Self::State;
75
76    /// Something that must be done after leaving the critical section.
77    fn release(state: Self::State);
78
79    /// Returns whether locks guarded by this type should participate in
80    /// lock dependency tracking.
81    fn lockdep_enabled() -> bool {
82        false
83    }
84}
85
86/// A no-op guard that does nothing around the critical section.
87pub struct NoOp;
88
89cfg_if::cfg_if! {
90    // For user-mode std apps, we use the alias of [`NoOp`] for all guards,
91    // since we can not disable IRQs or preemption in user-mode.
92    if #[cfg(any(target_os = "none", doc))] {
93        /// A guard that disables/enables local IRQs around the critical section.
94        pub struct IrqSave(usize);
95
96        /// A guard that disables/enables kernel preemption around the critical
97        /// section.
98        pub struct NoPreempt;
99
100        /// A guard that disables/enables both kernel preemption and local IRQs
101        /// around the critical section.
102        ///
103        /// When entering the critical section, it disables kernel preemption
104        /// first, followed by local IRQs. When leaving the critical section, it
105        /// re-enables local IRQs first, followed by kernel preemption.
106        pub struct NoPreemptIrqSave(usize);
107    } else {
108        /// Alias of [`NoOp`].
109        pub type IrqSave = NoOp;
110
111        /// Alias of [`NoOp`].
112        pub type NoPreempt = NoOp;
113
114        /// Alias of [`NoOp`].
115        pub type NoPreemptIrqSave = NoOp;
116    }
117}
118
119impl BaseGuard for NoOp {
120    type State = ();
121    fn acquire() -> Self::State {}
122    fn release(_state: Self::State) {}
123}
124
125impl NoOp {
126    /// Creates a new [`NoOp`] guard.
127    pub const fn new() -> Self {
128        Self
129    }
130}
131
132impl Default for NoOp {
133    fn default() -> Self {
134        Self::new()
135    }
136}
137
138impl Drop for NoOp {
139    fn drop(&mut self) {}
140}
141
142#[cfg(any(target_os = "none", doc))]
143mod imp {
144    use super::*;
145
146    impl BaseGuard for IrqSave {
147        type State = usize;
148
149        #[inline]
150        fn acquire() -> Self::State {
151            super::arch::local_irq_save_and_disable()
152        }
153
154        #[inline]
155        fn release(state: Self::State) {
156            // restore IRQ states
157            super::arch::local_irq_restore(state);
158        }
159
160        fn lockdep_enabled() -> bool {
161            // Keep this disabled for now. The current task-only lockdep model
162            // no longer depends on per-CPU held-lock state, but the codebase
163            // does not currently expose any BaseSpinLock<IrqSave, _> aliases or
164            // real users, so there is no need to widen the tracked guard set
165            // until that use case is defined and tested.
166            false
167        }
168    }
169
170    impl BaseGuard for NoPreempt {
171        type State = ();
172        fn acquire() -> Self::State {
173            // disable preempt
174            #[cfg(feature = "preempt")]
175            ax_crate_interface::call_interface!(KernelGuardIf::disable_preempt);
176        }
177        fn release(_state: Self::State) {
178            // enable preempt
179            #[cfg(feature = "preempt")]
180            ax_crate_interface::call_interface!(KernelGuardIf::enable_preempt);
181        }
182
183        fn lockdep_enabled() -> bool {
184            true
185        }
186    }
187
188    impl BaseGuard for NoPreemptIrqSave {
189        type State = usize;
190        fn acquire() -> Self::State {
191            // disable preempt
192            #[cfg(feature = "preempt")]
193            ax_crate_interface::call_interface!(KernelGuardIf::disable_preempt);
194            // disable IRQs and save IRQ states
195            super::arch::local_irq_save_and_disable()
196        }
197        fn release(state: Self::State) {
198            // restore IRQ states
199            super::arch::local_irq_restore(state);
200            // enable preempt
201            #[cfg(feature = "preempt")]
202            ax_crate_interface::call_interface!(KernelGuardIf::enable_preempt);
203        }
204
205        fn lockdep_enabled() -> bool {
206            true
207        }
208    }
209
210    impl IrqSave {
211        /// Creates a new [`IrqSave`] guard.
212        pub fn new() -> Self {
213            Self(Self::acquire())
214        }
215    }
216
217    impl Drop for IrqSave {
218        fn drop(&mut self) {
219            Self::release(self.0)
220        }
221    }
222
223    impl Default for IrqSave {
224        fn default() -> Self {
225            Self::new()
226        }
227    }
228
229    impl NoPreempt {
230        /// Creates a new [`NoPreempt`] guard.
231        pub fn new() -> Self {
232            Self::acquire();
233            Self
234        }
235    }
236
237    impl Drop for NoPreempt {
238        fn drop(&mut self) {
239            Self::release(())
240        }
241    }
242
243    impl Default for NoPreempt {
244        fn default() -> Self {
245            Self::new()
246        }
247    }
248
249    impl NoPreemptIrqSave {
250        /// Creates a new [`NoPreemptIrqSave`] guard.
251        pub fn new() -> Self {
252            Self(Self::acquire())
253        }
254    }
255
256    impl Drop for NoPreemptIrqSave {
257        fn drop(&mut self) {
258            Self::release(self.0)
259        }
260    }
261
262    impl Default for NoPreemptIrqSave {
263        fn default() -> Self {
264            Self::new()
265        }
266    }
267}