Skip to content

Commit e7e9516

Browse files
authored
Merge pull request raspberrypi#738 from wedsonaf/generic-revocable
rust: generalise `RevocableMutex` to any synchronisation primitive
2 parents b4ef314 + 8976aec commit e7e9516

File tree

6 files changed

+346
-191
lines changed

6 files changed

+346
-191
lines changed

rust/kernel/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ impl<T, U, V> Data<T, U, V> {
303303

304304
/// Returns the locked registrations if they're still available.
305305
pub fn registrations(&self) -> Option<RevocableMutexGuard<'_, T>> {
306-
self.registrations.try_lock()
306+
self.registrations.try_write()
307307
}
308308
}
309309

rust/kernel/sync.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod condvar;
2828
mod guard;
2929
mod locked_by;
3030
mod mutex;
31-
mod revocable_mutex;
31+
mod revocable;
3232
mod rwsem;
3333
mod seqlock;
3434
pub mod smutex;
@@ -38,9 +38,9 @@ pub use arc::{Ref, RefBorrow, UniqueRef};
3838
pub use condvar::CondVar;
3939
pub use guard::{Guard, Lock, LockFactory, LockInfo, LockIniter, ReadLock, WriteLock};
4040
pub use locked_by::LockedBy;
41-
pub use mutex::Mutex;
42-
pub use revocable_mutex::{RevocableMutex, RevocableMutexGuard};
43-
pub use rwsem::RwSemaphore;
41+
pub use mutex::{Mutex, RevocableMutex, RevocableMutexGuard};
42+
pub use revocable::{Revocable, RevocableGuard};
43+
pub use rwsem::{RevocableRwSemaphore, RevocableRwSemaphoreGuard, RwSemaphore};
4444
pub use seqlock::{SeqLock, SeqLockReadGuard};
4545
pub use spinlock::{RawSpinLock, SpinLock};
4646

rust/kernel/sync/mutex.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//!
55
//! This module allows Rust code to use the kernel's [`struct mutex`].
66
7-
use super::{Guard, Lock, LockFactory, LockIniter};
7+
use super::{Guard, Lock, LockFactory, LockIniter, WriteLock};
88
use crate::{bindings, str::CStr, Opaque};
99
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};
1010

@@ -114,3 +114,42 @@ unsafe impl<T: ?Sized> Lock for Mutex<T> {
114114
&self.data
115115
}
116116
}
117+
118+
/// A revocable mutex.
119+
///
120+
/// That is, a mutex to which access can be revoked at runtime. It is a specialisation of the more
121+
/// generic [`super::revocable::Revocable`].
122+
///
123+
/// # Examples
124+
///
125+
/// ```
126+
/// # use kernel::sync::RevocableMutex;
127+
/// # use kernel::revocable_init;
128+
/// # use core::pin::Pin;
129+
///
130+
/// struct Example {
131+
/// a: u32,
132+
/// b: u32,
133+
/// }
134+
///
135+
/// fn read_sum(v: &RevocableMutex<Example>) -> Option<u32> {
136+
/// let guard = v.try_write()?;
137+
/// Some(guard.a + guard.b)
138+
/// }
139+
///
140+
/// fn example() {
141+
/// // SAFETY: We call `revocable_init` immediately below.
142+
/// let mut v = unsafe { RevocableMutex::new(Example { a: 10, b: 20 }) };
143+
/// // SAFETY: We never move out of `v`.
144+
/// let pinned = unsafe { Pin::new_unchecked(&mut v) };
145+
/// revocable_init!(pinned, "example::v");
146+
/// assert_eq!(read_sum(&v), Some(30));
147+
/// v.revoke();
148+
/// assert_eq!(read_sum(&v), None);
149+
/// }
150+
/// ```
151+
pub type RevocableMutex<T> = super::revocable::Revocable<Mutex<()>, T>;
152+
153+
/// A guard for a revocable mutex.
154+
pub type RevocableMutexGuard<'a, T, I = WriteLock> =
155+
super::revocable::RevocableGuard<'a, Mutex<()>, T, I>;

rust/kernel/sync/revocable.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Synchronisation primitives where acccess to their contents can be revoked at runtime.
4+
5+
use crate::{
6+
bindings,
7+
str::CStr,
8+
sync::{Guard, Lock, LockFactory, LockInfo, NeedsLockClass, ReadLock, WriteLock},
9+
True,
10+
};
11+
use core::{
12+
mem::MaybeUninit,
13+
ops::{Deref, DerefMut},
14+
pin::Pin,
15+
};
16+
17+
/// The state within the revocable synchronisation primitive.
18+
///
19+
/// We don't use simply `Option<T>` because we need to drop in-place because the contents are
20+
/// implicitly pinned.
21+
///
22+
/// # Invariants
23+
///
24+
/// The `is_available` field determines if `data` is initialised.
25+
pub struct Inner<T> {
26+
is_available: bool,
27+
data: MaybeUninit<T>,
28+
}
29+
30+
impl<T> Inner<T> {
31+
fn new(data: T) -> Self {
32+
// INVARIANT: `data` is initialised and `is_available` is `true`, so the state matches.
33+
Self {
34+
is_available: true,
35+
data: MaybeUninit::new(data),
36+
}
37+
}
38+
39+
fn drop_in_place(&mut self) {
40+
if !self.is_available {
41+
// Already dropped.
42+
return;
43+
}
44+
45+
// INVARIANT: `data` is being dropped and `is_available` is set to `false`, so the state
46+
// matches.
47+
self.is_available = false;
48+
49+
// SAFETY: By the type invariants, `data` is valid because `is_available` was true.
50+
unsafe { self.data.assume_init_drop() };
51+
}
52+
}
53+
54+
impl<T> Drop for Inner<T> {
55+
fn drop(&mut self) {
56+
self.drop_in_place();
57+
}
58+
}
59+
60+
/// Revocable synchronisation primitive.
61+
///
62+
/// That is, it wraps synchronisation primitives so that access to their contents can be revoked at
63+
/// runtime, rendering them inacessible.
64+
///
65+
/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
66+
/// [`RevocableGuard`] are dropped), the wrapped object is also dropped.
67+
///
68+
/// For better ergonomics, we advise the use of specialisations of this struct, for example,
69+
/// [`super::RevocableMutex`] and [`super::RevocableRwSemaphore`]. Callers that do not need to
70+
/// sleep while holding on to a guard should use [`crate::revocable::Revocable`] instead, which is
71+
/// more efficient as it uses RCU to keep objects alive.
72+
///
73+
/// # Examples
74+
///
75+
/// ```
76+
/// # use kernel::sync::{Mutex, Revocable};
77+
/// # use kernel::revocable_init;
78+
/// # use core::pin::Pin;
79+
///
80+
/// struct Example {
81+
/// a: u32,
82+
/// b: u32,
83+
/// }
84+
///
85+
/// fn add_two(v: &Revocable<Mutex<()>, Example>) -> Option<u32> {
86+
/// let mut guard = v.try_write()?;
87+
/// guard.a += 2;
88+
/// guard.b += 2;
89+
/// Some(guard.a + guard.b)
90+
/// }
91+
///
92+
/// fn example() {
93+
/// // SAFETY: We call `revocable_init` immediately below.
94+
/// let mut v = unsafe { Revocable::<Mutex<()>, Example>::new(Example { a: 10, b: 20 }) };
95+
/// // SAFETY: We never move out of `v`.
96+
/// let pinned = unsafe { Pin::new_unchecked(&mut v) };
97+
/// revocable_init!(pinned, "example::v");
98+
/// assert_eq!(add_two(&v), Some(34));
99+
/// v.revoke();
100+
/// assert_eq!(add_two(&v), None);
101+
/// }
102+
/// ```
103+
pub struct Revocable<F: LockFactory, T> {
104+
inner: F::LockedType<Inner<T>>,
105+
}
106+
107+
/// Safely initialises a [`Revocable`] instance with the given name, generating a new lock class.
108+
#[macro_export]
109+
macro_rules! revocable_init {
110+
($mutex:expr, $name:literal) => {
111+
$crate::init_with_lockdep!($mutex, $name)
112+
};
113+
}
114+
115+
impl<F: LockFactory, T> Revocable<F, T> {
116+
/// Creates a new revocable instance of the given lock.
117+
///
118+
/// # Safety
119+
///
120+
/// The caller must call [`Revocable::init`] before using the revocable synch primitive.
121+
pub unsafe fn new(data: T) -> Self {
122+
Self {
123+
// SAFETY: The safety requirements of this function require that `Revocable::init`
124+
// be called before the returned object can be used. Lock initialisation is called
125+
// from `Revocable::init`.
126+
inner: unsafe { F::new_lock(Inner::new(data)) },
127+
}
128+
}
129+
}
130+
131+
impl<F: LockFactory, T> NeedsLockClass for Revocable<F, T>
132+
where
133+
F::LockedType<Inner<T>>: NeedsLockClass,
134+
{
135+
unsafe fn init(
136+
self: Pin<&mut Self>,
137+
name: &'static CStr,
138+
key1: *mut bindings::lock_class_key,
139+
key2: *mut bindings::lock_class_key,
140+
) {
141+
// SAFETY: `inner` is pinned when `self` is.
142+
let inner = unsafe { self.map_unchecked_mut(|r| &mut r.inner) };
143+
144+
// SAFETY: The safety requirements of this function satisfy the ones for `inner.init`
145+
// (they're the same).
146+
unsafe { inner.init(name, key1, key2) };
147+
}
148+
}
149+
150+
impl<F: LockFactory, T> Revocable<F, T>
151+
where
152+
F::LockedType<Inner<T>>: Lock<Inner = Inner<T>>,
153+
{
154+
/// Revokes access to and drops the wrapped object.
155+
///
156+
/// Revocation and dropping happen after ongoing accessors complete.
157+
pub fn revoke(&self) {
158+
self.lock().drop_in_place();
159+
}
160+
161+
/// Tries to lock the \[revocable\] wrapped object in write (exclusive) mode.
162+
///
163+
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
164+
///
165+
/// Returns a guard that gives access to the object otherwise; the object is guaranteed to
166+
/// remain accessible while the guard is alive. Callers are allowed to sleep while holding on
167+
/// to the returned guard.
168+
pub fn try_write(&self) -> Option<RevocableGuard<'_, F, T, WriteLock>> {
169+
let inner = self.lock();
170+
if !inner.is_available {
171+
return None;
172+
}
173+
Some(RevocableGuard::new(inner))
174+
}
175+
176+
fn lock(&self) -> Guard<'_, F::LockedType<Inner<T>>> {
177+
let ctx = self.inner.lock_noguard();
178+
// SAFETY: The lock was acquired in the call above.
179+
unsafe { Guard::new(&self.inner, ctx) }
180+
}
181+
}
182+
183+
impl<F: LockFactory, T> Revocable<F, T>
184+
where
185+
F::LockedType<Inner<T>>: Lock<ReadLock, Inner = Inner<T>>,
186+
{
187+
/// Tries to lock the \[revocable\] wrapped object in read (shared) mode.
188+
///
189+
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
190+
///
191+
/// Returns a guard that gives access to the object otherwise; the object is guaranteed to
192+
/// remain accessible while the guard is alive. Callers are allowed to sleep while holding on
193+
/// to the returned guard.
194+
pub fn try_read(&self) -> Option<RevocableGuard<'_, F, T, ReadLock>> {
195+
let ctx = self.inner.lock_noguard();
196+
// SAFETY: The lock was acquired in the call above.
197+
let inner = unsafe { Guard::new(&self.inner, ctx) };
198+
if !inner.is_available {
199+
return None;
200+
}
201+
Some(RevocableGuard::new(inner))
202+
}
203+
}
204+
205+
/// A guard that allows access to a revocable object and keeps it alive.
206+
pub struct RevocableGuard<'a, F: LockFactory, T, I: LockInfo>
207+
where
208+
F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>,
209+
{
210+
guard: Guard<'a, F::LockedType<Inner<T>>, I>,
211+
}
212+
213+
impl<'a, F: LockFactory, T, I: LockInfo> RevocableGuard<'a, F, T, I>
214+
where
215+
F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>,
216+
{
217+
fn new(guard: Guard<'a, F::LockedType<Inner<T>>, I>) -> Self {
218+
Self { guard }
219+
}
220+
}
221+
222+
impl<F: LockFactory, T, I: LockInfo<Writable = True>> RevocableGuard<'_, F, T, I>
223+
where
224+
F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>,
225+
{
226+
/// Returns a pinned mutable reference to the wrapped object.
227+
pub fn as_pinned_mut(&mut self) -> Pin<&mut T> {
228+
// SAFETY: Revocable mutexes must be pinned, so we choose to always project the data as
229+
// pinned as well (i.e., we guarantee we never move it).
230+
unsafe { Pin::new_unchecked(&mut *self) }
231+
}
232+
}
233+
234+
impl<F: LockFactory, T, I: LockInfo> Deref for RevocableGuard<'_, F, T, I>
235+
where
236+
F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>,
237+
{
238+
type Target = T;
239+
240+
fn deref(&self) -> &Self::Target {
241+
unsafe { &*self.guard.data.as_ptr() }
242+
}
243+
}
244+
245+
impl<F: LockFactory, T, I: LockInfo<Writable = True>> DerefMut for RevocableGuard<'_, F, T, I>
246+
where
247+
F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>,
248+
{
249+
fn deref_mut(&mut self) -> &mut Self::Target {
250+
unsafe { &mut *self.guard.data.as_mut_ptr() }
251+
}
252+
}

0 commit comments

Comments
 (0)