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 // `ax-kspin` lockdep stores held-lock state in per-CPU data, which
162 // requires preemption to remain disabled while a tracked lock is
163 // held. `IrqSave` only disables local IRQs, so enabling lockdep
164 // here would let a task be preempted while its held-lock state is
165 // still recorded on the current CPU.
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}