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