-
Notifications
You must be signed in to change notification settings - Fork 463
Add sync
module with a mutex implementation.
#102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! A generic lock guard and trait. | ||
//! | ||
//! This module contains a lock guard that can be used with any locking primitive that implements | ||
//! the ([`Lock`]) trait. It also contains the definition of the trait, which can be leveraged by | ||
//! other constructs to work on generic locking primitives. | ||
|
||
/// Allows mutual exclusion primitives that implement the [`Lock`] trait to automatically unlock | ||
/// when a guard goes out of scope. It also provides a safe and convenient way to access the data | ||
/// protected by the lock. | ||
#[must_use = "the lock unlocks immediately when the guard is unused"] | ||
pub struct Guard<'a, L: Lock + ?Sized> { | ||
pub(crate) lock: &'a L, | ||
} | ||
|
||
// SAFETY: `Guard` is sync when the data protected by the lock is also sync. This is more | ||
// conservative than the default compiler implementation; more details can be found on | ||
// https://github.com/rust-lang/rust/issues/41622 -- it refers to `MutexGuard` from the standard | ||
// library. | ||
unsafe impl<L> Sync for Guard<'_, L> | ||
where | ||
L: Lock + ?Sized, | ||
L::Inner: Sync, | ||
{ | ||
} | ||
|
||
impl<L: Lock + ?Sized> core::ops::Deref for Guard<'_, L> { | ||
type Target = L::Inner; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
// SAFETY: The caller owns the lock, so it is safe to deref the protected data. | ||
unsafe { &*self.lock.locked_data().get() } | ||
} | ||
} | ||
|
||
impl<L: Lock + ?Sized> core::ops::DerefMut for Guard<'_, L> { | ||
fn deref_mut(&mut self) -> &mut L::Inner { | ||
// SAFETY: The caller owns the lock, so it is safe to deref the protected data. | ||
unsafe { &mut *self.lock.locked_data().get() } | ||
} | ||
} | ||
|
||
impl<L: Lock + ?Sized> Drop for Guard<'_, L> { | ||
fn drop(&mut self) { | ||
// SAFETY: The caller owns the lock, so it is safe to unlock it. | ||
unsafe { self.lock.unlock() }; | ||
} | ||
} | ||
|
||
impl<'a, L: Lock + ?Sized> Guard<'a, L> { | ||
/// Constructs a new lock guard. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The caller must ensure that it owns the lock. | ||
pub(crate) unsafe fn new(lock: &'a L) -> Self { | ||
Self { lock } | ||
} | ||
} | ||
|
||
/// A generic mutual exclusion primitive. | ||
/// | ||
/// [`Guard`] is written such that any mutual exclusion primitive that can implement this trait can | ||
/// also benefit from having an automatic way to unlock itself. | ||
pub trait Lock { | ||
/// The type of the data protected by the lock. | ||
type Inner: ?Sized; | ||
|
||
/// Acquires the lock, making the caller its owner. | ||
fn lock_noguard(&self); | ||
|
||
/// Releases the lock, giving up ownership of the lock. | ||
/// | ||
/// # Safety | ||
/// | ||
/// It must only be called by the current owner of the lock. | ||
unsafe fn unlock(&self); | ||
|
||
/// Returns the data protected by the lock. | ||
/// | ||
/// # Safety | ||
/// | ||
/// It must only be called by the current owner of the lock. | ||
unsafe fn locked_data(&self) -> &core::cell::UnsafeCell<Self::Inner>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
wedsonaf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//! Synchronisation primitives. | ||
//! | ||
//! This module contains the kernel APIs related to synchronisation that have been ported or | ||
//! wrapped for usage by Rust code in the kernel and is shared by all of them. | ||
//! | ||
//! # Example | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move the example into Not sure if it is best to also move each of them to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we move it to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an attribute for pub use, to render the doc in place, so yes we can rerender it as it would be in this file. |
||
//! | ||
//!``` | ||
//! fn test() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for wrapping into a |
||
//! // SAFETY: `init` is called below. | ||
//! let data = alloc::sync::Arc::pin(unsafe{ Mutex::new(0) }); | ||
//! mutex_init!(data.as_ref(), "test::data"); | ||
//! *data.lock() = 10; | ||
//! kernel::println!("{}", *data.lock()); | ||
//! } | ||
//! ``` | ||
|
||
use crate::{bindings, CStr}; | ||
use core::pin::Pin; | ||
|
||
mod guard; | ||
mod mutex; | ||
|
||
pub use guard::{Guard, Lock}; | ||
pub use mutex::Mutex; | ||
|
||
/// Safely initialises an object that has an `init` function that takes a name and a lock class as | ||
/// arguments, for example, [`Mutex`]. | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! init_with_lockdep { | ||
($obj:expr, $name:literal) => {{ | ||
static mut CLASS: core::mem::MaybeUninit<$crate::bindings::lock_class_key> = | ||
core::mem::MaybeUninit::uninit(); | ||
// SAFETY: `CLASS` is never used by Rust code directly; the kernel may change it though. | ||
#[allow(unused_unsafe)] | ||
unsafe { | ||
$crate::sync::NeedsLockClass::init($obj, $crate::cstr!($name), CLASS.as_mut_ptr()) | ||
}; | ||
}}; | ||
} | ||
|
||
/// A trait for types that need a lock class during initialisation. | ||
/// | ||
/// Implementers of this trait benefit from the [`init_with_lockdep`] macro that generates a new | ||
/// class for each initialisation call site. | ||
pub trait NeedsLockClass { | ||
/// Initialises the type instance so that it can be safely used. | ||
/// | ||
/// Callers are encouraged to use the [`init_with_lockdep`] macro as it automatically creates a | ||
/// new lock class on each usage. | ||
/// | ||
/// # Safety | ||
/// | ||
/// `key` must point to a valid memory location as it will be used by the kernel. | ||
unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! A kernel mutex. | ||
//! | ||
//! This module allows Rust code to use the kernel's [`struct mutex`]. | ||
|
||
use super::{Guard, Lock, NeedsLockClass}; | ||
use crate::{bindings, CStr}; | ||
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin}; | ||
|
||
/// Safely initialises a [`Mutex`] with the given name, generating a new lock class. | ||
#[macro_export] | ||
macro_rules! mutex_init { | ||
($mutex:expr, $name:literal) => { | ||
$crate::init_with_lockdep!($mutex, $name) | ||
}; | ||
} | ||
|
||
/// Exposes the kernel's [`struct mutex`]. When multiple threads attempt to lock the same mutex, | ||
/// only one at a time is allowed to progress, the others will block (sleep) until the mutex is | ||
/// unlocked, at which point another thread will be allowed to wake up and make progress. | ||
/// | ||
/// A [`Mutex`] must first be initialised with a call to [`Mutex::init`] before it can be used. The | ||
/// [`mutex_init`] macro is provided to automatically assign a new lock class to a mutex instance. | ||
/// | ||
/// Since it may block, [`Mutex`] needs to be used with care in atomic contexts. | ||
/// | ||
/// [`struct mutex`]: ../../../include/linux/mutex.h | ||
pub struct Mutex<T: ?Sized> { | ||
/// The kernel `struct mutex` object. | ||
mutex: UnsafeCell<bindings::mutex>, | ||
|
||
/// A mutex needs to be pinned because it contains a [`struct list_head`] that is | ||
/// self-referential, so it cannot be safely moved once it is initialised. | ||
_pin: PhantomPinned, | ||
|
||
/// The data protected by the mutex. | ||
data: UnsafeCell<T>, | ||
} | ||
|
||
// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can. | ||
ojeda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {} | ||
|
||
// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the | ||
// data it protects is `Send`. | ||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {} | ||
|
||
impl<T> Mutex<T> { | ||
/// Constructs a new mutex. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The caller must call [`Mutex::init`] before using the mutex. | ||
pub unsafe fn new(t: T) -> Self { | ||
Self { | ||
mutex: UnsafeCell::new(bindings::mutex::default()), | ||
data: UnsafeCell::new(t), | ||
_pin: PhantomPinned, | ||
} | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Mutex<T> { | ||
/// Locks the mutex and gives the caller access to the data protected by it. Only one thread at | ||
/// a time is allowed to access the protected data. | ||
pub fn lock(&self) -> Guard<Self> { | ||
self.lock_noguard(); | ||
// SAFETY: The mutex was just acquired. | ||
unsafe { Guard::new(self) } | ||
} | ||
} | ||
|
||
impl<T: ?Sized> NeedsLockClass for Mutex<T> { | ||
unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) { | ||
bindings::__mutex_init(self.mutex.get(), name.as_ptr() as _, key); | ||
} | ||
} | ||
|
||
impl<T: ?Sized> Lock for Mutex<T> { | ||
type Inner = T; | ||
|
||
#[cfg(not(CONFIG_DEBUG_LOCK_ALLOC))] | ||
fn lock_noguard(&self) { | ||
// SAFETY: `mutex` points to valid memory. | ||
unsafe { bindings::mutex_lock(self.mutex.get()) }; | ||
} | ||
|
||
#[cfg(CONFIG_DEBUG_LOCK_ALLOC)] | ||
fn lock_noguard(&self) { | ||
// SAFETY: `mutex` points to valid memory. | ||
unsafe { bindings::mutex_lock_nested(self.mutex.get(), 0) }; | ||
} | ||
|
||
unsafe fn unlock(&self) { | ||
bindings::mutex_unlock(self.mutex.get()); | ||
} | ||
|
||
unsafe fn locked_data(&self) -> &UnsafeCell<T> { | ||
&self.data | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.