Skip to content

Commit

Permalink
Merge pull request torvalds#569 from wedsonaf/revocable-mutex
Browse files Browse the repository at this point in the history
rust: add `RevocableMutex`.
  • Loading branch information
wedsonaf authored Nov 27, 2021
2 parents c6389c5 + 6c9714f commit 0028943
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
2 changes: 2 additions & 0 deletions rust/kernel/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod condvar;
mod guard;
mod locked_by;
mod mutex;
mod revocable_mutex;
mod seqlock;
mod spinlock;

Expand All @@ -36,6 +37,7 @@ pub use condvar::CondVar;
pub use guard::{CreatableLock, Guard, GuardMut, Lock};
pub use locked_by::LockedBy;
pub use mutex::Mutex;
pub use revocable_mutex::{RevocableMutex, RevocableMutexGuard};
pub use seqlock::{SeqLock, SeqLockReadGuard};
pub use spinlock::SpinLock;

Expand Down
184 changes: 184 additions & 0 deletions rust/kernel/sync/revocable_mutex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0

//! A kernel mutex where acccess to contents can be revoked at runtime.

use crate::{
bindings,
str::CStr,
sync::{GuardMut, Mutex, NeedsLockClass},
};
use core::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
pin::Pin,
ptr::drop_in_place,
};

/// The state within a `RevocableMutex` that is protected by a mutex.
///
/// We don't use simply `Option<T>` because we need to drop in-place because the contents are
/// implicitly pinned.
struct RevocableMutexInner<T: ?Sized> {
is_available: bool,
data: ManuallyDrop<T>,
}

/// A mutex whose contents can become inaccessible at runtime.
///
/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
/// [`RevocableMutexGuard`] are dropped), the wrapped object is also dropped.
///
/// # Examples
///
/// ```
/// # use kernel::sync::RevocableMutex;
/// # use kernel::revocable_mutex_init;
/// # use core::pin::Pin;
///
/// struct Example {
/// a: u32,
/// b: u32,
/// }
///
/// fn add_two(v: &RevocableMutex<Example>) -> Option<u32> {
/// let guard = v.try_lock()?;
/// Some(guard.a + guard.b)
/// }
///
/// fn example() {
/// // SAFETY: We call `revocable_mutex_init` immediately below.
/// let mut v = unsafe { RevocableMutex::new(Example { a: 10, b: 20 }) };
/// // SAFETY: We never move out of `v`.
/// let pinned = unsafe { Pin::new_unchecked(&mut v) };
/// revocable_mutex_init!(pinned, "example::v");
/// assert_eq!(add_two(&v), Some(30));
/// v.revoke();
/// assert_eq!(add_two(&v), None);
/// }
/// ```
pub struct RevocableMutex<T: ?Sized> {
inner: Mutex<RevocableMutexInner<T>>,
}

// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can.
unsafe impl<T: ?Sized + Send> Send for RevocableMutex<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 RevocableMutex<T> {}

/// Safely initialises a [`RevocableMutex`] with the given name, generating a new lock class.
#[macro_export]
macro_rules! revocable_mutex_init {
($mutex:expr, $name:literal) => {
$crate::init_with_lockdep!($mutex, $name)
};
}

impl<T> RevocableMutex<T> {
/// Creates a new revocable instance of the given data.
///
/// # Safety
///
/// The caller must call [`RevocableMutex::init`] before using the revocable mutex.
pub unsafe fn new(data: T) -> Self {
Self {
// SAFETY: The safety requirements of this function require that `RevocableMutex::init`
// be called before the returned object can be used. Mutex initialisation is called
// from `RevocableMutex::init`, so we satisfy the requirement from `Mutex`.
inner: unsafe {
Mutex::new(RevocableMutexInner {
is_available: true,
data: ManuallyDrop::new(data),
})
},
}
}
}

impl<T> NeedsLockClass for RevocableMutex<T> {
unsafe fn init(
self: Pin<&mut Self>,
name: &'static CStr,
key1: *mut bindings::lock_class_key,
key2: *mut bindings::lock_class_key,
) {
// SAFETY: `inner` is pinned when `self` is.
let mutex = unsafe { self.map_unchecked_mut(|r| &mut r.inner) };

// SAFETY: The safety requirements of this function satisfy the ones for `Mutex::init`
// (they're the same).
unsafe { mutex.init(name, key1, key2) };
}
}

impl<T: ?Sized> RevocableMutex<T> {
/// Tries to lock (and access) the \[revocable\] wrapped object.
///
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
///
/// Returns a guard that gives access to the object otherwise; the object is guaranteed to
/// remain accessible while the guard is alive. Callers are allowed to sleep while holding on
/// to the returned guard.
pub fn try_lock(&self) -> Option<RevocableMutexGuard<'_, T>> {
let inner = self.inner.lock();
if !inner.is_available {
return None;
}
Some(RevocableMutexGuard::new(inner))
}

/// Revokes access to and drops the wrapped object.
///
/// Revocation and dropping happens after ongoing accessors complete.
pub fn revoke(&self) {
let mut inner = self.inner.lock();
if !inner.is_available {
// Already revoked.
return;
}

// SAFETY: We know `inner.data` is valid because `is_available` is set to true. We'll drop
// it here and set it to false so it isn't dropped again.
unsafe { drop_in_place(&mut inner.data) };
inner.is_available = false;
}
}

impl<T: ?Sized> Drop for RevocableMutex<T> {
fn drop(&mut self) {
self.revoke();
}
}

/// A guard that allows access to a revocable object and keeps it alive.
pub struct RevocableMutexGuard<'a, T: ?Sized> {
guard: GuardMut<'a, Mutex<RevocableMutexInner<T>>>,
}

impl<'a, T: ?Sized> RevocableMutexGuard<'a, T> {
fn new(guard: GuardMut<'a, Mutex<RevocableMutexInner<T>>>) -> Self {
Self { guard }
}

/// Returns a pinned mutable reference to the wrapped object.
pub fn as_pinned_mut(&mut self) -> Pin<&mut T> {
// SAFETY: Revocable mutexes must be pinned, so we choose to always project the data as
// pinned as well (i.e., we guarantee we never move it).
unsafe { Pin::new_unchecked(&mut self.guard.data) }
}
}

impl<T: ?Sized> Deref for RevocableMutexGuard<'_, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.guard.data
}
}

impl<T: ?Sized> DerefMut for RevocableMutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard.data
}
}

0 comments on commit 0028943

Please sign in to comment.