From: Boqun Feng <boqun@kernel.org>
To: rust-for-linux@vger.kernel.org, rcu@vger.kernel.org
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Liam R. Howlett" <liam@infradead.org>,
"Andrew Ballance" <andrewjballance@gmail.com>,
"Alexander Viro" <viro@zeniv.linux.org.uk>,
"Christian Brauner" <brauner@kernel.org>,
"Jan Kara" <jack@suse.cz>, "Lyude Paul" <lyude@redhat.com>,
"Paul E. McKenney" <paulmck@kernel.org>,
"Frederic Weisbecker" <frederic@kernel.org>,
"Neeraj Upadhyay" <neeraj.upadhyay@kernel.org>,
"Joel Fernandes" <joelagnelf@nvidia.com>,
"Josh Triplett" <josh@joshtriplett.org>,
"Uladzislau Rezki" <urezki@gmail.com>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
"Lai Jiangshan" <jiangshanlai@gmail.com>,
Zqiang <qiang.zhang@linux.dev>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Yury Norov (NVIDIA)" <yury.norov@gmail.com>,
"Asahi Lina" <lina+kernel@asahilina.net>,
"Matthew Maurer" <mmaurer@google.com>,
"Lorenzo Stoakes" <ljs@kernel.org>,
linux-kernel@vger.kernel.org, maple-tree@lists.infradead.org,
linux-mm@kvack.org, linux-fsdevel@vger.kernel.org,
linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org,
linaro-mm-sig@lists.linaro.org
Subject: [RFC PATCH v2 3/4] rust: rcu: Introduce RcuFreeBox
Date: Fri, 17 Jul 2026 22:32:46 -0700 [thread overview]
Message-ID: <20260718053247.25154-4-boqun@kernel.org> (raw)
In-Reply-To: <20260718053247.25154-1-boqun@kernel.org>
The current RcuBox will call the `drop()` function after a grace period
inside an RCU callback. This suffices for maintaining a RCU-protected
object:
RcuBox::drop():
call_rcu(
|..| { // <- call back after one grace period.
T::drop(); // <- call the destructor of the inner object.
}
)
However, to support a different RCU usage pattern as below we need to
extend RcuBox:
1. clean up the object, and unshare it from future RCU readers.
2. wait for an RCU grace period.
3. no other RCU readers, we can free the memory.
An `RcuFreeBox<T: RcuFreeSafe>` is introduced to provide support for
this:
RcuFreeBox::drop():
T::drop_before_gp(); // clean up and ushare.
kfree_call_rcu(..); // free it after one grace period.
Signed-off-by: Boqun Feng <boqun@kernel.org>
---
rust/kernel/sync/rcu.rs | 34 ++++++++++++++++
rust/kernel/sync/rcu/rcu_box.rs | 72 +++++++++++++++++++++++++++++++--
2 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index 42f6bbc83f71..e781a5044de9 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -4,6 +4,8 @@
//!
//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
+use core::pin::Pin;
+
use crate::{
bindings,
types::{
@@ -18,6 +20,8 @@
pub use self::rcu_box::RcuKVBox;
pub use self::rcu_box::RcuVBox;
+pub use self::rcu_box::RcuFreeBox;
+
/// Evidence that the RCU read side lock is held on the current thread/CPU.
///
/// The type is explicitly not `Send` because this property is per-thread/CPU.
@@ -100,3 +104,33 @@ pub trait ForeignOwnableRcu: ForeignOwnable {
/// [`from_foreign`]: ForeignOwnable::from_foreign
unsafe fn rcu_borrow<'a>(ptr: *mut ffi::c_void) -> Self::RcuBorrowed<'a>;
}
+
+/// Declares a struct is safe to free after a grace period if all readers are guarded by RCU.
+///
+/// # Safety
+///
+/// Implementation must guarantee `drop_before_gp()` makes sure no future RCU reader will access
+/// any part of [`Self`], as a result, after `drop_before_gp()` return + one grace period, no RCU
+/// reader will be on the object, and it's safe to free it.
+///
+/// Notes for implementators: implementing this trait in general requires `Self` being a
+/// [`UnsafePinned`], i.e. a `&mut Self` is not a noalias reference if `Self` has non-trivial
+/// `drop()` function.
+pub unsafe trait RcuFreeSafe {
+ /// Clean up `Self` and make it ready to be RCU freed.
+ fn drop_before_gp(self: Pin<&mut Self>);
+}
+
+macro_rules! impl_not_drop {
+ ($($t:ty, )*) => {
+ // SAFETY: Dropping `T` has no side effect means `T` is always ready to be freed. And an
+ // empty `drop_before_gp()` suffices.
+ $(unsafe impl RcuFreeSafe for $t {
+ fn drop_before_gp(self: Pin<&mut Self>) {
+ $crate::const_assert!(!core::mem::needs_drop::<$t>());
+ }
+ })*
+ }
+}
+
+impl_not_drop! {i8,u8,i16,u16,i32,u32,isize,usize,i64,u64,}
diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
index cb1fd422480a..ab4e9b2a4444 100644
--- a/rust/kernel/sync/rcu/rcu_box.rs
+++ b/rust/kernel/sync/rcu/rcu_box.rs
@@ -6,6 +6,7 @@
use core::{
marker::PhantomData,
+ mem::ManuallyDrop,
ops::Deref,
ptr::NonNull, //
};
@@ -29,17 +30,18 @@
use super::{
ForeignOwnableRcu,
- Guard, //
+ Guard,
+ RcuFreeSafe, //
};
-/// A box that is freed with rcu.
+/// A box that is drop with RCU.
///
-/// The value must be `Send`, as rcu may drop it on another thread.
+/// The value must be `Send`, as RCU may drop it on another thread.
///
/// # Invariants
///
/// * The pointer is valid and references a pinned `RcuBoxInner<T>` allocated with `A`.
-/// * This `RcuBox` holds exclusive permissions to rcu free the allocation.
+/// * This `RcuBox` holds exclusive permissions to RCU-free the allocation.
pub struct RcuBox<T: Send, A: Allocator>(NonNull<RcuBoxInner<T>>, PhantomData<A>);
/// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator.
@@ -223,6 +225,56 @@ fn drop(&mut self) {
drop(unsafe { Box::<_, A>::from_raw(box_inner) });
}
+/// A box that is freed with RCU.
+///
+/// Currently we require `T` being `Send` because of an implementation limitation. In theory we can
+/// support `T` being `!Send`, since the RCU callback is only used to free the memory, not dropping
+/// `T`.
+pub struct RcuFreeBox<T: Send + RcuFreeSafe, A: Allocator>(RcuBox<ManuallyDrop<T>, A>);
+
+impl<T: Send + RcuFreeSafe, A: Allocator> RcuFreeBox<T, A> {
+ /// Create a new `RcuFreeBox`.
+ pub fn new(x: T, flags: alloc::Flags) -> Result<Self, AllocError> {
+ Ok(Self(RcuBox::new(ManuallyDrop::new(x), flags)?))
+ }
+}
+
+impl<T: Send + RcuFreeSafe, A: Allocator> Deref for RcuFreeBox<T, A> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ self.0.deref()
+ }
+}
+
+impl<T: Send + RcuFreeSafe, A: Allocator> Drop for RcuFreeBox<T, A> {
+ fn drop(&mut self) {
+ // CAST: `ManuallyDrop<RcuBoxInner<T>>` is transparent to `RcuBoxInner<T>`, and `RcuBox`
+ // owns the object per type invariants.
+ let inner: *mut RcuBoxInner<T> = self.0 .0.as_ptr().cast();
+
+ // SAFETY: Per the invariants of `RcuBox`, `inner` owns the pointed object. And we are not
+ // going to move it.
+ let pin = unsafe { Pin::new_unchecked(&mut (*inner).value) };
+
+ pin.drop_before_gp();
+
+ // `needs_drop::<ManuallyDrop>()` returns `false`, hence `kvfree_call_rcu()` will be called
+ // and free the underlying data after a grace period.
+ }
+}
+
+// Note that `T: Sync` is required since when moving an `RcuFreeBox<T, A>`, the previous owner may
+// still access `&T` for one grace period.
+//
+// SAFETY: Ownership of the `RcuFreeBox<T, A>` allows for `&T` and dropping the `T`, so `T: Send +
+// Sync` implies `RcuFreeBox<T, A>: Send`.
+unsafe impl<T: Send + Sync + RcuFreeSafe, A: Allocator> Send for RcuFreeBox<T, A> {}
+
+// SAFETY: `&RcuFreeBox<T, A>` allows for no operations other than those permitted by `&T`, so `T:
+// Sync` implies `RcuFreeBox<T, A>: Sync`.
+unsafe impl<T: Send + Sync + RcuFreeSafe, A: Allocator> Sync for RcuFreeBox<T, A> {}
+
#[kunit_tests(rust_rcu_box)]
mod tests {
use super::*;
@@ -236,6 +288,12 @@ fn rcu_box_basic() -> Result {
drop(rb);
+ let rb = RcuFreeBox::<_, alloc::allocator::Kmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+
+ drop(rb);
+
let rb = RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
assert_eq!(*rb, 42);
@@ -243,6 +301,12 @@ fn rcu_box_basic() -> Result {
drop(rb);
+ let rb = RcuFreeBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+
+ drop(rb);
+
Ok(())
}
}
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-07-18 5:33 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-18 5:32 [RFC PATCH v2 0/4] rust: sync: Introduce Rcu*Box Boqun Feng
2026-07-18 5:32 ` [RFC PATCH v2 1/4] rust: rcu: Add RcuBox type Boqun Feng
2026-07-18 5:48 ` sashiko-bot
2026-07-18 5:32 ` [RFC PATCH v2 2/4] rust: maple_tree: Add load_rcu() Boqun Feng
2026-07-18 6:00 ` sashiko-bot
2026-07-18 5:32 ` Boqun Feng [this message]
2026-07-18 5:42 ` [RFC PATCH v2 3/4] rust: rcu: Introduce RcuFreeBox sashiko-bot
2026-07-18 5:32 ` [NOT FOR MERGE] [RFC PATCH v2 4/4] rust: poll: use kfree_rcu() for PollCondVar Boqun Feng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260718053247.25154-4-boqun@kernel.org \
--to=boqun@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=andrewjballance@gmail.com \
--cc=bjorn3_gh@protonmail.com \
--cc=brauner@kernel.org \
--cc=christian.koenig@amd.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=frederic@kernel.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=jack@suse.cz \
--cc=jiangshanlai@gmail.com \
--cc=joelagnelf@nvidia.com \
--cc=josh@joshtriplett.org \
--cc=liam@infradead.org \
--cc=lina+kernel@asahilina.net \
--cc=linaro-mm-sig@lists.linaro.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=maple-tree@lists.infradead.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mmaurer@google.com \
--cc=neeraj.upadhyay@kernel.org \
--cc=ojeda@kernel.org \
--cc=paulmck@kernel.org \
--cc=qiang.zhang@linux.dev \
--cc=rcu@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sumit.semwal@linaro.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=urezki@gmail.com \
--cc=viro@zeniv.linux.org.uk \
--cc=work@onurozkan.dev \
--cc=yury.norov@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.