Skip to content

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

Merged
merged 1 commit into from
Mar 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion drivers/char/rust_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
86 changes: 86 additions & 0 deletions rust/kernel/sync/guard.rs
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>;
}
59 changes: 59 additions & 0 deletions rust/kernel/sync/mod.rs
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move the example into mutex.rs, i.e. so that we have a different one for condvar, spinlock, etc.

Not sure if it is best to also move each of them to mutex_init!, rather than have them in the module doc, hmm... I will think about it, but we can move it later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move it to mutex.rs, would it be visible in the rendered docs? (Given that is gets 'exported' via pub use.)

Copy link
Member

Choose a reason for hiding this comment

The 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() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for wrapping into a fn test() { in examples, i.e. the code can go directly in "file" scope.

//! // 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);
}
101 changes: 101 additions & 0 deletions rust/kernel/sync/mutex.rs
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.
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
}
}