Skip to content

Commit 7bd1ccf

Browse files
authored
Merge pull request #783 from wedsonaf/rcu
rust: add basic rcu support
2 parents 3826627 + 2c76107 commit 7bd1ccf

File tree

3 files changed

+100
-16
lines changed

3 files changed

+100
-16
lines changed

rust/kernel/revocable.rs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence
66
//! of a [`RevocableGuard`] ensures that objects remain valid.
77
8-
use crate::bindings;
8+
use crate::{bindings, sync::rcu};
99
use core::{
1010
cell::UnsafeCell,
1111
marker::PhantomData,
@@ -40,6 +40,29 @@ use core::{
4040
/// v.revoke();
4141
/// assert_eq!(add_two(&v), None);
4242
/// ```
43+
///
44+
/// Sample example as above, but explicitly using the rcu read side lock.
45+
///
46+
/// ```
47+
/// # use kernel::revocable::Revocable;
48+
/// use kernel::sync::rcu;
49+
///
50+
/// struct Example {
51+
/// a: u32,
52+
/// b: u32,
53+
/// }
54+
///
55+
/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
56+
/// let guard = rcu::read_lock();
57+
/// let e = v.try_access_with_guard(&guard)?;
58+
/// Some(e.a + e.b)
59+
/// }
60+
///
61+
/// let v = Revocable::new(Example { a: 10, b: 20 });
62+
/// assert_eq!(add_two(&v), Some(30));
63+
/// v.revoke();
64+
/// assert_eq!(add_two(&v), None);
65+
/// ```
4366
pub struct Revocable<T: ?Sized> {
4467
is_available: AtomicBool,
4568
data: ManuallyDrop<UnsafeCell<T>>,
@@ -57,7 +80,7 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Revocable<T> {}
5780

5881
impl<T> Revocable<T> {
5982
/// Creates a new revocable instance of the given data.
60-
pub fn new(data: T) -> Self {
83+
pub const fn new(data: T) -> Self {
6184
Self {
6285
is_available: AtomicBool::new(true),
6386
data: ManuallyDrop::new(UnsafeCell::new(data)),
@@ -74,9 +97,26 @@ impl<T: ?Sized> Revocable<T> {
7497
/// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep
7598
/// because another CPU may be waiting to complete the revocation of this object.
7699
pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
77-
let guard = RevocableGuard::new(self.data.get());
100+
let guard = rcu::read_lock();
101+
if self.is_available.load(Ordering::Relaxed) {
102+
Some(RevocableGuard::new(self.data.get(), guard))
103+
} else {
104+
None
105+
}
106+
}
107+
108+
/// Tries to access the \[revocable\] wrapped object.
109+
///
110+
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
111+
///
112+
/// Returns a shared reference to the object otherwise; the object is guaranteed to
113+
/// remain accessible while the rcu read side guard is alive. In such cases, callers are not
114+
/// allowed to sleep because another CPU may be waiting to complete the revocation of this
115+
/// object.
116+
pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> {
78117
if self.is_available.load(Ordering::Relaxed) {
79-
Some(guard)
118+
// SAFETY: Given that the RCU read side lock is held, data has to remain valid.
119+
Some(unsafe { &*self.data.get() })
80120
} else {
81121
None
82122
}
@@ -127,29 +167,20 @@ impl<T: ?Sized> Drop for Revocable<T> {
127167
/// The RCU read-side lock is held while the guard is alive.
128168
pub struct RevocableGuard<'a, T: ?Sized> {
129169
data_ref: *const T,
170+
_rcu_guard: rcu::Guard,
130171
_p: PhantomData<&'a ()>,
131172
}
132173

133174
impl<T: ?Sized> RevocableGuard<'_, T> {
134-
fn new(data_ref: *const T) -> Self {
135-
// SAFETY: Just an FFI call, there are no further requirements.
136-
unsafe { bindings::rcu_read_lock() };
137-
138-
// INVARIANTS: The RCU read-side lock was just acquired.
175+
fn new(data_ref: *const T, rcu_guard: rcu::Guard) -> Self {
139176
Self {
140177
data_ref,
178+
_rcu_guard: rcu_guard,
141179
_p: PhantomData,
142180
}
143181
}
144182
}
145183

146-
impl<T: ?Sized> Drop for RevocableGuard<'_, T> {
147-
fn drop(&mut self) {
148-
// SAFETY: By the type invariants, we know that we hold the RCU read-side lock.
149-
unsafe { bindings::rcu_read_unlock() };
150-
}
151-
}
152-
153184
impl<T: ?Sized> Deref for RevocableGuard<'_, T> {
154185
type Target = T;
155186

rust/kernel/sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod guard;
3030
mod locked_by;
3131
mod mutex;
3232
mod nowait;
33+
pub mod rcu;
3334
mod revocable;
3435
mod rwsem;
3536
mod seqlock;

rust/kernel/sync/rcu.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! RCU support.
4+
//!
5+
//! C header: [`include/linux/rcupdate.h`](../../../../include/linux/rcupdate.h)
6+
7+
use crate::bindings;
8+
use core::marker::PhantomData;
9+
10+
/// Evidence that the RCU read side lock is held on the current thread/CPU.
11+
///
12+
/// The type is explicitly not `Send` because this property is per-thread/CPU.
13+
///
14+
/// # Invariants
15+
///
16+
/// The RCU read side lock is actually held while instances of this guard exist.
17+
pub struct Guard {
18+
_not_send: PhantomData<*mut ()>,
19+
}
20+
21+
impl Guard {
22+
/// Acquires the RCU read side lock and returns a guard.
23+
pub fn new() -> Self {
24+
// SAFETY: An FFI call with no additional requirements.
25+
unsafe { bindings::rcu_read_lock() };
26+
// INVARIANT: The RCU read side lock was just acquired above.
27+
Self {
28+
_not_send: PhantomData,
29+
}
30+
}
31+
32+
/// Explicitly releases the RCU read side lock.
33+
pub fn unlock(self) {}
34+
}
35+
36+
impl Default for Guard {
37+
fn default() -> Self {
38+
Self::new()
39+
}
40+
}
41+
42+
impl Drop for Guard {
43+
fn drop(&mut self) {
44+
// SAFETY: By the type invariants, the rcu read side is locked, so it is ok to unlock it.
45+
unsafe { bindings::rcu_read_unlock() };
46+
}
47+
}
48+
49+
/// Acquires the RCU read side lock.
50+
pub fn read_lock() -> Guard {
51+
Guard::new()
52+
}

0 commit comments

Comments
 (0)