Skip to main content

ksync/
raw_kernel_mutex.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_lock::RawLock;
6use core::ffi::c_void;
7use pin_init::{PinInit, pin_data, pin_init_from_closure};
8
9#[cfg(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"))]
10const RAW_MUTEX_SIZE: usize = 32;
11#[cfg(not(any(feature = "lock_name_tracing", feature = "spin_lock_tracing")))]
12const RAW_MUTEX_SIZE: usize = 24;
13
14unsafe extern "C" {
15    fn cpp_mutex_destroy(mutex: *mut c_void);
16    fn cpp_mutex_acquire(lock: *mut c_void, entry_storage: *mut c_void);
17    fn cpp_mutex_release(lock: *mut c_void, entry_storage: *mut c_void);
18    fn cpp_mutex_is_contested(lock: *mut c_void) -> bool;
19
20    fn cpp_critical_mutex_destroy(mutex: *mut c_void);
21    fn cpp_critical_mutex_acquire(lock: *mut c_void, entry_storage: *mut c_void) -> bool;
22    fn cpp_critical_mutex_release(
23        lock: *mut c_void,
24        entry_storage: *mut c_void,
25        should_clear: bool,
26    );
27    fn cpp_critical_mutex_is_contested(lock: *mut c_void) -> bool;
28}
29
30const MUTEX_MAGIC: u32 = 0x6D757478; // 'mutx'
31const INVALID_CPU: u32 = u32::MAX;
32
33const fn make_mutex_storage() -> [u8; RAW_MUTEX_SIZE] {
34    let mut bytes = [0u8; RAW_MUTEX_SIZE];
35    #[cfg(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"))]
36    let offset = 8;
37    #[cfg(not(any(feature = "lock_name_tracing", feature = "spin_lock_tracing")))]
38    let offset = 0;
39
40    let magic_bytes = MUTEX_MAGIC.to_ne_bytes();
41    let cpu_bytes = INVALID_CPU.to_ne_bytes();
42
43    bytes[offset] = magic_bytes[0];
44    bytes[offset + 1] = magic_bytes[1];
45    bytes[offset + 2] = magic_bytes[2];
46    bytes[offset + 3] = magic_bytes[3];
47
48    bytes[offset + 4] = cpu_bytes[0];
49    bytes[offset + 5] = cpu_bytes[1];
50    bytes[offset + 6] = cpu_bytes[2];
51    bytes[offset + 7] = cpu_bytes[3];
52
53    bytes
54}
55
56#[repr(C, align(8))]
57struct RawMutexStorage(zr::OpaqueBytes<RAW_MUTEX_SIZE>);
58
59#[derive(Default)]
60#[repr(C, align(8))]
61pub struct LockEntryStorage(zr::OpaqueBytes<40>);
62
63/// Opaque layout block matching the Zircon C++ Mutex exactly.
64#[pin_data(PinnedDrop)]
65#[repr(C)]
66pub struct RawMutex {
67    #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
68    class_id: *const c_void,
69    storage: RawMutexStorage,
70}
71
72impl RawMutex {
73    pub const INIT: Self = Self::const_init(core::ptr::null());
74
75    /// Statically initializes a RawMutex 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: RawMutexStorage(zr::OpaqueBytes::new(make_mutex_storage())),
81        }
82    }
83
84    /// Is the mutex contested i.e. is at least one thread blocked waiting on it?
85    ///
86    /// The contested flag does not track threads which are spin-waiting on the
87    /// Mutex and have yet to enter a blocking phase.
88    #[inline]
89    pub fn is_contested(&self) -> bool {
90        // SAFETY: The FFI call is safe because the lock is initialized
91        unsafe { cpp_mutex_is_contested(self.as_mut_ptr()) }
92    }
93
94    /// Returns a slice over the raw mutex storage bytes.
95    #[cfg(any(test, ktest))]
96    #[inline]
97    pub fn raw_storage_slice(&self) -> &[u8] {
98        // SAFETY: The storage is valid and allocated with RAW_MUTEX_SIZE bytes.
99        unsafe { core::slice::from_raw_parts(self.storage.0.get() as *const u8, RAW_MUTEX_SIZE) }
100    }
101}
102
103impl Default for RawMutex {
104    fn default() -> Self {
105        Self::const_init(core::ptr::null())
106    }
107}
108
109// SAFETY: RawMutex is safe to share and access across threads.
110unsafe impl Sync for RawMutex {}
111// SAFETY: RawMutex is safe to send across threads.
112unsafe impl Send for RawMutex {}
113
114zr::unsafe_pinned_drop_ffi!(RawMutex, cpp_mutex_destroy);
115
116pub struct RawMutexPolicy;
117
118// SAFETY: `cpp_mutex_acquire` and `cpp_mutex_release` enforce mutual exclusion on `RawMutex`.
119unsafe impl crate::LockPolicy<RawMutex> for RawMutexPolicy {
120    type AcquireArgs = ();
121    type GuardState = ();
122
123    #[inline]
124    unsafe fn acquire(
125        lock: &RawMutex,
126        entry: *mut LockEntryStorage,
127        _args: (),
128    ) -> Self::GuardState {
129        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
130        // that `entry` points to valid storage for a lockdep entry.
131        unsafe {
132            cpp_mutex_acquire(lock.as_mut_ptr(), entry as *mut c_void);
133        }
134    }
135
136    #[inline]
137    unsafe fn reacquire(
138        lock: &RawMutex,
139        entry: *mut LockEntryStorage,
140        state: &mut Self::GuardState,
141    ) {
142        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
143        *state = unsafe { Self::acquire(lock, entry, ()) };
144    }
145
146    #[inline]
147    unsafe fn release(lock: &RawMutex, entry: *mut LockEntryStorage, _state: Self::GuardState) {
148        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
149        // that `entry` points to valid storage for a lockdep entry.
150        unsafe {
151            cpp_mutex_release(lock.as_mut_ptr(), entry as *mut c_void);
152        }
153    }
154}
155
156// SAFETY: `RawMutex` is a physical kernel mutex with a unique storage address.
157unsafe impl crate::RawLock for RawMutex {
158    type LockEntry = LockEntryStorage;
159    type DefaultPolicy = RawMutexPolicy;
160
161    #[inline]
162    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
163        // SAFETY: The closure initializes the provided slot in place before returning Ok(()).
164        unsafe {
165            pin_init_from_closure(move |slot: *mut Self| {
166                slot.write(Self::const_init(class_id));
167                Ok(())
168            })
169        }
170    }
171
172    #[inline]
173    fn as_mut_ptr(&self) -> *mut c_void {
174        self as *const Self as *mut Self as *mut c_void
175    }
176}
177
178/// Opaque layout block matching the Zircon C++ CriticalMutex exactly.
179#[pin_data(PinnedDrop)]
180#[repr(C)]
181pub struct RawCriticalMutex {
182    #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
183    class_id: *const c_void,
184    storage: RawMutexStorage,
185}
186
187impl RawCriticalMutex {
188    pub const INIT: Self = Self::const_init(core::ptr::null());
189
190    /// Statically initializes a RawCriticalMutex in constant context.
191    pub const fn const_init(_class_id: *const c_void) -> Self {
192        Self {
193            #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
194            class_id: _class_id,
195            storage: RawMutexStorage(zr::OpaqueBytes::new(make_mutex_storage())),
196        }
197    }
198
199    /// Is the mutex contested i.e. is at least one thread blocked waiting on it?
200    ///
201    /// The contested flag does not track threads which are spin-waiting on the
202    /// Mutex and have yet to enter a blocking phase.
203    #[inline]
204    pub fn is_contested(&self) -> bool {
205        // SAFETY: The FFI call is safe because the lock is initialized
206        unsafe { cpp_critical_mutex_is_contested(self.as_mut_ptr()) }
207    }
208
209    /// Returns a slice over the raw critical mutex storage bytes.
210    #[cfg(any(test, ktest))]
211    #[inline]
212    pub fn raw_storage_slice(&self) -> &[u8] {
213        // SAFETY: The storage is valid and allocated with RAW_MUTEX_SIZE bytes.
214        unsafe { core::slice::from_raw_parts(self.storage.0.get() as *const u8, RAW_MUTEX_SIZE) }
215    }
216}
217
218impl Default for RawCriticalMutex {
219    fn default() -> Self {
220        Self::const_init(core::ptr::null())
221    }
222}
223
224// SAFETY: RawCriticalMutex is safe to share and access across threads.
225unsafe impl Sync for RawCriticalMutex {}
226// SAFETY: RawCriticalMutex is safe to send across threads.
227unsafe impl Send for RawCriticalMutex {}
228
229zr::unsafe_pinned_drop_ffi!(RawCriticalMutex, cpp_critical_mutex_destroy);
230
231pub struct RawCriticalMutexPolicy;
232
233// SAFETY: `cpp_critical_mutex_acquire` and `cpp_critical_mutex_release` enforce mutual exclusion
234// on `RawCriticalMutex`.
235unsafe impl crate::LockPolicy<RawCriticalMutex> for RawCriticalMutexPolicy {
236    type AcquireArgs = ();
237    type GuardState = bool;
238
239    #[inline]
240    unsafe fn acquire(
241        lock: &RawCriticalMutex,
242        entry: *mut LockEntryStorage,
243        _args: (),
244    ) -> Self::GuardState {
245        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
246        // that `entry` points to valid storage for a lockdep entry.
247        unsafe { cpp_critical_mutex_acquire(lock.as_mut_ptr(), entry as *mut c_void) }
248    }
249
250    #[inline]
251    unsafe fn reacquire(
252        lock: &RawCriticalMutex,
253        entry: *mut LockEntryStorage,
254        state: &mut Self::GuardState,
255    ) {
256        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
257        *state = unsafe { Self::acquire(lock, entry, ()) };
258    }
259
260    #[inline]
261    unsafe fn release(
262        lock: &RawCriticalMutex,
263        entry: *mut LockEntryStorage,
264        should_clear: Self::GuardState,
265    ) {
266        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
267        // that `entry` points to valid storage for a lockdep entry.
268        unsafe {
269            cpp_critical_mutex_release(lock.as_mut_ptr(), entry as *mut c_void, should_clear);
270        }
271    }
272}
273
274// SAFETY: `RawCriticalMutex` is a physical kernel critical mutex with a unique storage address.
275unsafe impl crate::RawLock for RawCriticalMutex {
276    type LockEntry = LockEntryStorage;
277    type DefaultPolicy = RawCriticalMutexPolicy;
278
279    #[inline]
280    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
281        // SAFETY: The closure initializes the provided slot in place before returning Ok(()).
282        unsafe {
283            pin_init_from_closure(move |slot: *mut Self| {
284                slot.write(Self::const_init(class_id));
285                Ok(())
286            })
287        }
288    }
289
290    #[inline]
291    fn as_mut_ptr(&self) -> *mut c_void {
292        self as *const Self as *mut Self as *mut c_void
293    }
294}
295
296const _: () = {
297    const HAS_NAME_STORAGE: bool =
298        cfg!(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"));
299    #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
300    const EXPECTED_SIZE: usize = if HAS_NAME_STORAGE { 40 } else { 32 };
301    #[cfg(not(any(feature = "lock_dep", feature = "lock_metadata_only")))]
302    const EXPECTED_SIZE: usize = if HAS_NAME_STORAGE { 32 } else { 24 };
303
304    assert!(core::mem::size_of::<RawMutex>() == EXPECTED_SIZE);
305    assert!(core::mem::align_of::<RawMutex>() == 8);
306
307    assert!(core::mem::size_of::<RawCriticalMutex>() == EXPECTED_SIZE);
308    assert!(core::mem::align_of::<RawCriticalMutex>() == 8);
309};