Skip to content

Commit 6c9714f

Browse files
committed
rust: add RevocableMutex.
This is a mutex where access to its contents can be revoked at runtime. This is different from `Revocable` in a few ways: 1. The caller may sleep while holding a guard. 2. Accessors are all serialised. 3. Accessing is not as cheap (because it involves acquiring the mutex). 4. The size of the object is larger (because it involves a mutex + flag). An example of where this a good fit to be used in device state that holds registrations to other subsystems, for example, the PL061 driver registers with the gpio subsystem; the registration is only used on `probe` and is revoked on `remove`. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent c6389c5 commit 6c9714f

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

rust/kernel/sync/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod condvar;
2828
mod guard;
2929
mod locked_by;
3030
mod mutex;
31+
mod revocable_mutex;
3132
mod seqlock;
3233
mod spinlock;
3334

@@ -36,6 +37,7 @@ pub use condvar::CondVar;
3637
pub use guard::{CreatableLock, Guard, GuardMut, Lock};
3738
pub use locked_by::LockedBy;
3839
pub use mutex::Mutex;
40+
pub use revocable_mutex::{RevocableMutex, RevocableMutexGuard};
3941
pub use seqlock::{SeqLock, SeqLockReadGuard};
4042
pub use spinlock::SpinLock;
4143

rust/kernel/sync/revocable_mutex.rs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A kernel mutex where acccess to contents can be revoked at runtime.
4+
5+
use crate::{
6+
bindings,
7+
str::CStr,
8+
sync::{GuardMut, Mutex, NeedsLockClass},
9+
};
10+
use core::{
11+
mem::ManuallyDrop,
12+
ops::{Deref, DerefMut},
13+
pin::Pin,
14+
ptr::drop_in_place,
15+
};
16+
17+
/// The state within a `RevocableMutex` that is protected by a mutex.
18+
///
19+
/// We don't use simply `Option<T>` because we need to drop in-place because the contents are
20+
/// implicitly pinned.
21+
struct RevocableMutexInner<T: ?Sized> {
22+
is_available: bool,
23+
data: ManuallyDrop<T>,
24+
}
25+
26+
/// A mutex whose contents can become inaccessible at runtime.
27+
///
28+
/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
29+
/// [`RevocableMutexGuard`] are dropped), the wrapped object is also dropped.
30+
///
31+
/// # Examples
32+
///
33+
/// ```
34+
/// # use kernel::sync::RevocableMutex;
35+
/// # use kernel::revocable_mutex_init;
36+
/// # use core::pin::Pin;
37+
///
38+
/// struct Example {
39+
/// a: u32,
40+
/// b: u32,
41+
/// }
42+
///
43+
/// fn add_two(v: &RevocableMutex<Example>) -> Option<u32> {
44+
/// let guard = v.try_lock()?;
45+
/// Some(guard.a + guard.b)
46+
/// }
47+
///
48+
/// fn example() {
49+
/// // SAFETY: We call `revocable_mutex_init` immediately below.
50+
/// let mut v = unsafe { RevocableMutex::new(Example { a: 10, b: 20 }) };
51+
/// // SAFETY: We never move out of `v`.
52+
/// let pinned = unsafe { Pin::new_unchecked(&mut v) };
53+
/// revocable_mutex_init!(pinned, "example::v");
54+
/// assert_eq!(add_two(&v), Some(30));
55+
/// v.revoke();
56+
/// assert_eq!(add_two(&v), None);
57+
/// }
58+
/// ```
59+
pub struct RevocableMutex<T: ?Sized> {
60+
inner: Mutex<RevocableMutexInner<T>>,
61+
}
62+
63+
// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can.
64+
unsafe impl<T: ?Sized + Send> Send for RevocableMutex<T> {}
65+
66+
// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the
67+
// data it protects is `Send`.
68+
unsafe impl<T: ?Sized + Send> Sync for RevocableMutex<T> {}
69+
70+
/// Safely initialises a [`RevocableMutex`] with the given name, generating a new lock class.
71+
#[macro_export]
72+
macro_rules! revocable_mutex_init {
73+
($mutex:expr, $name:literal) => {
74+
$crate::init_with_lockdep!($mutex, $name)
75+
};
76+
}
77+
78+
impl<T> RevocableMutex<T> {
79+
/// Creates a new revocable instance of the given data.
80+
///
81+
/// # Safety
82+
///
83+
/// The caller must call [`RevocableMutex::init`] before using the revocable mutex.
84+
pub unsafe fn new(data: T) -> Self {
85+
Self {
86+
// SAFETY: The safety requirements of this function require that `RevocableMutex::init`
87+
// be called before the returned object can be used. Mutex initialisation is called
88+
// from `RevocableMutex::init`, so we satisfy the requirement from `Mutex`.
89+
inner: unsafe {
90+
Mutex::new(RevocableMutexInner {
91+
is_available: true,
92+
data: ManuallyDrop::new(data),
93+
})
94+
},
95+
}
96+
}
97+
}
98+
99+
impl<T> NeedsLockClass for RevocableMutex<T> {
100+
unsafe fn init(
101+
self: Pin<&mut Self>,
102+
name: &'static CStr,
103+
key1: *mut bindings::lock_class_key,
104+
key2: *mut bindings::lock_class_key,
105+
) {
106+
// SAFETY: `inner` is pinned when `self` is.
107+
let mutex = unsafe { self.map_unchecked_mut(|r| &mut r.inner) };
108+
109+
// SAFETY: The safety requirements of this function satisfy the ones for `Mutex::init`
110+
// (they're the same).
111+
unsafe { mutex.init(name, key1, key2) };
112+
}
113+
}
114+
115+
impl<T: ?Sized> RevocableMutex<T> {
116+
/// Tries to lock (and access) the \[revocable\] wrapped object.
117+
///
118+
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
119+
///
120+
/// Returns a guard that gives access to the object otherwise; the object is guaranteed to
121+
/// remain accessible while the guard is alive. Callers are allowed to sleep while holding on
122+
/// to the returned guard.
123+
pub fn try_lock(&self) -> Option<RevocableMutexGuard<'_, T>> {
124+
let inner = self.inner.lock();
125+
if !inner.is_available {
126+
return None;
127+
}
128+
Some(RevocableMutexGuard::new(inner))
129+
}
130+
131+
/// Revokes access to and drops the wrapped object.
132+
///
133+
/// Revocation and dropping happens after ongoing accessors complete.
134+
pub fn revoke(&self) {
135+
let mut inner = self.inner.lock();
136+
if !inner.is_available {
137+
// Already revoked.
138+
return;
139+
}
140+
141+
// SAFETY: We know `inner.data` is valid because `is_available` is set to true. We'll drop
142+
// it here and set it to false so it isn't dropped again.
143+
unsafe { drop_in_place(&mut inner.data) };
144+
inner.is_available = false;
145+
}
146+
}
147+
148+
impl<T: ?Sized> Drop for RevocableMutex<T> {
149+
fn drop(&mut self) {
150+
self.revoke();
151+
}
152+
}
153+
154+
/// A guard that allows access to a revocable object and keeps it alive.
155+
pub struct RevocableMutexGuard<'a, T: ?Sized> {
156+
guard: GuardMut<'a, Mutex<RevocableMutexInner<T>>>,
157+
}
158+
159+
impl<'a, T: ?Sized> RevocableMutexGuard<'a, T> {
160+
fn new(guard: GuardMut<'a, Mutex<RevocableMutexInner<T>>>) -> Self {
161+
Self { guard }
162+
}
163+
164+
/// Returns a pinned mutable reference to the wrapped object.
165+
pub fn as_pinned_mut(&mut self) -> Pin<&mut T> {
166+
// SAFETY: Revocable mutexes must be pinned, so we choose to always project the data as
167+
// pinned as well (i.e., we guarantee we never move it).
168+
unsafe { Pin::new_unchecked(&mut self.guard.data) }
169+
}
170+
}
171+
172+
impl<T: ?Sized> Deref for RevocableMutexGuard<'_, T> {
173+
type Target = T;
174+
175+
fn deref(&self) -> &Self::Target {
176+
&self.guard.data
177+
}
178+
}
179+
180+
impl<T: ?Sized> DerefMut for RevocableMutexGuard<'_, T> {
181+
fn deref_mut(&mut self) -> &mut Self::Target {
182+
&mut self.guard.data
183+
}
184+
}

0 commit comments

Comments
 (0)