Skip to main content

ksync/
raw_spin_lock.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::lockdep_tracked::{LockdepTracked, impl_lockdep_tracked_lock_policy};
6use crate::raw_lock::{ConstRawLock, ExclusiveLockPolicy, LockPolicy, RawLock};
7use core::ffi::c_void;
8use pin_init::{PinInit, pin_data};
9
10#[cfg(target_arch = "x86_64")]
11type InnerSavedState = u64;
12#[cfg(not(target_arch = "x86_64"))]
13type InnerSavedState = bool;
14
15/// Opaque token representing the saved interrupt state.
16#[repr(transparent)]
17#[derive(Copy, Clone, Default)]
18pub struct InterruptSavedState(InnerSavedState);
19
20unsafe extern "C" {
21    fn cpp_ksync_interrupt_save() -> InterruptSavedState;
22
23    fn cpp_spinlock_init(lock: *mut c_void, class_id: *const c_void);
24    fn cpp_spinlock_destroy(lock: *mut c_void);
25    fn cpp_spinlock_acquire_irqsave(lock: *mut c_void) -> InterruptSavedState;
26    fn cpp_spinlock_release_irqrestore(lock: *mut c_void, state: InterruptSavedState);
27    fn cpp_spinlock_acquire_no_irqsave(lock: *mut c_void);
28    fn cpp_spinlock_release_no_irqrestore(lock: *mut c_void);
29
30    fn cpp_monitored_spinlock_init(lock: *mut c_void, class_id: *const c_void);
31    fn cpp_monitored_spinlock_destroy(lock: *mut c_void);
32    fn cpp_monitored_spinlock_acquire_irqsave(
33        lock: *mut c_void,
34        name: *const core::ffi::c_char,
35    ) -> InterruptSavedState;
36    fn cpp_monitored_spinlock_release_irqrestore(lock: *mut c_void, state: InterruptSavedState);
37    fn cpp_monitored_spinlock_acquire_no_irqsave(lock: *mut c_void, name: *const core::ffi::c_char);
38    fn cpp_monitored_spinlock_release_no_irqrestore(lock: *mut c_void);
39}
40
41#[cfg(feature = "spin_lock_tracing")]
42const RAW_SPINLOCK_SIZE: usize = 16;
43#[cfg(not(feature = "spin_lock_tracing"))]
44const RAW_SPINLOCK_SIZE: usize = 4;
45
46/// Opaque layout block for the Zircon C++ `SpinLock` object.
47///
48/// Note: When `spin_lock_tracing` is disabled, C++ `SpinLock` has size 4 and alignment 4, whereas
49/// this type intentionally over-aligns to 8 bytes (`#[repr(C, align(8))]`, resulting in size 8 and
50/// alignment 8) so that `RawSpinlock` and `#[repr(C)]` FFI structs embedding it (such as
51/// `ControlRegion` and `TrapMap`, which pad `sizeof(LockType)` to 8 bytes in C++) maintain uniform
52/// 8-byte alignment across all `lock_dep` and `spin_lock_tracing` configurations.
53#[pin_data(PinnedDrop)]
54#[repr(C, align(8))]
55pub struct RawSpinlockStorage {
56    storage: zr::OpaqueBytes<RAW_SPINLOCK_SIZE>,
57}
58
59impl RawSpinlockStorage {
60    #[allow(clippy::declare_interior_mutable_const)]
61    pub const INIT: Self = Self::const_init();
62
63    /// Statically initializes a `RawSpinlockStorage` in constant context.
64    pub const fn const_init() -> Self {
65        Self { storage: zr::OpaqueBytes::new([0u8; RAW_SPINLOCK_SIZE]) }
66    }
67}
68
69impl Default for RawSpinlockStorage {
70    fn default() -> Self {
71        Self::INIT
72    }
73}
74
75// SAFETY: `RawSpinlockStorage` is safe to share and access across threads.
76unsafe impl Sync for RawSpinlockStorage {}
77// SAFETY: `RawSpinlockStorage` is safe to send across threads.
78unsafe impl Send for RawSpinlockStorage {}
79
80zr::unsafe_pinned_drop_ffi!(RawSpinlockStorage, cpp_spinlock_destroy);
81
82/// Opaque layout block for the Zircon C++ `MonitoredSpinLock` object.
83///
84/// Like [`RawSpinlockStorage`], this type intentionally over-aligns to 8 bytes even when
85/// `spin_lock_tracing` is disabled.
86#[pin_data(PinnedDrop)]
87#[repr(C, align(8))]
88pub struct RawMonitoredSpinlockStorage {
89    storage: zr::OpaqueBytes<RAW_SPINLOCK_SIZE>,
90}
91
92impl RawMonitoredSpinlockStorage {
93    #[allow(clippy::declare_interior_mutable_const)]
94    pub const INIT: Self = Self::const_init();
95
96    /// Statically initializes a `RawMonitoredSpinlockStorage` in constant context.
97    pub const fn const_init() -> Self {
98        Self { storage: zr::OpaqueBytes::new([0u8; RAW_SPINLOCK_SIZE]) }
99    }
100}
101
102impl Default for RawMonitoredSpinlockStorage {
103    fn default() -> Self {
104        Self::INIT
105    }
106}
107
108// SAFETY: `RawMonitoredSpinlockStorage` is safe to share and access across threads.
109unsafe impl Sync for RawMonitoredSpinlockStorage {}
110// SAFETY: `RawMonitoredSpinlockStorage` is safe to send across threads.
111unsafe impl Send for RawMonitoredSpinlockStorage {}
112
113zr::unsafe_pinned_drop_ffi!(RawMonitoredSpinlockStorage, cpp_monitored_spinlock_destroy);
114
115pub struct IrqSavePolicy;
116
117// SAFETY: `cpp_spinlock_acquire_irqsave` (or `cpp_ksync_interrupt_save` +
118// `cpp_spinlock_acquire_no_irqsave`) and `cpp_spinlock_release_irqrestore` enforce mutual
119// exclusion on `RawSpinlockStorage`.
120unsafe impl LockPolicy<RawSpinlockStorage> for IrqSavePolicy {
121    type AcquireArgs = ();
122    type GuardState = InterruptSavedState;
123
124    #[inline]
125    unsafe fn pre_validate(_lock: &RawSpinlockStorage, state: &mut Self::GuardState, _args: ()) {
126        // SAFETY: Saving and disabling interrupts is safe when paired with a matching release.
127        *state = unsafe { cpp_ksync_interrupt_save() };
128    }
129
130    #[inline]
131    unsafe fn pre_reacquire(_lock: &RawSpinlockStorage, state: &mut Self::GuardState) {
132        // SAFETY: Saving and disabling interrupts is safe when paired with a matching release.
133        *state = unsafe { cpp_ksync_interrupt_save() };
134    }
135
136    #[inline]
137    unsafe fn acquire(lock: &RawSpinlockStorage, state: &mut Self::GuardState, _args: ()) {
138        // SAFETY: The FFI call is safe because the lock is initialized.
139        *state = unsafe { cpp_spinlock_acquire_irqsave(lock.as_mut_ptr()) };
140    }
141
142    #[inline]
143    unsafe fn post_validate_acquire(
144        lock: &RawSpinlockStorage,
145        _state: &mut Self::GuardState,
146        _args: (),
147    ) {
148        // SAFETY: The lock is initialized and interrupts were disabled in `pre_validate`.
149        unsafe {
150            cpp_spinlock_acquire_no_irqsave(lock.as_mut_ptr());
151        }
152    }
153
154    #[inline]
155    unsafe fn reacquire(lock: &RawSpinlockStorage, state: &mut Self::GuardState) {
156        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
157        unsafe {
158            Self::acquire(lock, state, ());
159        }
160    }
161
162    #[inline]
163    unsafe fn post_validate_reacquire(lock: &RawSpinlockStorage, _state: &mut Self::GuardState) {
164        // SAFETY: The lock is initialized and interrupts were disabled in `pre_reacquire`.
165        unsafe {
166            cpp_spinlock_acquire_no_irqsave(lock.as_mut_ptr());
167        }
168    }
169
170    #[inline]
171    unsafe fn release(lock: &RawSpinlockStorage, state: &mut Self::GuardState) {
172        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
173        // CPU with saved interrupt state `*state`.
174        unsafe {
175            cpp_spinlock_release_irqrestore(lock.as_mut_ptr(), *state);
176        }
177    }
178}
179
180// SAFETY: `IrqSavePolicy` enforces exclusive mutual exclusion on `RawSpinlockStorage`.
181unsafe impl ExclusiveLockPolicy<RawSpinlockStorage> for IrqSavePolicy {}
182
183pub struct NoIrqSavePolicy;
184
185// SAFETY: `cpp_spinlock_acquire_no_irqsave` and `cpp_spinlock_release_no_irqrestore` enforce
186// mutual exclusion on `RawSpinlockStorage`.
187unsafe impl LockPolicy<RawSpinlockStorage> for NoIrqSavePolicy {
188    type AcquireArgs = ();
189    type GuardState = ();
190
191    #[inline]
192    unsafe fn acquire(lock: &RawSpinlockStorage, _state: &mut Self::GuardState, _args: ()) {
193        // SAFETY: The FFI call is safe because the lock is initialized.
194        unsafe { cpp_spinlock_acquire_no_irqsave(lock.as_mut_ptr()) }
195    }
196
197    #[inline]
198    unsafe fn reacquire(lock: &RawSpinlockStorage, state: &mut Self::GuardState) {
199        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
200        unsafe {
201            Self::acquire(lock, state, ());
202        }
203    }
204
205    #[inline]
206    unsafe fn release(lock: &RawSpinlockStorage, _state: &mut Self::GuardState) {
207        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
208        // CPU.
209        unsafe {
210            cpp_spinlock_release_no_irqrestore(lock.as_mut_ptr());
211        }
212    }
213}
214
215// SAFETY: `NoIrqSavePolicy` enforces exclusive mutual exclusion on `RawSpinlockStorage`.
216unsafe impl ExclusiveLockPolicy<RawSpinlockStorage> for NoIrqSavePolicy {}
217
218/// Saved interrupt state and source tag for a held `RawMonitoredSpinlock`.
219#[derive(Copy, Clone, Default)]
220pub struct MonitoredSpinlockGuardState {
221    pub interrupt_state: InterruptSavedState,
222    pub tag: crate::SourceTag,
223}
224
225// SAFETY: `cpp_monitored_spinlock_acquire_irqsave` (or `cpp_ksync_interrupt_save` +
226// `cpp_monitored_spinlock_acquire_no_irqsave`) and `cpp_monitored_spinlock_release_irqrestore`
227// enforce mutual exclusion on `RawMonitoredSpinlockStorage`.
228unsafe impl LockPolicy<RawMonitoredSpinlockStorage> for IrqSavePolicy {
229    type AcquireArgs = crate::SourceTag;
230    type GuardState = MonitoredSpinlockGuardState;
231
232    #[inline]
233    unsafe fn pre_validate(
234        _lock: &RawMonitoredSpinlockStorage,
235        state: &mut Self::GuardState,
236        tag: Self::AcquireArgs,
237    ) {
238        // SAFETY: Saving and disabling interrupts is safe when paired with a matching release.
239        let interrupt_state = unsafe { cpp_ksync_interrupt_save() };
240        *state = MonitoredSpinlockGuardState { interrupt_state, tag };
241    }
242
243    #[inline]
244    unsafe fn pre_reacquire(_lock: &RawMonitoredSpinlockStorage, state: &mut Self::GuardState) {
245        // SAFETY: Saving and disabling interrupts is safe when paired with a matching release.
246        state.interrupt_state = unsafe { cpp_ksync_interrupt_save() };
247    }
248
249    #[inline]
250    unsafe fn acquire(
251        lock: &RawMonitoredSpinlockStorage,
252        state: &mut Self::GuardState,
253        tag: Self::AcquireArgs,
254    ) {
255        // SAFETY: The FFI call is safe because the lock is initialized and `tag` points to a
256        // static null-terminated C string.
257        let interrupt_state =
258            unsafe { cpp_monitored_spinlock_acquire_irqsave(lock.as_mut_ptr(), tag.as_ptr()) };
259        *state = MonitoredSpinlockGuardState { interrupt_state, tag };
260    }
261
262    #[inline]
263    unsafe fn post_validate_acquire(
264        lock: &RawMonitoredSpinlockStorage,
265        _state: &mut Self::GuardState,
266        tag: Self::AcquireArgs,
267    ) {
268        // SAFETY: The lock is initialized, interrupts were disabled in `pre_validate`, and `tag`
269        // points to a static null-terminated C string.
270        unsafe {
271            cpp_monitored_spinlock_acquire_no_irqsave(lock.as_mut_ptr(), tag.as_ptr());
272        }
273    }
274
275    #[inline]
276    unsafe fn reacquire(lock: &RawMonitoredSpinlockStorage, state: &mut Self::GuardState) {
277        // SAFETY: Same safety requirements as `acquire`.
278        state.interrupt_state = unsafe {
279            cpp_monitored_spinlock_acquire_irqsave(lock.as_mut_ptr(), state.tag.as_ptr())
280        };
281    }
282
283    #[inline]
284    unsafe fn post_validate_reacquire(
285        lock: &RawMonitoredSpinlockStorage,
286        state: &mut Self::GuardState,
287    ) {
288        // SAFETY: The lock is initialized, interrupts were disabled in `pre_reacquire`, and
289        // `state.tag` points to a static null-terminated C string.
290        unsafe {
291            cpp_monitored_spinlock_acquire_no_irqsave(lock.as_mut_ptr(), state.tag.as_ptr());
292        }
293    }
294
295    #[inline]
296    unsafe fn release(lock: &RawMonitoredSpinlockStorage, state: &mut Self::GuardState) {
297        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
298        // CPU with saved interrupt state `state.interrupt_state`.
299        unsafe {
300            cpp_monitored_spinlock_release_irqrestore(lock.as_mut_ptr(), state.interrupt_state);
301        }
302    }
303}
304
305// SAFETY: `IrqSavePolicy` enforces exclusive mutual exclusion on `RawMonitoredSpinlockStorage`.
306unsafe impl ExclusiveLockPolicy<RawMonitoredSpinlockStorage> for IrqSavePolicy {}
307
308// SAFETY: `cpp_monitored_spinlock_acquire_no_irqsave` and
309// `cpp_monitored_spinlock_release_no_irqrestore` enforce mutual exclusion on
310// `RawMonitoredSpinlockStorage`.
311unsafe impl LockPolicy<RawMonitoredSpinlockStorage> for NoIrqSavePolicy {
312    type AcquireArgs = crate::SourceTag;
313    type GuardState = crate::SourceTag;
314
315    #[inline]
316    unsafe fn acquire(
317        lock: &RawMonitoredSpinlockStorage,
318        state: &mut Self::GuardState,
319        tag: Self::AcquireArgs,
320    ) {
321        // SAFETY: The FFI call is safe because the lock is initialized and `tag` points to a
322        // static null-terminated C string.
323        unsafe {
324            cpp_monitored_spinlock_acquire_no_irqsave(lock.as_mut_ptr(), tag.as_ptr());
325        }
326        *state = tag;
327    }
328
329    #[inline]
330    unsafe fn reacquire(lock: &RawMonitoredSpinlockStorage, state: &mut Self::GuardState) {
331        // SAFETY: Same safety requirements as `acquire`.
332        unsafe {
333            cpp_monitored_spinlock_acquire_no_irqsave(lock.as_mut_ptr(), state.as_ptr());
334        }
335    }
336
337    #[inline]
338    unsafe fn release(lock: &RawMonitoredSpinlockStorage, _state: &mut Self::GuardState) {
339        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
340        // CPU.
341        unsafe {
342            cpp_monitored_spinlock_release_no_irqrestore(lock.as_mut_ptr());
343        }
344    }
345}
346
347// SAFETY: `NoIrqSavePolicy` enforces exclusive mutual exclusion on `RawMonitoredSpinlockStorage`.
348unsafe impl ExclusiveLockPolicy<RawMonitoredSpinlockStorage> for NoIrqSavePolicy {}
349
350// SAFETY: `RawSpinlockStorage` is a physical kernel spinlock with a unique storage address.
351unsafe impl RawLock for RawSpinlockStorage {
352    type DefaultPolicy = IrqSavePolicy;
353
354    #[inline]
355    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
356        zr::pin_init_ffi!(cpp_spinlock_init, class_id)
357    }
358
359    #[inline]
360    fn as_mut_ptr(&self) -> *mut c_void {
361        self as *const Self as *mut Self as *mut c_void
362    }
363}
364
365impl ConstRawLock for RawSpinlockStorage {
366    const INIT: Self = Self::INIT;
367}
368
369// SAFETY: `RawMonitoredSpinlockStorage` is a physical kernel spinlock with a unique storage
370// address.
371unsafe impl RawLock for RawMonitoredSpinlockStorage {
372    type DefaultPolicy = IrqSavePolicy;
373
374    #[inline]
375    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
376        zr::pin_init_ffi!(cpp_monitored_spinlock_init, class_id)
377    }
378
379    #[inline]
380    fn as_mut_ptr(&self) -> *mut c_void {
381        self as *const Self as *mut Self as *mut c_void
382    }
383}
384
385impl ConstRawLock for RawMonitoredSpinlockStorage {
386    const INIT: Self = Self::INIT;
387}
388
389impl_lockdep_tracked_lock_policy!(IrqSavePolicy);
390impl_lockdep_tracked_lock_policy!(NoIrqSavePolicy);
391
392/// Lockdep-tracked kernel spinlock matching the Zircon C++ `Lock<SpinLock>` layout.
393pub type RawSpinlock =
394    LockdepTracked<RawSpinlockStorage, IrqSavePolicy, { lockdep::LOCK_FLAGS_IRQ_SAFE }>;
395
396/// Lockdep-tracked kernel monitored spinlock matching the Zircon C++ `Lock<MonitoredSpinLock>`
397/// layout.
398pub type RawMonitoredSpinlock =
399    LockdepTracked<RawMonitoredSpinlockStorage, IrqSavePolicy, { lockdep::LOCK_FLAGS_IRQ_SAFE }>;
400
401const _: () = {
402    #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
403    const BASE_SIZE: usize = 8;
404    #[cfg(not(any(feature = "lock_dep", feature = "lock_metadata_only")))]
405    const BASE_SIZE: usize = 0;
406
407    // `RawSpinlockStorage` is `#[repr(C, align(8))]`, so the 4-byte non-tracing storage is padded
408    // to 8 bytes.
409    const EXPECTED_STORAGE_SIZE: usize = if RAW_SPINLOCK_SIZE == 4 { 8 } else { 16 };
410    const EXPECTED_SPINLOCK_SIZE: usize = BASE_SIZE + EXPECTED_STORAGE_SIZE;
411
412    assert!(core::mem::size_of::<RawSpinlockStorage>() == EXPECTED_STORAGE_SIZE);
413    assert!(core::mem::align_of::<RawSpinlockStorage>() == 8);
414    assert!(core::mem::size_of::<RawMonitoredSpinlockStorage>() == EXPECTED_STORAGE_SIZE);
415    assert!(core::mem::align_of::<RawMonitoredSpinlockStorage>() == 8);
416    assert!(core::mem::size_of::<RawSpinlock>() == EXPECTED_SPINLOCK_SIZE);
417    assert!(core::mem::align_of::<RawSpinlock>() == 8);
418    assert!(core::mem::size_of::<RawMonitoredSpinlock>() == EXPECTED_SPINLOCK_SIZE);
419    assert!(core::mem::align_of::<RawMonitoredSpinlock>() == 8);
420};