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