diff --git a/drivers/char/rust_example.rs b/drivers/char/rust_example.rs index 91ba6bc353a987..d24480798b2a23 100644 --- a/drivers/char/rust_example.rs +++ b/drivers/char/rust_example.rs @@ -9,7 +9,7 @@ use alloc::boxed::Box; use core::pin::Pin; use kernel::prelude::*; -use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev}; +use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev, mutex_init, sync::Mutex}; module! { type: RustExample, @@ -74,6 +74,15 @@ impl KernelModule for RustExample { println!(" my_usize: {}", my_usize.read(&lock)); } + // Test mutexes. + { + // SAFETY: `init` is called below. + let data = Pin::from(Box::try_new(unsafe { Mutex::new(0) })?); + mutex_init!(data.as_ref(), "RustExample::init::data"); + *data.lock() = 10; + println!("Value: {}", *data.lock()); + } + // Including this large variable on the stack will trigger // stack probing on the supported archs. // This will verify that stack probing does not lead to diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index c1f16e0b25bdc4..95da8c33d2caf7 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -41,6 +41,7 @@ pub mod prelude; pub mod printk; pub mod random; mod static_assert; +pub mod sync; #[cfg(CONFIG_SYSCTL)] pub mod sysctl; diff --git a/rust/kernel/sync/guard.rs b/rust/kernel/sync/guard.rs new file mode 100644 index 00000000000000..dac1fe2516f79f --- /dev/null +++ b/rust/kernel/sync/guard.rs @@ -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 Sync for Guard<'_, L> +where + L: Lock + ?Sized, + L::Inner: Sync, +{ +} + +impl 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 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 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; +} diff --git a/rust/kernel/sync/mod.rs b/rust/kernel/sync/mod.rs new file mode 100644 index 00000000000000..c1bf72bcbb0e15 --- /dev/null +++ b/rust/kernel/sync/mod.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! 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 +//! +//!``` +//! fn test() { +//! // 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); +} diff --git a/rust/kernel/sync/mutex.rs b/rust/kernel/sync/mutex.rs new file mode 100644 index 00000000000000..a040861da0a024 --- /dev/null +++ b/rust/kernel/sync/mutex.rs @@ -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 { + /// The kernel `struct mutex` object. + mutex: UnsafeCell, + + /// 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, +} + +// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can. +unsafe impl Send for Mutex {} + +// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the +// data it protects is `Send`. +unsafe impl Sync for Mutex {} + +impl Mutex { + /// 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 Mutex { + /// 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.lock_noguard(); + // SAFETY: The mutex was just acquired. + unsafe { Guard::new(self) } + } +} + +impl NeedsLockClass for Mutex { + 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 Lock for Mutex { + 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 { + &self.data + } +}