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 crate::raw_lock::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_spinlock_init(lock: *mut c_void, class_id: *const c_void);
22    fn cpp_spinlock_destroy(lock: *mut c_void);
23    fn cpp_spinlock_acquire_irqsave(
24        lock: *mut c_void,
25        entry_storage: *mut c_void,
26    ) -> InterruptSavedState;
27    fn cpp_spinlock_release_irqrestore(
28        lock: *mut c_void,
29        entry_storage: *mut c_void,
30        state: InterruptSavedState,
31    );
32    fn cpp_spinlock_acquire_no_irqsave(lock: *mut c_void, entry_storage: *mut c_void);
33    fn cpp_spinlock_release_no_irqrestore(lock: *mut c_void, entry_storage: *mut c_void);
34
35    fn cpp_monitored_spinlock_init(lock: *mut c_void, class_id: *const c_void);
36    fn cpp_monitored_spinlock_destroy(lock: *mut c_void);
37    fn cpp_monitored_spinlock_acquire_irqsave(
38        lock: *mut c_void,
39        entry_storage: *mut c_void,
40        name: *const core::ffi::c_char,
41    ) -> InterruptSavedState;
42    fn cpp_monitored_spinlock_release_irqrestore(
43        lock: *mut c_void,
44        entry_storage: *mut c_void,
45        state: InterruptSavedState,
46    );
47    fn cpp_monitored_spinlock_acquire_no_irqsave(
48        lock: *mut c_void,
49        entry_storage: *mut c_void,
50        name: *const core::ffi::c_char,
51    );
52    fn cpp_monitored_spinlock_release_no_irqrestore(lock: *mut c_void, entry_storage: *mut c_void);
53}
54
55#[cfg(feature = "spin_lock_tracing")]
56const RAW_SPINLOCK_SIZE: usize = 16;
57#[cfg(not(feature = "spin_lock_tracing"))]
58const RAW_SPINLOCK_SIZE: usize = 4;
59
60#[repr(C, align(8))]
61struct RawSpinlockStorage(zr::OpaqueBytes<RAW_SPINLOCK_SIZE>);
62
63/// Opaque layout block matching the Zircon C++ SpinLock exactly.
64#[pin_data(PinnedDrop)]
65#[repr(C)]
66pub struct RawSpinlock {
67    #[cfg(feature = "lock_dep")]
68    class_id: *const c_void,
69    storage: RawSpinlockStorage,
70}
71
72impl RawSpinlock {
73    pub const INIT: Self = Self::const_init(core::ptr::null());
74
75    /// Statically initializes a RawSpinlock in constant context.
76    pub const fn const_init(_class_id: *const c_void) -> Self {
77        Self {
78            #[cfg(feature = "lock_dep")]
79            class_id: _class_id,
80            storage: RawSpinlockStorage(zr::OpaqueBytes::new([0u8; RAW_SPINLOCK_SIZE])),
81        }
82    }
83}
84
85impl Default for RawSpinlock {
86    fn default() -> Self {
87        Self::INIT
88    }
89}
90
91// SAFETY: RawSpinlock is safe to share and access across threads.
92unsafe impl Sync for RawSpinlock {}
93unsafe impl Send for RawSpinlock {}
94
95zr::unsafe_pinned_drop_ffi!(RawSpinlock, cpp_spinlock_destroy);
96
97/// Opaque layout block matching the Zircon C++ MonitoredSpinLock exactly.
98#[pin_data(PinnedDrop)]
99#[repr(C)]
100pub struct RawMonitoredSpinlock {
101    #[cfg(feature = "lock_dep")]
102    class_id: *const c_void,
103    storage: RawSpinlockStorage,
104}
105
106impl RawMonitoredSpinlock {
107    pub const INIT: Self = Self::const_init(core::ptr::null());
108
109    /// Statically initializes a RawMonitoredSpinlock in constant context.
110    pub const fn const_init(_class_id: *const c_void) -> Self {
111        Self {
112            #[cfg(feature = "lock_dep")]
113            class_id: _class_id,
114            storage: RawSpinlockStorage(zr::OpaqueBytes::new([0u8; RAW_SPINLOCK_SIZE])),
115        }
116    }
117}
118
119impl Default for RawMonitoredSpinlock {
120    fn default() -> Self {
121        Self::INIT
122    }
123}
124
125// SAFETY: RawMonitoredSpinlock is safe to share and access across threads.
126unsafe impl Sync for RawMonitoredSpinlock {}
127unsafe impl Send for RawMonitoredSpinlock {}
128
129zr::unsafe_pinned_drop_ffi!(RawMonitoredSpinlock, cpp_monitored_spinlock_destroy);
130
131pub struct IrqSavePolicy;
132
133impl crate::LockPolicy<RawSpinlock> for IrqSavePolicy {
134    type AcquireArgs = ();
135    type GuardState = InterruptSavedState;
136
137    #[inline]
138    unsafe fn acquire(
139        lock: &RawSpinlock,
140        entry: *mut LockEntryStorage,
141        _args: (),
142    ) -> Self::GuardState {
143        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
144        // that `entry` points to valid storage for a lockdep entry.
145        unsafe { cpp_spinlock_acquire_irqsave(lock.as_mut_ptr(), entry as *mut c_void) }
146    }
147
148    #[inline]
149    unsafe fn reacquire(
150        lock: &RawSpinlock,
151        entry: *mut LockEntryStorage,
152        state: &mut Self::GuardState,
153    ) {
154        *state = unsafe { Self::acquire(lock, entry, ()) };
155    }
156
157    #[inline]
158    unsafe fn release(lock: &RawSpinlock, entry: *mut LockEntryStorage, state: Self::GuardState) {
159        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
160        // that `entry` points to valid storage for a lockdep entry.
161        unsafe {
162            cpp_spinlock_release_irqrestore(lock.as_mut_ptr(), entry as *mut c_void, state);
163        }
164    }
165}
166
167pub struct NoIrqSavePolicy;
168
169impl crate::LockPolicy<RawSpinlock> for NoIrqSavePolicy {
170    type AcquireArgs = ();
171    type GuardState = ();
172
173    #[inline]
174    unsafe fn acquire(
175        lock: &RawSpinlock,
176        entry: *mut LockEntryStorage,
177        _args: (),
178    ) -> Self::GuardState {
179        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
180        // that `entry` points to valid storage for a lockdep entry.
181        unsafe { cpp_spinlock_acquire_no_irqsave(lock.as_mut_ptr(), entry as *mut c_void) }
182    }
183
184    #[inline]
185    unsafe fn reacquire(
186        lock: &RawSpinlock,
187        entry: *mut LockEntryStorage,
188        state: &mut Self::GuardState,
189    ) {
190        *state = unsafe { Self::acquire(lock, entry, ()) };
191    }
192
193    #[inline]
194    unsafe fn release(lock: &RawSpinlock, entry: *mut LockEntryStorage, _state: Self::GuardState) {
195        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
196        // that `entry` points to valid storage for a lockdep entry.
197        unsafe {
198            cpp_spinlock_release_no_irqrestore(lock.as_mut_ptr(), entry as *mut c_void);
199        }
200    }
201}
202
203/// Saved interrupt state and source tag for a held `RawMonitoredSpinlock`.
204#[derive(Copy, Clone, Default)]
205pub struct MonitoredSpinlockGuardState {
206    pub interrupt_state: InterruptSavedState,
207    pub tag: crate::SourceTag,
208}
209
210impl crate::LockPolicy<RawMonitoredSpinlock> for IrqSavePolicy {
211    type AcquireArgs = crate::SourceTag;
212    type GuardState = MonitoredSpinlockGuardState;
213
214    #[inline]
215    unsafe fn acquire(
216        lock: &RawMonitoredSpinlock,
217        entry: *mut LockEntryStorage,
218        tag: Self::AcquireArgs,
219    ) -> Self::GuardState {
220        // SAFETY: The FFI call is safe because the lock is initialized, `entry` points to valid
221        // storage for a lockdep entry, and `tag` points to a static null-terminated C string.
222        let interrupt_state = unsafe {
223            cpp_monitored_spinlock_acquire_irqsave(
224                lock.as_mut_ptr(),
225                entry as *mut c_void,
226                tag.as_ptr(),
227            )
228        };
229        MonitoredSpinlockGuardState { interrupt_state, tag }
230    }
231
232    #[inline]
233    unsafe fn reacquire(
234        lock: &RawMonitoredSpinlock,
235        entry: *mut LockEntryStorage,
236        state: &mut Self::GuardState,
237    ) {
238        // SAFETY: Same safety requirements as `acquire`.
239        state.interrupt_state = unsafe {
240            cpp_monitored_spinlock_acquire_irqsave(
241                lock.as_mut_ptr(),
242                entry as *mut c_void,
243                state.tag.as_ptr(),
244            )
245        };
246    }
247
248    #[inline]
249    unsafe fn release(
250        lock: &RawMonitoredSpinlock,
251        entry: *mut LockEntryStorage,
252        state: Self::GuardState,
253    ) {
254        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
255        // that `entry` points to valid storage for a lockdep entry.
256        unsafe {
257            cpp_monitored_spinlock_release_irqrestore(
258                lock.as_mut_ptr(),
259                entry as *mut c_void,
260                state.interrupt_state,
261            );
262        }
263    }
264}
265
266impl crate::LockPolicy<RawMonitoredSpinlock> for NoIrqSavePolicy {
267    type AcquireArgs = crate::SourceTag;
268    type GuardState = crate::SourceTag;
269
270    #[inline]
271    unsafe fn acquire(
272        lock: &RawMonitoredSpinlock,
273        entry: *mut LockEntryStorage,
274        tag: Self::AcquireArgs,
275    ) -> Self::GuardState {
276        // SAFETY: The FFI call is safe because the lock is initialized, `entry` points to valid
277        // storage for a lockdep entry, and `tag` points to a static null-terminated C string.
278        unsafe {
279            cpp_monitored_spinlock_acquire_no_irqsave(
280                lock.as_mut_ptr(),
281                entry as *mut c_void,
282                tag.as_ptr(),
283            );
284        }
285        tag
286    }
287
288    #[inline]
289    unsafe fn reacquire(
290        lock: &RawMonitoredSpinlock,
291        entry: *mut LockEntryStorage,
292        state: &mut Self::GuardState,
293    ) {
294        // SAFETY: Same safety requirements as `acquire`.
295        unsafe {
296            cpp_monitored_spinlock_acquire_no_irqsave(
297                lock.as_mut_ptr(),
298                entry as *mut c_void,
299                state.as_ptr(),
300            );
301        }
302    }
303
304    #[inline]
305    unsafe fn release(
306        lock: &RawMonitoredSpinlock,
307        entry: *mut LockEntryStorage,
308        _state: Self::GuardState,
309    ) {
310        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
311        // that `entry` points to valid storage for a lockdep entry.
312        unsafe {
313            cpp_monitored_spinlock_release_no_irqrestore(lock.as_mut_ptr(), entry as *mut c_void);
314        }
315    }
316}
317
318impl crate::RawLock for RawSpinlock {
319    const LOCK_FLAGS: lockdep::LockFlags = lockdep::LOCK_FLAGS_IRQ_SAFE;
320
321    type LockEntry = LockEntryStorage;
322    type DefaultPolicy = IrqSavePolicy;
323
324    #[inline]
325    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
326        zr::pin_init_ffi!(cpp_spinlock_init, class_id)
327    }
328
329    #[inline]
330    fn as_mut_ptr(&self) -> *mut c_void {
331        self as *const Self as *mut Self as *mut c_void
332    }
333}
334
335impl crate::RawLock for RawMonitoredSpinlock {
336    const LOCK_FLAGS: lockdep::LockFlags = lockdep::LOCK_FLAGS_IRQ_SAFE;
337
338    type LockEntry = LockEntryStorage;
339    type DefaultPolicy = IrqSavePolicy;
340
341    #[inline]
342    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
343        zr::pin_init_ffi!(cpp_monitored_spinlock_init, class_id)
344    }
345
346    #[inline]
347    fn as_mut_ptr(&self) -> *mut c_void {
348        self as *const Self as *mut Self as *mut c_void
349    }
350}
351
352const _: () = {
353    #[cfg(feature = "lock_dep")]
354    const BASE_SIZE: usize = 8;
355    #[cfg(not(feature = "lock_dep"))]
356    const BASE_SIZE: usize = 0;
357
358    const EXPECTED_SPINLOCK_SIZE: usize = BASE_SIZE + if RAW_SPINLOCK_SIZE == 4 { 8 } else { 16 };
359
360    assert!(core::mem::size_of::<RawSpinlock>() == EXPECTED_SPINLOCK_SIZE);
361    assert!(core::mem::align_of::<RawSpinlock>() == 8);
362    assert!(core::mem::size_of::<RawMonitoredSpinlock>() == EXPECTED_SPINLOCK_SIZE);
363    assert!(core::mem::align_of::<RawMonitoredSpinlock>() == 8);
364};