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::lockdep_tracked::LockdepTracked;
6use crate::raw_lock::{ConstRawLock, ExclusiveLockPolicy, LockPolicy, RawLock};
7use core::ffi::c_void;
8use pin_init::{PinInit, pin_data, pin_init_from_closure};
9
10#[cfg(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"))]
11const RAW_MUTEX_SIZE: usize = 32;
12#[cfg(not(any(feature = "lock_name_tracing", feature = "spin_lock_tracing")))]
13const RAW_MUTEX_SIZE: usize = 24;
14
15unsafe extern "C" {
16    fn cpp_mutex_destroy(lock: *mut c_void);
17    fn cpp_mutex_acquire(lock: *mut c_void);
18    fn cpp_mutex_release(lock: *mut c_void);
19    fn cpp_mutex_is_contested(lock: *mut c_void) -> bool;
20
21    fn cpp_critical_mutex_acquire(lock: *mut c_void) -> bool;
22    fn cpp_critical_mutex_release(lock: *mut c_void, should_clear: bool);
23}
24
25const MUTEX_MAGIC: u32 = 0x6D757478; // 'mutx'
26const INVALID_CPU: u32 = u32::MAX;
27
28const fn make_mutex_storage() -> [u8; RAW_MUTEX_SIZE] {
29    let mut bytes = [0u8; RAW_MUTEX_SIZE];
30    #[cfg(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"))]
31    let offset = 8;
32    #[cfg(not(any(feature = "lock_name_tracing", feature = "spin_lock_tracing")))]
33    let offset = 0;
34
35    let magic_bytes = MUTEX_MAGIC.to_ne_bytes();
36    let cpu_bytes = INVALID_CPU.to_ne_bytes();
37
38    bytes[offset] = magic_bytes[0];
39    bytes[offset + 1] = magic_bytes[1];
40    bytes[offset + 2] = magic_bytes[2];
41    bytes[offset + 3] = magic_bytes[3];
42
43    bytes[offset + 4] = cpu_bytes[0];
44    bytes[offset + 5] = cpu_bytes[1];
45    bytes[offset + 6] = cpu_bytes[2];
46    bytes[offset + 7] = cpu_bytes[3];
47
48    bytes
49}
50
51/// Opaque layout block matching the Zircon C++ `Mutex` (and `CriticalMutex`) object.
52#[pin_data(PinnedDrop)]
53#[repr(C, align(8))]
54pub struct RawMutexStorage {
55    storage: zr::OpaqueBytes<RAW_MUTEX_SIZE>,
56}
57
58impl RawMutexStorage {
59    #[allow(clippy::declare_interior_mutable_const)]
60    pub const INIT: Self = Self::const_init();
61
62    /// Statically initializes a `RawMutexStorage` in constant context.
63    pub const fn const_init() -> Self {
64        Self { storage: zr::OpaqueBytes::new(make_mutex_storage()) }
65    }
66
67    /// Is the mutex contested i.e. is at least one thread blocked waiting on it?
68    ///
69    /// The contested flag does not track threads which are spin-waiting on the
70    /// Mutex and have yet to enter a blocking phase.
71    #[inline]
72    pub fn is_contested(&self) -> bool {
73        // SAFETY: The FFI call is safe because the lock is initialized.
74        unsafe { cpp_mutex_is_contested(self.as_mut_ptr()) }
75    }
76
77    /// Returns a slice over the raw mutex storage bytes.
78    #[cfg(any(test, ktest))]
79    #[inline]
80    pub fn raw_storage_slice(&self) -> &[u8] {
81        // SAFETY: The storage is valid and allocated with RAW_MUTEX_SIZE bytes.
82        unsafe { core::slice::from_raw_parts(self.storage.get() as *const u8, RAW_MUTEX_SIZE) }
83    }
84}
85
86impl Default for RawMutexStorage {
87    fn default() -> Self {
88        Self::INIT
89    }
90}
91
92// SAFETY: RawMutexStorage is safe to share and access across threads.
93unsafe impl Sync for RawMutexStorage {}
94// SAFETY: RawMutexStorage is safe to send across threads.
95unsafe impl Send for RawMutexStorage {}
96
97zr::unsafe_pinned_drop_ffi!(RawMutexStorage, cpp_mutex_destroy);
98
99// SAFETY: `cpp_mutex_acquire` and `cpp_mutex_release` enforce mutual exclusion on
100// `RawMutexStorage`.
101unsafe impl LockPolicy<RawMutexStorage> for RawMutexStorage {
102    type AcquireArgs = ();
103    type GuardState = ();
104
105    #[inline]
106    unsafe fn acquire(lock: &RawMutexStorage, _state: &mut Self::GuardState, _args: ()) {
107        // SAFETY: The FFI call is safe because the lock is initialized.
108        unsafe {
109            cpp_mutex_acquire(lock.as_mut_ptr());
110        }
111    }
112
113    #[inline]
114    unsafe fn reacquire(lock: &RawMutexStorage, state: &mut Self::GuardState) {
115        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
116        unsafe {
117            Self::acquire(lock, state, ());
118        }
119    }
120
121    #[inline]
122    unsafe fn release(lock: &RawMutexStorage, _state: &mut Self::GuardState) {
123        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
124        // thread.
125        unsafe {
126            cpp_mutex_release(lock.as_mut_ptr());
127        }
128    }
129}
130
131// SAFETY: `cpp_mutex_acquire` and `cpp_mutex_release` enforce exclusive mutual exclusion on
132// `RawMutexStorage`.
133unsafe impl ExclusiveLockPolicy<RawMutexStorage> for RawMutexStorage {}
134
135// SAFETY: `RawMutexStorage` is a physical kernel mutex with a unique storage address.
136unsafe impl RawLock for RawMutexStorage {
137    type DefaultPolicy = Self;
138
139    #[inline]
140    unsafe fn init(_class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
141        // SAFETY: The closure initializes the provided slot in place before returning Ok(()).
142        unsafe {
143            pin_init_from_closure(move |slot: *mut Self| {
144                slot.write(Self::const_init());
145                Ok(())
146            })
147        }
148    }
149
150    #[inline]
151    fn as_mut_ptr(&self) -> *mut c_void {
152        self as *const Self as *mut Self as *mut c_void
153    }
154}
155
156impl ConstRawLock for RawMutexStorage {
157    const INIT: Self = Self::INIT;
158}
159
160/// Lock policy for acquiring and releasing a `RawMutexStorage` as a C++ `CriticalMutex`.
161pub struct RawCriticalMutexPolicy;
162
163// SAFETY: `cpp_critical_mutex_acquire` and `cpp_critical_mutex_release` enforce mutual exclusion
164// on `RawMutexStorage`.
165unsafe impl LockPolicy<RawMutexStorage> for RawCriticalMutexPolicy {
166    type AcquireArgs = ();
167    type GuardState = bool;
168
169    #[inline]
170    unsafe fn acquire(lock: &RawMutexStorage, state: &mut Self::GuardState, _args: ()) {
171        // SAFETY: The FFI call is safe because the lock is initialized.
172        *state = unsafe { cpp_critical_mutex_acquire(lock.as_mut_ptr()) };
173    }
174
175    #[inline]
176    unsafe fn reacquire(lock: &RawMutexStorage, state: &mut Self::GuardState) {
177        // SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
178        unsafe {
179            Self::acquire(lock, state, ());
180        }
181    }
182
183    #[inline]
184    unsafe fn release(lock: &RawMutexStorage, should_clear: &mut Self::GuardState) {
185        // SAFETY: The FFI call is safe because the lock is initialized and held by the current
186        // thread.
187        unsafe {
188            cpp_critical_mutex_release(lock.as_mut_ptr(), *should_clear);
189        }
190    }
191}
192
193// SAFETY: `cpp_critical_mutex_acquire` and `cpp_critical_mutex_release` enforce exclusive mutual
194// exclusion on `RawMutexStorage`.
195unsafe impl ExclusiveLockPolicy<RawMutexStorage> for RawCriticalMutexPolicy {}
196
197/// Lockdep-tracked kernel mutex matching the Zircon C++ `Lock<Mutex>` layout.
198pub type RawMutex = LockdepTracked<RawMutexStorage>;
199
200/// Lockdep-tracked kernel critical mutex matching the Zircon C++ `Lock<CriticalMutex>` layout.
201pub type RawCriticalMutex = LockdepTracked<RawMutexStorage, RawCriticalMutexPolicy>;
202
203const _: () = {
204    const HAS_NAME_STORAGE: bool =
205        cfg!(any(feature = "lock_name_tracing", feature = "spin_lock_tracing"));
206    #[cfg(any(feature = "lock_dep", feature = "lock_metadata_only"))]
207    const EXPECTED_SIZE: usize = if HAS_NAME_STORAGE { 40 } else { 32 };
208    #[cfg(not(any(feature = "lock_dep", feature = "lock_metadata_only")))]
209    const EXPECTED_SIZE: usize = if HAS_NAME_STORAGE { 32 } else { 24 };
210
211    assert!(core::mem::size_of::<RawMutex>() == EXPECTED_SIZE);
212    assert!(core::mem::align_of::<RawMutex>() == 8);
213
214    assert!(core::mem::size_of::<RawCriticalMutex>() == EXPECTED_SIZE);
215    assert!(core::mem::align_of::<RawCriticalMutex>() == 8);
216};