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::raw_kernel_mutex::LockEntryStorage;
6use core::ffi::c_void;
7use pin_init::{PinInit, pin_data};
8
9#[cfg(target_arch = "x86_64")]
10type InnerSavedState = u64;
11#[cfg(not(target_arch = "x86_64"))]
12type InnerSavedState = bool;
13
14/// Opaque token representing the saved interrupt state.
15#[repr(transparent)]
16#[derive(Copy, Clone, Default)]
17pub struct InterruptSavedState(InnerSavedState);
18
19unsafe extern "C" {
20    fn cpp_spinlock_init(lock: *mut c_void, class_id: *const c_void);
21    fn cpp_spinlock_destroy(lock: *mut c_void);
22    fn cpp_spinlock_acquire_irqsave(
23        lock: *mut c_void,
24        entry_storage: *mut c_void,
25    ) -> InterruptSavedState;
26    fn cpp_spinlock_release_irqrestore(
27        lock: *mut c_void,
28        entry_storage: *mut c_void,
29        state: InterruptSavedState,
30    );
31}
32
33#[cfg(feature = "spin_lock_tracing")]
34const RAW_SPINLOCK_SIZE: usize = 16;
35#[cfg(not(feature = "spin_lock_tracing"))]
36const RAW_SPINLOCK_SIZE: usize = 4;
37
38#[repr(C, align(8))]
39struct RawSpinlockStorage(zr::OpaqueBytes<RAW_SPINLOCK_SIZE>);
40
41impl RawSpinlockStorage {
42    const fn zeroed() -> Self {
43        Self(zr::OpaqueBytes::new([0; RAW_SPINLOCK_SIZE]))
44    }
45}
46
47/// Opaque layout block matching the Zircon C++ SpinLock exactly.
48#[pin_data(PinnedDrop)]
49#[repr(C)]
50pub struct RawSpinlock {
51    #[cfg(feature = "lock_dep")]
52    class_id: *const c_void,
53    storage: RawSpinlockStorage,
54}
55
56impl RawSpinlock {
57    /// Creates a zero-initialized (un-acquired) `RawSpinlock`.
58    pub const fn zeroed() -> Self {
59        Self {
60            #[cfg(feature = "lock_dep")]
61            class_id: core::ptr::null(),
62            storage: RawSpinlockStorage::zeroed(),
63        }
64    }
65}
66
67// SAFETY: RawSpinlock is safe to share and access across threads.
68unsafe impl Sync for RawSpinlock {}
69unsafe impl Send for RawSpinlock {}
70
71zr::unsafe_pinned_drop_ffi!(RawSpinlock, cpp_spinlock_destroy);
72
73impl crate::RawLock for RawSpinlock {
74    type LockEntry = LockEntryStorage;
75    type GuardState = InterruptSavedState;
76
77    #[inline]
78    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
79        zr::pin_init_ffi!(cpp_spinlock_init, class_id)
80    }
81
82    #[inline]
83    fn as_mut_ptr(&self) -> *mut c_void {
84        self as *const Self as *mut Self as *mut c_void
85    }
86
87    #[inline]
88    unsafe fn acquire(&self, entry: *mut Self::LockEntry) -> Self::GuardState {
89        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
90        // that `entry` points to valid storage for a lockdep entry.
91        unsafe { cpp_spinlock_acquire_irqsave(self.as_mut_ptr(), entry as *mut c_void) }
92    }
93
94    #[inline]
95    unsafe fn release(&self, entry: *mut Self::LockEntry, state: Self::GuardState) {
96        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
97        // that `entry` points to valid storage for a lockdep entry.
98        unsafe {
99            cpp_spinlock_release_irqrestore(self.as_mut_ptr(), entry as *mut c_void, state);
100        }
101    }
102}
103
104const _: () = {
105    #[cfg(feature = "lock_dep")]
106    const BASE_SIZE: usize = 8;
107    #[cfg(not(feature = "lock_dep"))]
108    const BASE_SIZE: usize = 0;
109
110    const EXPECTED_SPINLOCK_SIZE: usize = BASE_SIZE + if RAW_SPINLOCK_SIZE == 4 { 8 } else { 16 };
111
112    assert!(core::mem::size_of::<RawSpinlock>() == EXPECTED_SPINLOCK_SIZE);
113    assert!(core::mem::align_of::<RawSpinlock>() == 8);
114};