302 lines
8.4 KiB
Rust
302 lines
8.4 KiB
Rust
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
||
|
// Copyright (C) 2024 Google LLC.
|
||
|
|
||
|
//! Support for defining statics containing locks.
|
||
|
|
||
|
use crate::{
|
||
|
str::CStr,
|
||
|
sync::lock::{Backend, Guard, Lock},
|
||
|
sync::{LockClassKey, LockedBy},
|
||
|
types::Opaque,
|
||
|
};
|
||
|
use core::{
|
||
|
cell::UnsafeCell,
|
||
|
marker::{PhantomData, PhantomPinned},
|
||
|
};
|
||
|
|
||
|
/// Trait implemented for marker types for global locks.
|
||
|
///
|
||
|
/// See [`global_lock!`] for examples.
|
||
|
pub trait GlobalLockBackend {
|
||
|
/// The name for this global lock.
|
||
|
const NAME: &'static CStr;
|
||
|
/// Item type stored in this global lock.
|
||
|
type Item: 'static;
|
||
|
/// The backend used for this global lock.
|
||
|
type Backend: Backend + 'static;
|
||
|
/// The class for this global lock.
|
||
|
fn get_lock_class() -> &'static LockClassKey;
|
||
|
}
|
||
|
|
||
|
/// Type used for global locks.
|
||
|
///
|
||
|
/// See [`global_lock!`] for examples.
|
||
|
pub struct GlobalLock<B: GlobalLockBackend> {
|
||
|
inner: Lock<B::Item, B::Backend>,
|
||
|
}
|
||
|
|
||
|
impl<B: GlobalLockBackend> GlobalLock<B> {
|
||
|
/// Creates a global lock.
|
||
|
///
|
||
|
/// # Safety
|
||
|
///
|
||
|
/// * Before any other method on this lock is called, [`Self::init`] must be called.
|
||
|
/// * The type `B` must not be used with any other lock.
|
||
|
pub const unsafe fn new(data: B::Item) -> Self {
|
||
|
Self {
|
||
|
inner: Lock {
|
||
|
state: Opaque::uninit(),
|
||
|
data: UnsafeCell::new(data),
|
||
|
_pin: PhantomPinned,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Initializes a global lock.
|
||
|
///
|
||
|
/// # Safety
|
||
|
///
|
||
|
/// Must not be called more than once on a given lock.
|
||
|
pub unsafe fn init(&'static self) {
|
||
|
// SAFETY: The pointer to `state` is valid for the duration of this call, and both `name`
|
||
|
// and `key` are valid indefinitely. The `state` is pinned since we have a `'static`
|
||
|
// reference to `self`.
|
||
|
//
|
||
|
// We have exclusive access to the `state` since the caller of `new` promised to call
|
||
|
// `init` before using any other methods. As `init` can only be called once, all other
|
||
|
// uses of this lock must happen after this call.
|
||
|
unsafe {
|
||
|
B::Backend::init(
|
||
|
self.inner.state.get(),
|
||
|
B::NAME.as_char_ptr(),
|
||
|
B::get_lock_class().as_ptr(),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Lock this global lock.
|
||
|
pub fn lock(&'static self) -> GlobalGuard<B> {
|
||
|
GlobalGuard {
|
||
|
inner: self.inner.lock(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Try to lock this global lock.
|
||
|
pub fn try_lock(&'static self) -> Option<GlobalGuard<B>> {
|
||
|
Some(GlobalGuard {
|
||
|
inner: self.inner.try_lock()?,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// A guard for a [`GlobalLock`].
|
||
|
///
|
||
|
/// See [`global_lock!`] for examples.
|
||
|
pub struct GlobalGuard<B: GlobalLockBackend> {
|
||
|
inner: Guard<'static, B::Item, B::Backend>,
|
||
|
}
|
||
|
|
||
|
impl<B: GlobalLockBackend> core::ops::Deref for GlobalGuard<B> {
|
||
|
type Target = B::Item;
|
||
|
|
||
|
fn deref(&self) -> &Self::Target {
|
||
|
&self.inner
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<B: GlobalLockBackend> core::ops::DerefMut for GlobalGuard<B> {
|
||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||
|
&mut self.inner
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// A version of [`LockedBy`] for a [`GlobalLock`].
|
||
|
///
|
||
|
/// See [`global_lock!`] for examples.
|
||
|
pub struct GlobalLockedBy<T: ?Sized, B: GlobalLockBackend> {
|
||
|
_backend: PhantomData<B>,
|
||
|
value: UnsafeCell<T>,
|
||
|
}
|
||
|
|
||
|
// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`.
|
||
|
unsafe impl<T, B> Send for GlobalLockedBy<T, B>
|
||
|
where
|
||
|
T: ?Sized,
|
||
|
B: GlobalLockBackend,
|
||
|
LockedBy<T, B::Item>: Send,
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`.
|
||
|
unsafe impl<T, B> Sync for GlobalLockedBy<T, B>
|
||
|
where
|
||
|
T: ?Sized,
|
||
|
B: GlobalLockBackend,
|
||
|
LockedBy<T, B::Item>: Sync,
|
||
|
{
|
||
|
}
|
||
|
|
||
|
impl<T, B: GlobalLockBackend> GlobalLockedBy<T, B> {
|
||
|
/// Create a new [`GlobalLockedBy`].
|
||
|
///
|
||
|
/// The provided value will be protected by the global lock indicated by `B`.
|
||
|
pub fn new(val: T) -> Self {
|
||
|
Self {
|
||
|
value: UnsafeCell::new(val),
|
||
|
_backend: PhantomData,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<T: ?Sized, B: GlobalLockBackend> GlobalLockedBy<T, B> {
|
||
|
/// Access the value immutably.
|
||
|
///
|
||
|
/// The caller must prove shared access to the lock.
|
||
|
pub fn as_ref<'a>(&'a self, _guard: &'a GlobalGuard<B>) -> &'a T {
|
||
|
// SAFETY: The lock is globally unique, so there can only be one guard.
|
||
|
unsafe { &*self.value.get() }
|
||
|
}
|
||
|
|
||
|
/// Access the value mutably.
|
||
|
///
|
||
|
/// The caller must prove shared exclusive to the lock.
|
||
|
pub fn as_mut<'a>(&'a self, _guard: &'a mut GlobalGuard<B>) -> &'a mut T {
|
||
|
// SAFETY: The lock is globally unique, so there can only be one guard.
|
||
|
unsafe { &mut *self.value.get() }
|
||
|
}
|
||
|
|
||
|
/// Access the value mutably directly.
|
||
|
///
|
||
|
/// The caller has exclusive access to this `GlobalLockedBy`, so they do not need to hold the
|
||
|
/// lock.
|
||
|
pub fn get_mut(&mut self) -> &mut T {
|
||
|
self.value.get_mut()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Defines a global lock.
|
||
|
///
|
||
|
/// The global mutex must be initialized before first use. Usually this is done by calling
|
||
|
/// [`GlobalLock::init`] in the module initializer.
|
||
|
///
|
||
|
/// # Examples
|
||
|
///
|
||
|
/// A global counter:
|
||
|
///
|
||
|
/// ```
|
||
|
/// # mod ex {
|
||
|
/// # use kernel::prelude::*;
|
||
|
/// kernel::sync::global_lock! {
|
||
|
/// // SAFETY: Initialized in module initializer before first use.
|
||
|
/// unsafe(uninit) static MY_COUNTER: Mutex<u32> = 0;
|
||
|
/// }
|
||
|
///
|
||
|
/// fn increment_counter() -> u32 {
|
||
|
/// let mut guard = MY_COUNTER.lock();
|
||
|
/// *guard += 1;
|
||
|
/// *guard
|
||
|
/// }
|
||
|
///
|
||
|
/// impl kernel::Module for MyModule {
|
||
|
/// fn init(_module: &'static ThisModule) -> Result<Self> {
|
||
|
/// // SAFETY: Called exactly once.
|
||
|
/// unsafe { MY_COUNTER.init() };
|
||
|
///
|
||
|
/// Ok(MyModule {})
|
||
|
/// }
|
||
|
/// }
|
||
|
/// # struct MyModule {}
|
||
|
/// # }
|
||
|
/// ```
|
||
|
///
|
||
|
/// A global mutex used to protect all instances of a given struct:
|
||
|
///
|
||
|
/// ```
|
||
|
/// # mod ex {
|
||
|
/// # use kernel::prelude::*;
|
||
|
/// use kernel::sync::{GlobalGuard, GlobalLockedBy};
|
||
|
///
|
||
|
/// kernel::sync::global_lock! {
|
||
|
/// // SAFETY: Initialized in module initializer before first use.
|
||
|
/// unsafe(uninit) static MY_MUTEX: Mutex<()> = ();
|
||
|
/// }
|
||
|
///
|
||
|
/// /// All instances of this struct are protected by `MY_MUTEX`.
|
||
|
/// struct MyStruct {
|
||
|
/// my_counter: GlobalLockedBy<u32, MY_MUTEX>,
|
||
|
/// }
|
||
|
///
|
||
|
/// impl MyStruct {
|
||
|
/// /// Increment the counter in this instance.
|
||
|
/// ///
|
||
|
/// /// The caller must hold the `MY_MUTEX` mutex.
|
||
|
/// fn increment(&self, guard: &mut GlobalGuard<MY_MUTEX>) -> u32 {
|
||
|
/// let my_counter = self.my_counter.as_mut(guard);
|
||
|
/// *my_counter += 1;
|
||
|
/// *my_counter
|
||
|
/// }
|
||
|
/// }
|
||
|
///
|
||
|
/// impl kernel::Module for MyModule {
|
||
|
/// fn init(_module: &'static ThisModule) -> Result<Self> {
|
||
|
/// // SAFETY: Called exactly once.
|
||
|
/// unsafe { MY_MUTEX.init() };
|
||
|
///
|
||
|
/// Ok(MyModule {})
|
||
|
/// }
|
||
|
/// }
|
||
|
/// # struct MyModule {}
|
||
|
/// # }
|
||
|
/// ```
|
||
|
#[macro_export]
|
||
|
macro_rules! global_lock {
|
||
|
{
|
||
|
$(#[$meta:meta])* $pub:vis
|
||
|
unsafe(uninit) static $name:ident: $kind:ident<$valuety:ty> = $value:expr;
|
||
|
} => {
|
||
|
#[doc = ::core::concat!(
|
||
|
"Backend type used by [`",
|
||
|
::core::stringify!($name),
|
||
|
"`](static@",
|
||
|
::core::stringify!($name),
|
||
|
")."
|
||
|
)]
|
||
|
#[allow(non_camel_case_types, unreachable_pub)]
|
||
|
$pub enum $name {}
|
||
|
|
||
|
impl $crate::sync::lock::GlobalLockBackend for $name {
|
||
|
const NAME: &'static $crate::str::CStr = $crate::c_str!(::core::stringify!($name));
|
||
|
type Item = $valuety;
|
||
|
type Backend = $crate::global_lock_inner!(backend $kind);
|
||
|
|
||
|
fn get_lock_class() -> &'static $crate::sync::LockClassKey {
|
||
|
$crate::static_lock_class!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$(#[$meta])*
|
||
|
$pub static $name: $crate::sync::lock::GlobalLock<$name> = {
|
||
|
// Defined here to be outside the unsafe scope.
|
||
|
let init: $valuety = $value;
|
||
|
|
||
|
// SAFETY:
|
||
|
// * The user of this macro promises to initialize the macro before use.
|
||
|
// * We are only generating one static with this backend type.
|
||
|
unsafe { $crate::sync::lock::GlobalLock::new(init) }
|
||
|
};
|
||
|
};
|
||
|
}
|
||
|
pub use global_lock;
|
||
|
|
||
|
#[doc(hidden)]
|
||
|
#[macro_export]
|
||
|
macro_rules! global_lock_inner {
|
||
|
(backend Mutex) => {
|
||
|
$crate::sync::lock::mutex::MutexBackend
|
||
|
};
|
||
|
(backend SpinLock) => {
|
||
|
$crate::sync::lock::spinlock::SpinLockBackend
|
||
|
};
|
||
|
}
|