blob: 8340fa56f0fb83bb65a5b6be62f7d1cfe1ab0d2b [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use core::ffi::c_void;
use ksync::{ExclusiveLockPolicy, LockPolicy, LockdepTracked, RawLock, RawSharedLock};
use pin_init::{PinInit, pin_data};
unsafe extern "C" {
fn cpp_brwlock_pi_init(lock: *mut c_void, class_id: *const c_void);
fn cpp_brwlock_pi_destroy(lock: *mut c_void);
fn cpp_brwlock_pi_acquire_read(lock: *mut c_void);
fn cpp_brwlock_pi_release_read(lock: *mut c_void);
fn cpp_brwlock_pi_acquire_write(lock: *mut c_void);
fn cpp_brwlock_pi_release_write(lock: *mut c_void);
fn cpp_brwlock_no_pi_init(lock: *mut c_void, class_id: *const c_void);
fn cpp_brwlock_no_pi_destroy(lock: *mut c_void);
fn cpp_brwlock_no_pi_acquire_read(lock: *mut c_void);
fn cpp_brwlock_no_pi_release_read(lock: *mut c_void);
fn cpp_brwlock_no_pi_acquire_write(lock: *mut c_void);
fn cpp_brwlock_no_pi_release_write(lock: *mut c_void);
}
/// Size in bytes of the C++ `BrwLockNoPi` object.
const BRWLOCK_NO_PI_STORAGE_SIZE: usize =
if crate::kernel::spin_tracing::RWLOCK_NAME_TRACING_ENABLED { 80 } else { 72 };
/// Opaque layout block matching the Zircon C++ `BrwLockPi` object.
#[cfg(not(target_arch = "riscv64"))]
#[pin_data(PinnedDrop)]
#[repr(C, align(16))]
pub struct RawBrwLockPiStorage {
#[pin]
storage: zr::OpaqueBytes<128>,
}
/// Opaque layout block matching the Zircon C++ `BrwLockPi` object.
#[cfg(target_arch = "riscv64")]
#[pin_data(PinnedDrop)]
#[repr(C, align(8))]
pub struct RawBrwLockPiStorage {
#[pin]
storage: zr::OpaqueBytes<BRWLOCK_NO_PI_STORAGE_SIZE>,
}
/// Opaque layout block matching the Zircon C++ `BrwLockNoPi` object.
#[pin_data(PinnedDrop)]
#[repr(C, align(8))]
pub struct RawBrwLockNoPiStorage {
#[pin]
storage: zr::OpaqueBytes<BRWLOCK_NO_PI_STORAGE_SIZE>,
}
macro_rules! impl_raw_brwlock {
(
storage: $storage:ident,
read_policy: $read_policy:ident,
write_policy: $write_policy:ident,
tracked: $tracked:ident,
cpp_name: $cpp_name:literal,
ffi_init: $ffi_init:ident,
ffi_destroy: $ffi_destroy:ident,
ffi_acquire_read: $ffi_acquire_read:ident,
ffi_release_read: $ffi_release_read:ident,
ffi_acquire_write: $ffi_acquire_write:ident,
ffi_release_write: $ffi_release_write:ident $(,)?
) => {
// SAFETY: `$storage` is safe to share and access across threads because it delegates to
// the thread-safe Zircon kernel `$cpp_name` implementation.
unsafe impl Sync for $storage {}
// SAFETY: `$storage` is safe to send across threads because it delegates to the
// thread-safe Zircon kernel `$cpp_name` implementation.
unsafe impl Send for $storage {}
// SAFETY: `$storage` can only be constructed in-place via `RawLock::init` (`$ffi_init`)
// and is `!Unpin`, and all read/write guards borrow the lock for their lifetime, so
// `$ffi_destroy` is called on a live, unheld C++ lock.
zr::unsafe_pinned_drop_ffi!($storage, $ffi_destroy);
#[doc = concat!("Policy for acquiring and releasing [`", stringify!($storage), "`]")]
/// in shared (read) mode.
pub struct $read_policy;
// SAFETY: `$ffi_acquire_read` and `$ffi_release_read` enforce shared reader
// synchronization on `$storage`.
unsafe impl LockPolicy<$storage> for $read_policy {
type AcquireArgs = ();
type GuardState = ();
/// Acquires `lock` in shared (read) mode.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::acquire`] contract.
#[inline]
unsafe fn acquire(lock: &$storage, _state: &mut Self::GuardState, _args: ()) {
// SAFETY: Every `&$storage` was initialized in-place by `RawLock::init` and
// remains pinned because `$storage` is `!Unpin`.
unsafe {
$ffi_acquire_read(lock.as_mut_ptr());
}
}
/// Re-acquires `lock` in shared (read) mode after a temporary release.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::reacquire`] contract.
#[inline]
unsafe fn reacquire(lock: &$storage, state: &mut Self::GuardState) {
// SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
unsafe {
Self::acquire(lock, state, ());
}
}
/// Releases `lock` from shared (read) mode.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::release`] contract (`lock` must be currently
/// held in shared (read) mode by the calling thread).
#[inline]
unsafe fn release(lock: &$storage, _state: &mut Self::GuardState) {
// SAFETY: Every `&$storage` is an initialized, pinned lock, and the caller
// guarantees the current thread holds the read lock.
unsafe {
$ffi_release_read(lock.as_mut_ptr());
}
}
}
#[doc = concat!("Policy for acquiring and releasing [`", stringify!($storage), "`]")]
/// in exclusive (write) mode.
pub struct $write_policy;
// SAFETY: `$ffi_acquire_write` and `$ffi_release_write` enforce exclusive mutual
// exclusion on `$storage`.
unsafe impl LockPolicy<$storage> for $write_policy {
type AcquireArgs = ();
type GuardState = ();
/// Acquires `lock` in exclusive (write) mode.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::acquire`] contract.
#[inline]
unsafe fn acquire(lock: &$storage, _state: &mut Self::GuardState, _args: ()) {
// SAFETY: Every `&$storage` was initialized in-place by `RawLock::init` and
// remains pinned because `$storage` is `!Unpin`.
unsafe {
$ffi_acquire_write(lock.as_mut_ptr());
}
}
/// Re-acquires `lock` in exclusive (write) mode after a temporary release.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::reacquire`] contract.
#[inline]
unsafe fn reacquire(lock: &$storage, state: &mut Self::GuardState) {
// SAFETY: Caller upholds the `LockPolicy::reacquire` contract.
unsafe {
Self::acquire(lock, state, ());
}
}
/// Releases `lock` from exclusive (write) mode.
///
/// # Safety
///
/// Caller must uphold the [`LockPolicy::release`] contract (`lock` must be currently
/// held in exclusive (write) mode by the calling thread).
#[inline]
unsafe fn release(lock: &$storage, _state: &mut Self::GuardState) {
// SAFETY: Every `&$storage` is an initialized, pinned lock, and the caller
// guarantees the current thread holds the write lock.
unsafe {
$ffi_release_write(lock.as_mut_ptr());
}
}
}
// SAFETY: `$write_policy` enforces exclusive mutual exclusion on `$storage`.
unsafe impl ExclusiveLockPolicy<$storage> for $write_policy {}
// SAFETY: `$storage` is a physical kernel rwlock with a unique storage address.
unsafe impl RawLock for $storage {
type DefaultPolicy = $write_policy;
/// Returns a pinned initializer for `Self` with the given lockdep `class_id`.
///
/// # Safety
///
/// `class_id` must be either null or a valid static `lockdep::LockClassId` pointer.
#[inline]
unsafe fn init(
class_id: *const c_void,
) -> impl PinInit<Self, core::convert::Infallible> {
// SAFETY: `Self` has the same size and alignment as the C++ lock (enforced by
// compile-time assertions), `$ffi_init` placement-new initializes it in `slot`,
// and the caller guarantees `class_id` is null or a valid static
// `lockdep::LockClassId`.
zr::pin_init_ffi!($ffi_init, class_id)
}
#[inline]
fn as_mut_ptr(&self) -> *mut c_void {
self.storage.get().cast::<c_void>()
}
}
// SAFETY: `$read_policy` acquires `$storage` in shared (read) mode, which allows multiple
// concurrent readers but excludes the exclusive (write) mode acquired by `$write_policy`,
// the default and only `ExclusiveLockPolicy` for `$storage`.
unsafe impl RawSharedLock for $storage {
type SharedPolicy = $read_policy;
}
#[doc = concat!("Lockdep-tracked kernel `", $cpp_name, "`")]
#[doc = concat!("matching the Zircon C++ `Lock<", $cpp_name, ">` layout.")]
pub type $tracked = LockdepTracked<$storage>;
};
}
impl_raw_brwlock! {
storage: RawBrwLockPiStorage,
read_policy: RawBrwLockPiReadPolicy,
write_policy: RawBrwLockPiWritePolicy,
tracked: RawBrwLockPi,
cpp_name: "BrwLockPi",
ffi_init: cpp_brwlock_pi_init,
ffi_destroy: cpp_brwlock_pi_destroy,
ffi_acquire_read: cpp_brwlock_pi_acquire_read,
ffi_release_read: cpp_brwlock_pi_release_read,
ffi_acquire_write: cpp_brwlock_pi_acquire_write,
ffi_release_write: cpp_brwlock_pi_release_write,
}
impl_raw_brwlock! {
storage: RawBrwLockNoPiStorage,
read_policy: RawBrwLockNoPiReadPolicy,
write_policy: RawBrwLockNoPiWritePolicy,
tracked: RawBrwLockNoPi,
cpp_name: "BrwLockNoPi",
ffi_init: cpp_brwlock_no_pi_init,
ffi_destroy: cpp_brwlock_no_pi_destroy,
ffi_acquire_read: cpp_brwlock_no_pi_acquire_read,
ffi_release_read: cpp_brwlock_no_pi_release_read,
ffi_acquire_write: cpp_brwlock_no_pi_acquire_write,
ffi_release_write: cpp_brwlock_no_pi_release_write,
}
#[cfg(all(not(target_arch = "riscv64"), not(with_lock_dep)))]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockPi>() == 128);
assert!(core::mem::align_of::<RawBrwLockPi>() == 16);
};
#[cfg(all(not(target_arch = "riscv64"), with_lock_dep))]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockPi>() == 144);
assert!(core::mem::align_of::<RawBrwLockPi>() == 16);
};
#[cfg(all(target_arch = "riscv64", not(with_lock_dep)))]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockPi>() == BRWLOCK_NO_PI_STORAGE_SIZE);
assert!(core::mem::align_of::<RawBrwLockPi>() == 8);
};
#[cfg(all(target_arch = "riscv64", with_lock_dep))]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockPi>() == BRWLOCK_NO_PI_STORAGE_SIZE + 8);
assert!(core::mem::align_of::<RawBrwLockPi>() == 8);
};
#[cfg(not(with_lock_dep))]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockNoPi>() == BRWLOCK_NO_PI_STORAGE_SIZE);
assert!(core::mem::align_of::<RawBrwLockNoPi>() == 8);
};
#[cfg(with_lock_dep)]
const _: () = {
assert!(core::mem::size_of::<RawBrwLockNoPi>() == BRWLOCK_NO_PI_STORAGE_SIZE + 8);
assert!(core::mem::align_of::<RawBrwLockNoPi>() == 8);
};