* [PATCH 0/3] rust: sync: Introduce Rcu*Box
@ 2026-06-05 13:35 Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
` (4 more replies)
0 siblings, 5 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
(this series is based on Alice's RFC [1] and discussion around Philipp's
patches [2], [3])
As an easy way to provide RCU-protected allocation, two major types are
provided:
- `RcuBox<T, A>`, inner T will be dropped and after a grace period.
Users: binder with maple_tree and dma_fence.
- `RcuFreeBox<T, A>`, inner T will be cleaned up immmediately and freed
after a grace period. (Name suggestion is welcome). This is an attempt
to consolidate Alice's `PollCondVarBox` [4] into a generic
implementation, InPlaceInit support is still missing, but I want to
get some feedback on the trait `RcuFreeSafe`.
[1]: https://lore.kernel.org/rust-for-linux/20260116-rcu-box-v1-0-38ebfbcd53f0@google.com/
[2]: https://lore.kernel.org/rust-for-linux/20260530143541.229628-2-phasta@kernel.org/
[3]: https://lore.kernel.org/rust-for-linux/20260520131725.266014-2-phasta@kernel.org/
[4]: https://lore.kernel.org/rust-for-linux/20260523-upgrade-poll-v4-0-f5b4c747eac2@google.com/
Regards,
Boqun
Alice Ryhl (2):
rust: rcu: Add RcuBox type
rust: maple_tree: Add load_rcu()
Boqun Feng (1):
rust: rcu: Introduce RcuFreeBox
rust/bindings/bindings_helper.h | 1 +
rust/kernel/maple_tree.rs | 52 ++++++
rust/kernel/sync/rcu.rs | 65 ++++++-
rust/kernel/sync/rcu/rcu_box.rs | 290 ++++++++++++++++++++++++++++++++
4 files changed, 407 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/sync/rcu/rcu_box.rs
--
2.51.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
@ 2026-06-05 13:35 ` Boqun Feng
2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:49 ` sashiko-bot
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: Add " Boqun Feng
` (3 subsequent siblings)
4 siblings, 2 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
From: Alice Ryhl <aliceryhl@google.com>
This adds an RcuBox container, which is like Box except that the value
is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
To allow containers to rely on the RCU properties of RcuBox, an
extension of ForeignOwnable is added.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
[boqun: Make RcuBox generic over Allocator and add tests]
[boqun: Add type alias for Rcu*Box]
Co-developed-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Boqun Feng <boqun@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/sync/rcu.rs | 34 ++++-
rust/kernel/sync/rcu/rcu_box.rs | 230 ++++++++++++++++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/sync/rcu/rcu_box.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 446dbeaf0866..2011645c7cfb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -80,6 +80,7 @@
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/random.h>
+#include <linux/rcupdate.h>
#include <linux/refcount.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..7da6b8d22277 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -4,7 +4,19 @@
//!
//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
-use crate::{bindings, types::NotThreadSafe};
+use crate::{
+ bindings,
+ types::{
+ ForeignOwnable,
+ NotThreadSafe, //
+ }, //
+};
+
+mod rcu_box;
+pub use self::rcu_box::RcuBox;
+pub use self::rcu_box::RcuKBox;
+pub use self::rcu_box::RcuKVBox;
+pub use self::rcu_box::RcuVBox;
/// Evidence that the RCU read side lock is held on the current thread/CPU.
///
@@ -50,3 +62,23 @@ fn drop(&mut self) {
pub fn read_lock() -> Guard {
Guard::new()
}
+
+/// Declares that a pointer type is rcu safe.
+pub trait ForeignOwnableRcu: ForeignOwnable {
+ /// Type used to immutably borrow an rcu-safe value that is currently foreign-owned.
+ type RcuBorrowed<'a>;
+
+ /// Borrows a foreign-owned object immutably for an rcu grace period.
+ ///
+ /// This method provides a way to access a foreign-owned rcu-safe value from Rust immutably.
+ ///
+ /// # Safety
+ ///
+ /// * The provided pointer must have been returned by a previous call to [`into_foreign`].
+ /// * If [`from_foreign`] is called, then `'a` must not end after the call to `from_foreign`
+ /// plus one rcu grace period.
+ ///
+ /// [`into_foreign`]: ForeignOwnable::into_foreign
+ /// [`from_foreign`]: ForeignOwnable::from_foreign
+ unsafe fn rcu_borrow<'a>(ptr: *mut ffi::c_void) -> Self::RcuBorrowed<'a>;
+}
diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
new file mode 100644
index 000000000000..943fe3e8974e
--- /dev/null
+++ b/rust/kernel/sync/rcu/rcu_box.rs
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 Google LLC.
+
+//! Provides the `RcuBox` type for Rust allocations that live for a grace period.
+
+use core::{
+ marker::PhantomData,
+ ops::Deref,
+ ptr::NonNull, //
+};
+
+use crate::{
+ alloc::{
+ self,
+ allocator::{
+ KVmalloc,
+ Kmalloc,
+ Vmalloc, //
+ },
+ AllocError,
+ Allocator, //
+ },
+ bindings,
+ ffi::c_void,
+ prelude::*,
+ types::ForeignOwnable,
+};
+
+use super::{
+ ForeignOwnableRcu,
+ Guard, //
+};
+
+/// A box that is freed with rcu.
+///
+/// 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.
+pub struct RcuBox<T: Send, A: Allocator>(NonNull<RcuBoxInner<T>>, PhantomData<A>);
+
+/// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuKBox};
+/// let rb = RcuKBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuKBox<T> = RcuBox<T, Kmalloc>;
+
+/// Type alias for [`RcuBox`] with a [`Vmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuVBox};
+/// let rb = RcuVBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuVBox<T> = RcuBox<T, Vmalloc>;
+
+/// Type alias for [`RcuBox`] with a [`KVmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuKVBox};
+/// let rb = RcuKVBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuKVBox<T> = RcuBox<T, KVmalloc>;
+
+struct RcuBoxInner<T> {
+ rcu_head: bindings::callback_head,
+ value: T,
+}
+
+// Note that `T: Sync` is required since when moving an `RcuBox<T, A>`, the previous owner may
+// still access `&T` for one grace period.
+//
+// SAFETY: Ownership of the `RcuBox<T, A>` allows for `&T` and dropping the `T`, so `T: Send +
+// Sync` implies `RcuBox<T, A>: Send`.
+unsafe impl<T: Send + Sync, A: Allocator> Send for RcuBox<T, A> {}
+
+// SAFETY: `&RcuBox<T, A>` allows for no operations other than those permitted by `&T`, so `T:
+// Sync` implies `RcuBox<T, A>: Sync`.
+unsafe impl<T: Send + Sync, A: Allocator> Sync for RcuBox<T, A> {}
+
+impl<T: Send, A: Allocator> RcuBox<T, A> {
+ /// Create a new `RcuBox`.
+ pub fn new(x: T, flags: alloc::Flags) -> Result<Self, AllocError> {
+ let b = Box::<_, A>::new(
+ RcuBoxInner {
+ value: x,
+ rcu_head: Default::default(),
+ },
+ flags,
+ )?;
+
+ // INVARIANT:
+ // * The pointer contains a valid `RcuBoxInner` allocated with `A`.
+ // * We just allocated it, so we own free permissions.
+ Ok(RcuBox(NonNull::from(Box::leak(b)), PhantomData))
+ }
+
+ /// Access the value for a grace period.
+ pub fn with_rcu<'rcu>(&self, _read_guard: &'rcu Guard) -> &'rcu T {
+ // SAFETY: The `RcuBox` has not been dropped yet, so the value is valid for at least one
+ // grace period.
+ unsafe { &(*self.0.as_ptr()).value }
+ }
+}
+
+impl<T: Send, A: Allocator> Deref for RcuBox<T, A> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ // SAFETY: While the `RcuBox<T>` exists, the value remains valid.
+ unsafe { &(*self.0.as_ptr()).value }
+ }
+}
+
+// SAFETY:
+// * The `RcuBoxInner<T>` was allocated with `A`.
+// * `NonNull::as_ptr` returns a non-null pointer.
+unsafe impl<T: Send + 'static, A: Allocator> ForeignOwnable for RcuBox<T, A> {
+ const FOREIGN_ALIGN: usize = <Box<RcuBoxInner<T>, A> as ForeignOwnable>::FOREIGN_ALIGN;
+
+ type Borrowed<'a> = &'a T;
+ type BorrowedMut<'a> = &'a T;
+
+ fn into_foreign(self) -> *mut c_void {
+ self.0.as_ptr().cast()
+ }
+
+ unsafe fn from_foreign(ptr: *mut c_void) -> Self {
+ // INVARIANT: Pointer returned by `into_foreign, A` carries same invariants as `RcuBox<T>`.
+ // SAFETY: `into_foreign` never returns a null pointer.
+ Self(unsafe { NonNull::new_unchecked(ptr.cast()) }, PhantomData)
+ }
+
+ unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: Caller ensures that `'a` is short enough.
+ unsafe { &(*ptr.cast::<RcuBoxInner<T>>()).value }
+ }
+
+ unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: `borrow_mut` has strictly stronger preconditions than `borrow`.
+ unsafe { Self::borrow(ptr) }
+ }
+}
+
+impl<T: Send + 'static, A: Allocator> ForeignOwnableRcu for RcuBox<T, A> {
+ type RcuBorrowed<'a> = &'a T;
+
+ unsafe fn rcu_borrow<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: `RcuBox::drop` can only run after `from_foreign` is called, and the value is
+ // valid until `RcuBox::drop` plus one grace period.
+ unsafe { &(*ptr.cast::<RcuBoxInner<T>>()).value }
+ }
+}
+
+impl<T: Send, A: Allocator> Drop for RcuBox<T, A> {
+ fn drop(&mut self) {
+ // SAFETY: The `rcu_head` field is in-bounds of a valid allocation.
+ let rcu_head = unsafe { &raw mut (*self.0.as_ptr()).rcu_head };
+ if core::mem::needs_drop::<T>() {
+ // SAFETY: `rcu_head` is the `rcu_head` field of `RcuBoxInner<T>`. All users will be
+ // gone in an rcu grace period. This is the destructor, so we may pass ownership of the
+ // allocation.
+ unsafe { bindings::call_rcu(rcu_head, Some(drop_rcu_box::<T, A>)) };
+ } else {
+ // SAFETY: All users will be gone in an rcu grace period.
+ // TODO: We are luckily since `kvfree_call_rcu()` works on both kmalloc and vmalloc,
+ // maybe a new `Allocator` method is needed.
+ unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().cast()) };
+ }
+ }
+}
+
+/// Free this `RcuBoxInner<T>`.
+///
+/// # Safety
+///
+/// `head` references the `rcu_head` field of an `RcuBoxInner<T>` that has no references to it.
+/// Ownership of the `Box<RcuBoxInner<T>, A>` must be passed.
+unsafe extern "C" fn drop_rcu_box<T, A: Allocator>(head: *mut bindings::callback_head) {
+ // SAFETY: Caller provides a pointer to the `rcu_head` field of a `RcuBoxInner<T>`.
+ let box_inner = unsafe { crate::container_of!(head, RcuBoxInner<T>, rcu_head) };
+
+ // SAFETY: Caller ensures exclusive access and passed ownership.
+ drop(unsafe { Box::<_, A>::from_raw(box_inner) });
+}
+
+#[kunit_tests(rust_rcu_box)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn rcu_box_basic() -> Result {
+ let rb = RcuBox::<_, alloc::allocator::Kmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
+ let rb = RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
+ Ok(())
+ }
+}
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 1/3] rust: rcu: Add RcuBox type
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
@ 2026-06-05 13:35 ` Boqun Feng
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: add load_rcu() Boqun Feng
` (2 subsequent siblings)
4 siblings, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
From: Alice Ryhl <aliceryhl@google.com>
This adds an RcuBox container, which is like Box except that the value
is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
To allow containers to rely on the RCU properties of RcuBox, an
extension of ForeignOwnable is added.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
[boqun: Make RcuBox generic over Allocator and add tests]
[boqun: Add type alias for Rcu*Box]
Co-developed-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Boqun Feng <boqun@kernel.org>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/sync/rcu.rs | 34 ++++-
rust/kernel/sync/rcu/rcu_box.rs | 230 ++++++++++++++++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/sync/rcu/rcu_box.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 446dbeaf0866..2011645c7cfb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -80,6 +80,7 @@
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/random.h>
+#include <linux/rcupdate.h>
#include <linux/refcount.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..7da6b8d22277 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -4,7 +4,19 @@
//!
//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
-use crate::{bindings, types::NotThreadSafe};
+use crate::{
+ bindings,
+ types::{
+ ForeignOwnable,
+ NotThreadSafe, //
+ }, //
+};
+
+mod rcu_box;
+pub use self::rcu_box::RcuBox;
+pub use self::rcu_box::RcuKBox;
+pub use self::rcu_box::RcuKVBox;
+pub use self::rcu_box::RcuVBox;
/// Evidence that the RCU read side lock is held on the current thread/CPU.
///
@@ -50,3 +62,23 @@ fn drop(&mut self) {
pub fn read_lock() -> Guard {
Guard::new()
}
+
+/// Declares that a pointer type is rcu safe.
+pub trait ForeignOwnableRcu: ForeignOwnable {
+ /// Type used to immutably borrow an rcu-safe value that is currently foreign-owned.
+ type RcuBorrowed<'a>;
+
+ /// Borrows a foreign-owned object immutably for an rcu grace period.
+ ///
+ /// This method provides a way to access a foreign-owned rcu-safe value from Rust immutably.
+ ///
+ /// # Safety
+ ///
+ /// * The provided pointer must have been returned by a previous call to [`into_foreign`].
+ /// * If [`from_foreign`] is called, then `'a` must not end after the call to `from_foreign`
+ /// plus one rcu grace period.
+ ///
+ /// [`into_foreign`]: ForeignOwnable::into_foreign
+ /// [`from_foreign`]: ForeignOwnable::from_foreign
+ unsafe fn rcu_borrow<'a>(ptr: *mut ffi::c_void) -> Self::RcuBorrowed<'a>;
+}
diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
new file mode 100644
index 000000000000..943fe3e8974e
--- /dev/null
+++ b/rust/kernel/sync/rcu/rcu_box.rs
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 Google LLC.
+
+//! Provides the `RcuBox` type for Rust allocations that live for a grace period.
+
+use core::{
+ marker::PhantomData,
+ ops::Deref,
+ ptr::NonNull, //
+};
+
+use crate::{
+ alloc::{
+ self,
+ allocator::{
+ KVmalloc,
+ Kmalloc,
+ Vmalloc, //
+ },
+ AllocError,
+ Allocator, //
+ },
+ bindings,
+ ffi::c_void,
+ prelude::*,
+ types::ForeignOwnable,
+};
+
+use super::{
+ ForeignOwnableRcu,
+ Guard, //
+};
+
+/// A box that is freed with rcu.
+///
+/// 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.
+pub struct RcuBox<T: Send, A: Allocator>(NonNull<RcuBoxInner<T>>, PhantomData<A>);
+
+/// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuKBox};
+/// let rb = RcuKBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuKBox<T> = RcuBox<T, Kmalloc>;
+
+/// Type alias for [`RcuBox`] with a [`Vmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuVBox};
+/// let rb = RcuVBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuVBox<T> = RcuBox<T, Vmalloc>;
+
+/// Type alias for [`RcuBox`] with a [`KVmalloc`] allocator.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::sync::rcu::{self, RcuKVBox};
+/// let rb = RcuKVBox::new(42, GFP_KERNEL)?;
+///
+/// assert_eq!(*rb, 42);
+/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42);
+/// # Ok::<(), Error>(())
+/// ```
+pub type RcuKVBox<T> = RcuBox<T, KVmalloc>;
+
+struct RcuBoxInner<T> {
+ rcu_head: bindings::callback_head,
+ value: T,
+}
+
+// Note that `T: Sync` is required since when moving an `RcuBox<T, A>`, the previous owner may
+// still access `&T` for one grace period.
+//
+// SAFETY: Ownership of the `RcuBox<T, A>` allows for `&T` and dropping the `T`, so `T: Send +
+// Sync` implies `RcuBox<T, A>: Send`.
+unsafe impl<T: Send + Sync, A: Allocator> Send for RcuBox<T, A> {}
+
+// SAFETY: `&RcuBox<T, A>` allows for no operations other than those permitted by `&T`, so `T:
+// Sync` implies `RcuBox<T, A>: Sync`.
+unsafe impl<T: Send + Sync, A: Allocator> Sync for RcuBox<T, A> {}
+
+impl<T: Send, A: Allocator> RcuBox<T, A> {
+ /// Create a new `RcuBox`.
+ pub fn new(x: T, flags: alloc::Flags) -> Result<Self, AllocError> {
+ let b = Box::<_, A>::new(
+ RcuBoxInner {
+ value: x,
+ rcu_head: Default::default(),
+ },
+ flags,
+ )?;
+
+ // INVARIANT:
+ // * The pointer contains a valid `RcuBoxInner` allocated with `A`.
+ // * We just allocated it, so we own free permissions.
+ Ok(RcuBox(NonNull::from(Box::leak(b)), PhantomData))
+ }
+
+ /// Access the value for a grace period.
+ pub fn with_rcu<'rcu>(&self, _read_guard: &'rcu Guard) -> &'rcu T {
+ // SAFETY: The `RcuBox` has not been dropped yet, so the value is valid for at least one
+ // grace period.
+ unsafe { &(*self.0.as_ptr()).value }
+ }
+}
+
+impl<T: Send, A: Allocator> Deref for RcuBox<T, A> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ // SAFETY: While the `RcuBox<T>` exists, the value remains valid.
+ unsafe { &(*self.0.as_ptr()).value }
+ }
+}
+
+// SAFETY:
+// * The `RcuBoxInner<T>` was allocated with `A`.
+// * `NonNull::as_ptr` returns a non-null pointer.
+unsafe impl<T: Send + 'static, A: Allocator> ForeignOwnable for RcuBox<T, A> {
+ const FOREIGN_ALIGN: usize = <Box<RcuBoxInner<T>, A> as ForeignOwnable>::FOREIGN_ALIGN;
+
+ type Borrowed<'a> = &'a T;
+ type BorrowedMut<'a> = &'a T;
+
+ fn into_foreign(self) -> *mut c_void {
+ self.0.as_ptr().cast()
+ }
+
+ unsafe fn from_foreign(ptr: *mut c_void) -> Self {
+ // INVARIANT: Pointer returned by `into_foreign, A` carries same invariants as `RcuBox<T>`.
+ // SAFETY: `into_foreign` never returns a null pointer.
+ Self(unsafe { NonNull::new_unchecked(ptr.cast()) }, PhantomData)
+ }
+
+ unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: Caller ensures that `'a` is short enough.
+ unsafe { &(*ptr.cast::<RcuBoxInner<T>>()).value }
+ }
+
+ unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: `borrow_mut` has strictly stronger preconditions than `borrow`.
+ unsafe { Self::borrow(ptr) }
+ }
+}
+
+impl<T: Send + 'static, A: Allocator> ForeignOwnableRcu for RcuBox<T, A> {
+ type RcuBorrowed<'a> = &'a T;
+
+ unsafe fn rcu_borrow<'a>(ptr: *mut c_void) -> &'a T {
+ // SAFETY: `RcuBox::drop` can only run after `from_foreign` is called, and the value is
+ // valid until `RcuBox::drop` plus one grace period.
+ unsafe { &(*ptr.cast::<RcuBoxInner<T>>()).value }
+ }
+}
+
+impl<T: Send, A: Allocator> Drop for RcuBox<T, A> {
+ fn drop(&mut self) {
+ // SAFETY: The `rcu_head` field is in-bounds of a valid allocation.
+ let rcu_head = unsafe { &raw mut (*self.0.as_ptr()).rcu_head };
+ if core::mem::needs_drop::<T>() {
+ // SAFETY: `rcu_head` is the `rcu_head` field of `RcuBoxInner<T>`. All users will be
+ // gone in an rcu grace period. This is the destructor, so we may pass ownership of the
+ // allocation.
+ unsafe { bindings::call_rcu(rcu_head, Some(drop_rcu_box::<T, A>)) };
+ } else {
+ // SAFETY: All users will be gone in an rcu grace period.
+ // TODO: We are luckily since `kvfree_call_rcu()` works on both kmalloc and vmalloc,
+ // maybe a new `Allocator` method is needed.
+ unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().cast()) };
+ }
+ }
+}
+
+/// Free this `RcuBoxInner<T>`.
+///
+/// # Safety
+///
+/// `head` references the `rcu_head` field of an `RcuBoxInner<T>` that has no references to it.
+/// Ownership of the `Box<RcuBoxInner<T>, A>` must be passed.
+unsafe extern "C" fn drop_rcu_box<T, A: Allocator>(head: *mut bindings::callback_head) {
+ // SAFETY: Caller provides a pointer to the `rcu_head` field of a `RcuBoxInner<T>`.
+ let box_inner = unsafe { crate::container_of!(head, RcuBoxInner<T>, rcu_head) };
+
+ // SAFETY: Caller ensures exclusive access and passed ownership.
+ drop(unsafe { Box::<_, A>::from_raw(box_inner) });
+}
+
+#[kunit_tests(rust_rcu_box)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn rcu_box_basic() -> Result {
+ let rb = RcuBox::<_, alloc::allocator::Kmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
+ let rb = RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
+ Ok(())
+ }
+}
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/3] rust: maple_tree: add load_rcu()
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: Add " Boqun Feng
@ 2026-06-05 13:35 ` Boqun Feng
2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:51 ` sashiko-bot
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: Add load_rcu() Boqun Feng
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
4 siblings, 2 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
From: Alice Ryhl <aliceryhl@google.com>
Now that we have a concept of rcu-safe containers, we may add a
load_rcu() method to MapleTree that does not take the spinlock.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
rust/kernel/maple_tree.rs | 52 +++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
index 265d6396a78a..1499191b8935 100644
--- a/rust/kernel/maple_tree.rs
+++ b/rust/kernel/maple_tree.rs
@@ -16,6 +16,10 @@
alloc::Flags,
error::to_result,
prelude::*,
+ sync::rcu::{
+ self,
+ ForeignOwnableRcu, //
+ },
types::{ForeignOwnable, Opaque},
};
@@ -233,6 +237,54 @@ pub fn erase(&self, index: usize) -> Option<T> {
unsafe { T::try_from_foreign(ret) }
}
+ /// Load the value at the given index with rcu.
+ ///
+ /// # Examples
+ ///
+ /// Read the value under an rcu read lock. Even if the value is removed, it remains accessible
+ /// for one rcu grace period.
+ ///
+ /// ```ignore
+ /// use kernel::{
+ /// maple_tree::MapleTree,
+ /// sync::rcu::{self, RcuBox},
+ /// };
+ ///
+ /// let tree = KBox::pin_init(MapleTree::<RcuBox<i32>>::new(), GFP_KERNEL)?;
+ ///
+ /// let ten = RcuBox::new(10, GFP_KERNEL)?;
+ /// tree.insert(100, ten, GFP_KERNEL)?;
+ ///
+ /// let rcu_read_lock = rcu::Guard::new();
+ /// let ten = tree.load_rcu(100, &rcu_read_lock);
+ /// assert_eq!(ten, Some(&10));
+ ///
+ /// // Even if the value gets removed, we may continue to access it for one rcu grace period.
+ /// tree.erase(100);
+ /// assert_eq!(ten, Some(&10));
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn load_rcu<'rcu>(
+ &self,
+ index: usize,
+ _rcu: &'rcu rcu::Guard,
+ ) -> Option<T::RcuBorrowed<'rcu>>
+ where
+ T: ForeignOwnableRcu,
+ {
+ // SAFETY: `self.tree` contains a valid maple tree.
+ let ret = unsafe { bindings::mtree_load(self.tree.get(), index) };
+ if ret.is_null() {
+ return None;
+ }
+
+ // SAFETY: If the pointer is not null, then it references a valid instance of `T`. It is
+ // safe to borrow the instance for 'rcu because the signature of this function enforces that
+ // the borrow does not outlive an rcu grace period.
+ Some(unsafe { T::rcu_borrow(ret) })
+ }
+
/// Lock the internal spinlock.
#[inline]
pub fn lock(&self) -> MapleGuard<'_, T> {
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/3] rust: maple_tree: Add load_rcu()
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
` (2 preceding siblings ...)
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: add load_rcu() Boqun Feng
@ 2026-06-05 13:35 ` Boqun Feng
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
4 siblings, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
From: Alice Ryhl <aliceryhl@google.com>
Now that we have a concept of rcu-safe containers, we may add a
load_rcu() method to MapleTree that does not take the spinlock.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
rust/kernel/maple_tree.rs | 52 +++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
index 265d6396a78a..1499191b8935 100644
--- a/rust/kernel/maple_tree.rs
+++ b/rust/kernel/maple_tree.rs
@@ -16,6 +16,10 @@
alloc::Flags,
error::to_result,
prelude::*,
+ sync::rcu::{
+ self,
+ ForeignOwnableRcu, //
+ },
types::{ForeignOwnable, Opaque},
};
@@ -233,6 +237,54 @@ pub fn erase(&self, index: usize) -> Option<T> {
unsafe { T::try_from_foreign(ret) }
}
+ /// Load the value at the given index with rcu.
+ ///
+ /// # Examples
+ ///
+ /// Read the value under an rcu read lock. Even if the value is removed, it remains accessible
+ /// for one rcu grace period.
+ ///
+ /// ```ignore
+ /// use kernel::{
+ /// maple_tree::MapleTree,
+ /// sync::rcu::{self, RcuBox},
+ /// };
+ ///
+ /// let tree = KBox::pin_init(MapleTree::<RcuBox<i32>>::new(), GFP_KERNEL)?;
+ ///
+ /// let ten = RcuBox::new(10, GFP_KERNEL)?;
+ /// tree.insert(100, ten, GFP_KERNEL)?;
+ ///
+ /// let rcu_read_lock = rcu::Guard::new();
+ /// let ten = tree.load_rcu(100, &rcu_read_lock);
+ /// assert_eq!(ten, Some(&10));
+ ///
+ /// // Even if the value gets removed, we may continue to access it for one rcu grace period.
+ /// tree.erase(100);
+ /// assert_eq!(ten, Some(&10));
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn load_rcu<'rcu>(
+ &self,
+ index: usize,
+ _rcu: &'rcu rcu::Guard,
+ ) -> Option<T::RcuBorrowed<'rcu>>
+ where
+ T: ForeignOwnableRcu,
+ {
+ // SAFETY: `self.tree` contains a valid maple tree.
+ let ret = unsafe { bindings::mtree_load(self.tree.get(), index) };
+ if ret.is_null() {
+ return None;
+ }
+
+ // SAFETY: If the pointer is not null, then it references a valid instance of `T`. It is
+ // safe to borrow the instance for 'rcu because the signature of this function enforces that
+ // the borrow does not outlive an rcu grace period.
+ Some(unsafe { T::rcu_borrow(ret) })
+ }
+
/// Lock the internal spinlock.
#[inline]
pub fn lock(&self) -> MapleGuard<'_, T> {
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
` (3 preceding siblings ...)
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: Add load_rcu() Boqun Feng
@ 2026-06-05 13:35 ` Boqun Feng
2026-06-05 13:46 ` sashiko-bot
` (2 more replies)
4 siblings, 3 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:35 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
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 | 31 +++++++++++++++
rust/kernel/sync/rcu/rcu_box.rs | 68 +++++++++++++++++++++++++++++++--
2 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index 7da6b8d22277..7c26591bb318 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::{
@@ -82,3 +84,32 @@ 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 {
+ 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 943fe3e8974e..8f52bb472daf 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.
@@ -205,6 +207,50 @@ 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)?))
+ }
+
+ /// Access the value for a grace period.
+ pub fn with_rcu<'rcu>(&self, read_guard: &'rcu Guard) -> &'rcu T {
+ self.0.with_rcu(read_guard)
+ }
+}
+
+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<T>` is transparet to `T`, adn `RcuBox` owns the object per type
+ // invariants.
+ let ptr = self.0 .0.as_ptr().cast::<T>();
+
+ // SAFETY: Per the invariants of `RcuBox`, `ptr` owns the pointed object. And we are not
+ // going to move it.
+ let pin = unsafe { Pin::new_unchecked(&mut *ptr) };
+
+ pin.drop_before_gp();
+
+ // `needs_drop::<ManuallyDrop>()` returns `false`, hence `kvfree_call_rcu()` will be called
+ // and free the underlying data after a gracer period.
+ }
+}
+
#[kunit_tests(rust_rcu_box)]
mod tests {
use super::*;
@@ -218,6 +264,13 @@ fn rcu_box_basic() -> Result {
drop(rb);
+ let rb = RcuFreeBox::<_, alloc::allocator::Kmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
let rb = RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
assert_eq!(*rb, 42);
@@ -225,6 +278,13 @@ fn rcu_box_basic() -> Result {
drop(rb);
+ let rb = RcuFreeBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
+
+ assert_eq!(*rb, 42);
+ assert_eq!(*rb.with_rcu(&Guard::new()), 42);
+
+ drop(rb);
+
Ok(())
}
}
--
2.51.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
@ 2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:49 ` sashiko-bot
1 sibling, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:38 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Liam R. Howlett, Andrew Ballance, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 05, 2026 at 06:35:37AM -0700, Boqun Feng wrote:
> From: Alice Ryhl <aliceryhl@google.com>
>
> This adds an RcuBox container, which is like Box except that the value
> is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
>
> To allow containers to rely on the RCU properties of RcuBox, an
> extension of ForeignOwnable is added.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> [boqun: Make RcuBox generic over Allocator and add tests]
> [boqun: Add type alias for Rcu*Box]
> Co-developed-by: Boqun Feng <boqun@kernel.org>
> Signed-off-by: Boqun Feng <boqun@kernel.org>
> ---
Please ignore this duplication...
Regards,
Boqun
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/3] rust: maple_tree: add load_rcu()
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: add load_rcu() Boqun Feng
@ 2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:51 ` sashiko-bot
1 sibling, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:38 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Liam R. Howlett, Andrew Ballance, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 05, 2026 at 06:35:39AM -0700, Boqun Feng wrote:
> From: Alice Ryhl <aliceryhl@google.com>
>
> Now that we have a concept of rcu-safe containers, we may add a
> load_rcu() method to MapleTree that does not take the spinlock.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
Please ignore this duplication...
Regards,
Boqun
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
@ 2026-06-05 13:46 ` sashiko-bot
2026-06-05 14:04 ` Alice Ryhl
2026-06-05 14:04 ` Boqun Feng
2 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-05 13:46 UTC (permalink / raw)
To: Boqun Feng; +Cc: dri-devel, linux-media, ojeda
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Pointer cast bypasses struct field offset, causing `drop_before_gp` to operate on `rcu_head` instead of `value`.
- [High] `RcuFreeBox::drop` creates a mutable reference (`&mut T`) before the RCU grace period elapses, violating Rust's strict aliasing rules.
- [High] `RcuFreeBox` wraps `T` in `ManuallyDrop<T>`, bypassing its destructor and creating an unresolvable leak or Use-After-Free dilemma.
- [Low] Missing `#[inline]` annotations on small/forwarding functions in the new abstraction.
- [Low] Multiple spelling errors in the code comments.
--
commit 78e35ac6ad00fb0c6ab7802573453596f9ee6134
Author: Boqun Feng <boqun@kernel.org>
rust: rcu: Introduce RcuFreeBox
The current RcuBox calls the drop function after a grace period inside an RCU
callback. To support a pattern where an object is cleaned up, unshared, and
freed after a grace period without readers, RcuFreeBox is introduced.
> diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
> index 943fe3e8974e0..8f52bb472daf9 100644
> --- a/rust/kernel/sync/rcu/rcu_box.rs
> +++ b/rust/kernel/sync/rcu/rcu_box.rs
[ ... ]
> @@ -205,6 +207,50 @@ 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> {
[Severity: Low]
Does this function need an inline annotation? The Rust subsystem guidelines
mention that small or forwarding functions in abstractions should be
annotated with #[inline].
> + Ok(Self(RcuBox::new(ManuallyDrop::new(x), flags)?))
> + }
> +
> + /// Access the value for a grace period.
> + pub fn with_rcu<'rcu>(&self, read_guard: &'rcu Guard) -> &'rcu T {
[Severity: Low]
Should this small forwarding function also have an #[inline] annotation?
> + self.0.with_rcu(read_guard)
> + }
> +}
> +
> +impl<T: Send + RcuFreeSafe, A: Allocator> Deref for RcuFreeBox<T, A> {
> + type Target = T;
> +
> + fn deref(&self) -> &T {
[Severity: Low]
Would an #[inline] annotation be appropriate for this Deref implementation
as well, since it is a simple forwarding call?
> + self.0.deref()
> + }
> +}
> +
> +impl<T: Send + RcuFreeSafe, A: Allocator> Drop for RcuFreeBox<T, A> {
> + fn drop(&mut self) {
> + // CAST: ManuallyDrop<T> is transparet to T, adn RcuBox owns the object per type
[Severity: Low]
There are a couple of typos here: transparet instead of transparent, and adn
instead of and.
> + // invariants.
> + let ptr = self.0 .0.as_ptr().cast::<T>();
[Severity: Critical]
Is this pointer cast skipping the struct field offset?
self.0.0.as_ptr() returns a pointer to RcuBoxInner<ManuallyDrop<T>>. The
RcuBoxInner struct starts with an rcu_head field, meaning the actual value
field is located at a non-zero offset.
Casting this pointer directly to *mut T will point to the rcu_head memory
instead of the value.
Could calling drop_before_gp() on this pointer corrupt memory by treating
the internal rcu_head structure bytes as if they were of type T?
> +
> + // SAFETY: Per the invariants of RcuBox, ptr owns the pointed object. And we are not
> + // going to move it.
> + let pin = unsafe { Pin::new_unchecked(&mut *ptr) };
[Severity: High]
Does creating a mutable reference here violate Rust's strict aliasing rules?
When an RcuFreeBox is dropped, it is typically removed from the shared data
structure, but the RCU grace period has not yet elapsed. Concurrent readers
might still hold shared references (&T) obtained via with_rcu.
Creating an exclusive mutable reference (&mut T) using Pin::new_unchecked
while shared references may still exist is immediate undefined behavior in
Rust.
> +
> + pin.drop_before_gp();
> +
> + // needs_drop::<ManuallyDrop>() returns false, hence kvfree_call_rcu() will be called
[Severity: High]
Could bypassing the destructor using ManuallyDrop<T> create an unresolvable
leak or use-after-free regression?
Since needs_drop::<ManuallyDrop<T>>() evaluates to false, it skips dropping
the internals of T. If T holds owned resources like a Box or Vec, they are
never freed after the grace period, which leaks memory.
Conversely, if a user tries to work around this by manually freeing
resources inside drop_before_gp(), could that trigger a use-after-free
since drop_before_gp() runs before the RCU grace period ends while
concurrent readers might still be accessing those resources?
> + // and free the underlying data after a gracer period.
[Severity: Low]
There is a typo here: gracer instead of grace.
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260605133541.22569-1-boqun@kernel.org?part=3
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
2026-06-05 13:38 ` Boqun Feng
@ 2026-06-05 13:49 ` sashiko-bot
2026-06-05 13:58 ` Boqun Feng
2026-06-05 14:41 ` Boqun Feng
1 sibling, 2 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-05 13:49 UTC (permalink / raw)
To: Boqun Feng; +Cc: ojeda, dri-devel, linux-media
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] RcuBox::into_foreign drops `self` instead of forgetting it, causing a guaranteed Use-After-Free.
- [High] RcuBox::drop bypasses the custom Allocator A and hardcodes kvfree_call_rcu() for types that do not require dropping, causing undefined behavior for non-kvfree compatible allocators.
--
commit f69c06884595bb68a76f4f1dd4014ee9e2c9e31c
Author: Alice Ryhl <aliceryhl@google.com>
rust: rcu: add RcuBox type
This adds an RcuBox container, which is like Box except that the value
is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
To allow containers to rely on the RCU properties of RcuBox, an
extension of ForeignOwnable is added.
> diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
> new file mode 100644
> index 0000000000000..943fe3e8974e0
> --- /dev/null
> +++ b/rust/kernel/sync/rcu/rcu_box.rs
[ ... ]
> +// SAFETY:
> +// * The `RcuBoxInner<T>` was allocated with `A`.
> +// * `NonNull::as_ptr` returns a non-null pointer.
> +unsafe impl<T: Send + 'static, A: Allocator> ForeignOwnable for RcuBox<T, A> {
> + const FOREIGN_ALIGN: usize = <Box<RcuBoxInner<T>, A> as ForeignOwnable>::FOREIGN_ALIGN;
> +
> + type Borrowed<'a> = &'a T;
> + type BorrowedMut<'a> = &'a T;
> +
> + fn into_foreign(self) -> *mut c_void {
> + self.0.as_ptr().cast()
> + }
[Severity: Critical]
Since `self` is consumed by value here without being wrapped in `ManuallyDrop`
or passed to `core::mem::forget()`, won't it be dropped at the end of this
method?
This seems like it would immediately schedule the memory for RCU free, causing
a use-after-free for the foreign code when it later accesses the pointer.
> +
> + unsafe fn from_foreign(ptr: *mut c_void) -> Self {
[ ... ]
> +impl<T: Send, A: Allocator> Drop for RcuBox<T, A> {
> + fn drop(&mut self) {
> + // SAFETY: The `rcu_head` field is in-bounds of a valid allocation.
> + let rcu_head = unsafe { &raw mut (*self.0.as_ptr()).rcu_head };
> + if core::mem::needs_drop::<T>() {
> + // SAFETY: `rcu_head` is the `rcu_head` field of `RcuBoxInner<T>`. All users will be
> + // gone in an rcu grace period. This is the destructor, so we may pass ownership of the
> + // allocation.
> + unsafe { bindings::call_rcu(rcu_head, Some(drop_rcu_box::<T, A>)) };
> + } else {
> + // SAFETY: All users will be gone in an rcu grace period.
> + // TODO: We are luckily since `kvfree_call_rcu()` works on both kmalloc and vmalloc,
> + // maybe a new `Allocator` method is needed.
> + unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().cast()) };
[Severity: High]
Since `RcuBox` is generic over any custom `A: Allocator`, wouldn't calling
`kvfree_call_rcu()` here break for allocators other than kmalloc or vmalloc?
Does this need to always route through `A::free()` via `drop_rcu_box`, or
should `A` be explicitly bounded to an allocator trait that supports
`kvfree_rcu`?
> + }
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260605133541.22569-1-boqun@kernel.org?part=1
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/3] rust: maple_tree: add load_rcu()
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: add load_rcu() Boqun Feng
2026-06-05 13:38 ` Boqun Feng
@ 2026-06-05 13:51 ` sashiko-bot
1 sibling, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-05 13:51 UTC (permalink / raw)
To: Boqun Feng; +Cc: dri-devel, ojeda, linux-media
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Lockless traversal in `load_rcu()` causes a use-after-free of internal mapletree nodes because the tree is not initialized with `MT_FLAGS_USE_RCU`.
- [High] Unsound API: `RcuBox::new` safely allows types with blocking destructors to execute their `Drop` implementation in RCU softirq context, leading to atomic context violations.
--
commit b7609fa7d8f12dc31f5e195b6f4d6155f2a20574
Author: Alice Ryhl <aliceryhl@google.com>
rust: maple_tree: add load_rcu()
This commit adds a load_rcu() method to MapleTree, allowing lookups under an
RCU read lock without taking the spinlock.
> diff --git a/rust/kernel/maple_tree.rs b/rust/kernel/maple_tree.rs
> index 265d6396a78a1..1499191b8935a 100644
> --- a/rust/kernel/maple_tree.rs
> +++ b/rust/kernel/maple_tree.rs
[ ... ]
> @@ -233,6 +237,54 @@ pub fn erase(&self, index: usize) -> Option<T> {
> unsafe { T::try_from_foreign(ret) }
> }
>
> + /// Load the value at the given index with rcu.
> + ///
> + /// # Examples
> + ///
> + /// Read the value under an rcu read lock. Even if the value is removed, it remains accessible
> + /// for one rcu grace period.
> + ///
> + /// ```ignore
> + /// use kernel::{
> + /// maple_tree::MapleTree,
> + /// sync::rcu::{self, RcuBox},
> + /// };
> + ///
> + /// let tree = KBox::pin_init(MapleTree::<RcuBox<i32>>::new(), GFP_KERNEL)?;
> + ///
> + /// let ten = RcuBox::new(10, GFP_KERNEL)?;
[Severity: High]
Is it safe to allow arbitrary types to be managed via RcuBox?
RcuBox::new() is a safe function that accepts any T: Send. It lacks trait
bounds to restrict T to types that are safe to drop in atomic contexts.
When an RcuBox is dropped, if T requires dropping, it schedules drop_rcu_box()
using bindings::call_rcu(). The drop_rcu_box() callback executes in an RCU
softirq context, where sleeping or blocking is strictly forbidden.
If a safe T blocks during its Drop implementation (for example, safely
acquiring a kernel::sync::Mutex on drop), will this execute in softirq
context and cause a scheduling-while-atomic panic?
> + /// tree.insert(100, ten, GFP_KERNEL)?;
> + ///
> + /// let rcu_read_lock = rcu::Guard::new();
> + /// let ten = tree.load_rcu(100, &rcu_read_lock);
> + /// assert_eq!(ten, Some(&10));
> + ///
> + /// // Even if the value gets removed, we may continue to access it for one rcu grace period.
> + /// tree.erase(100);
> + /// assert_eq!(ten, Some(&10));
> + /// # Ok::<_, Error>(())
> + /// ```
> + #[inline]
> + pub fn load_rcu<'rcu>(
> + &self,
> + index: usize,
> + _rcu: &'rcu rcu::Guard,
> + ) -> Option<T::RcuBorrowed<'rcu>>
> + where
> + T: ForeignOwnableRcu,
> + {
> + // SAFETY: `self.tree` contains a valid maple tree.
> + let ret = unsafe { bindings::mtree_load(self.tree.get(), index) };
[Severity: Critical]
Does this lockless traversal risk a use-after-free?
Looking at MapleTree::new() and MapleTreeAlloc::new() in
rust/kernel/maple_tree.rs, the tree is initialized with 0 or
bindings::MT_FLAGS_ALLOC_RANGE respectively, without bindings::MT_FLAGS_USE_RCU.
If MT_FLAGS_USE_RCU is not set, structural modifications to the tree (like
node splits or merges) will synchronously free the replaced internal tree
nodes directly to the slab allocator via kmem_cache_free(), completely
bypassing call_rcu().
Could a concurrent load_rcu() walking the tree dereference a node pointer
that was just synchronously freed by a writer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260605133541.22569-1-boqun@kernel.org?part=2
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 13:49 ` sashiko-bot
@ 2026-06-05 13:58 ` Boqun Feng
2026-06-05 14:41 ` Boqun Feng
1 sibling, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 13:58 UTC (permalink / raw)
To: sashiko-reviews; +Cc: ojeda, dri-devel, linux-media
On Fri, Jun 05, 2026 at 01:49:35PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Critical] RcuBox::into_foreign drops `self` instead of forgetting it, causing a guaranteed Use-After-Free.
> - [High] RcuBox::drop bypasses the custom Allocator A and hardcodes kvfree_call_rcu() for types that do not require dropping, causing undefined behavior for non-kvfree compatible allocators.
> --
>
[...]
> > +
> > + unsafe fn from_foreign(ptr: *mut c_void) -> Self {
>
> [ ... ]
>
> > +impl<T: Send, A: Allocator> Drop for RcuBox<T, A> {
> > + fn drop(&mut self) {
> > + // SAFETY: The `rcu_head` field is in-bounds of a valid allocation.
> > + let rcu_head = unsafe { &raw mut (*self.0.as_ptr()).rcu_head };
> > + if core::mem::needs_drop::<T>() {
> > + // SAFETY: `rcu_head` is the `rcu_head` field of `RcuBoxInner<T>`. All users will be
> > + // gone in an rcu grace period. This is the destructor, so we may pass ownership of the
> > + // allocation.
> > + unsafe { bindings::call_rcu(rcu_head, Some(drop_rcu_box::<T, A>)) };
> > + } else {
> > + // SAFETY: All users will be gone in an rcu grace period.
> > + // TODO: We are luckily since `kvfree_call_rcu()` works on both kmalloc and vmalloc,
> > + // maybe a new `Allocator` method is needed.
> > + unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().cast()) };
>
> [Severity: High]
> Since `RcuBox` is generic over any custom `A: Allocator`, wouldn't calling
> `kvfree_call_rcu()` here break for allocators other than kmalloc or vmalloc?
>
> Does this need to always route through `A::free()` via `drop_rcu_box`, or
> should `A` be explicitly bounded to an allocator trait that supports
> `kvfree_rcu`?
>
There is a "TODO" here to describe why currently kvfree_call_rcu() is
fine, is this a real issue in the current code base?
Regards,
Boqun
> > + }
> > + }
> > +}
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260605133541.22569-1-boqun@kernel.org?part=1
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
2026-06-05 13:46 ` sashiko-bot
@ 2026-06-05 14:04 ` Alice Ryhl
2026-06-05 14:20 ` Boqun Feng
2026-06-05 14:04 ` Boqun Feng
2 siblings, 1 reply; 19+ messages in thread
From: Alice Ryhl @ 2026-06-05 14:04 UTC (permalink / raw)
To: Boqun Feng
Cc: linux-kernel, rust-for-linux, rcu, Miguel Ojeda, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 05, 2026 at 06:35:41AM -0700, Boqun Feng wrote:
> 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 | 31 +++++++++++++++
> rust/kernel/sync/rcu/rcu_box.rs | 68 +++++++++++++++++++++++++++++++--
> 2 files changed, 95 insertions(+), 4 deletions(-)
>
> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index 7da6b8d22277..7c26591bb318 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::{
> @@ -82,3 +84,32 @@ 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 {
> + fn drop_before_gp(self: Pin<&mut Self>);
> +}
Should this have an associated type for the rcu-safe view?
pub unsafe trait RcuFreeSafe {
type RcuView<'a>;
/// Access this value in a manner that is safe after
/// `drop_before_gp` for one grace period.
fn rcu_view<'a>(self: Pin<&'a Self>, _rcu: &'a RcuGuard) -> Self::RcuView<'a>;
/// Drop this value in a manner where it may still be accessed via
/// `rcu_view` for one grace period.
///
/// # Safety
///
/// All other accesses to this value must happen before the call to this
/// method, except for accesses using `rcu_view`.
fn drop_before_gp(self: Pin<&mut Self>);
}
The idea being that once you call `drop_before_gp()`, the value
immediately becomes unusable as the type itself, but you can still use
it via `rcu_view`. The `RcuView` type can then be a type that has a
subset of the type's methods that is safe to use for one grace period
after `drop_before_gp`.
If you define the trait like this, then PollCondVar becomes RcuFreeSafe.
It can't be RcuFreeSafe today because you must not create new waiters after
`drop_before_gp()` is called. With this modified trait, it can simply
not provide methods for registering new waiters from the RcuView type.
Alice
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
2026-06-05 13:46 ` sashiko-bot
2026-06-05 14:04 ` Alice Ryhl
@ 2026-06-05 14:04 ` Boqun Feng
2 siblings, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 14:04 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, rcu
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Liam R. Howlett, Andrew Ballance, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 05, 2026 at 06:35:41AM -0700, Boqun Feng wrote:
> 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 | 31 +++++++++++++++
> rust/kernel/sync/rcu/rcu_box.rs | 68 +++++++++++++++++++++++++++++++--
> 2 files changed, 95 insertions(+), 4 deletions(-)
>
> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index 7da6b8d22277..7c26591bb318 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::{
> @@ -82,3 +84,32 @@ 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 {
> + 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 943fe3e8974e..8f52bb472daf 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.
> @@ -205,6 +207,50 @@ 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)?))
> + }
> +
> + /// Access the value for a grace period.
> + pub fn with_rcu<'rcu>(&self, read_guard: &'rcu Guard) -> &'rcu T {
> + self.0.with_rcu(read_guard)
> + }
> +}
> +
> +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<T>` is transparet to `T`, adn `RcuBox` owns the object per type
> + // invariants.
> + let ptr = self.0 .0.as_ptr().cast::<T>();
> +
> + // SAFETY: Per the invariants of `RcuBox`, `ptr` owns the pointed object. And we are not
> + // going to move it.
> + let pin = unsafe { Pin::new_unchecked(&mut *ptr) };
> +
This part needs to be:
// 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) };
Fixed locally.
Regards,
Boqun
> + pin.drop_before_gp();
> +
> + // `needs_drop::<ManuallyDrop>()` returns `false`, hence `kvfree_call_rcu()` will be called
> + // and free the underlying data after a gracer period.
> + }
> +}
> +
> #[kunit_tests(rust_rcu_box)]
> mod tests {
> use super::*;
> @@ -218,6 +264,13 @@ fn rcu_box_basic() -> Result {
>
> drop(rb);
>
> + let rb = RcuFreeBox::<_, alloc::allocator::Kmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
> +
> + assert_eq!(*rb, 42);
> + assert_eq!(*rb.with_rcu(&Guard::new()), 42);
> +
> + drop(rb);
> +
> let rb = RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
>
> assert_eq!(*rb, 42);
> @@ -225,6 +278,13 @@ fn rcu_box_basic() -> Result {
>
> drop(rb);
>
> + let rb = RcuFreeBox::<_, alloc::allocator::Vmalloc>::new(42i32, alloc::flags::GFP_KERNEL)?;
> +
> + assert_eq!(*rb, 42);
> + assert_eq!(*rb.with_rcu(&Guard::new()), 42);
> +
> + drop(rb);
> +
> Ok(())
> }
> }
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 14:04 ` Alice Ryhl
@ 2026-06-05 14:20 ` Boqun Feng
2026-06-05 14:54 ` Alice Ryhl
0 siblings, 1 reply; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 14:20 UTC (permalink / raw)
To: Alice Ryhl
Cc: linux-kernel, rust-for-linux, rcu, Miguel Ojeda, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 05, 2026 at 02:04:08PM +0000, Alice Ryhl wrote:
> On Fri, Jun 05, 2026 at 06:35:41AM -0700, Boqun Feng wrote:
> > 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 | 31 +++++++++++++++
> > rust/kernel/sync/rcu/rcu_box.rs | 68 +++++++++++++++++++++++++++++++--
> > 2 files changed, 95 insertions(+), 4 deletions(-)
> >
> > diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> > index 7da6b8d22277..7c26591bb318 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::{
> > @@ -82,3 +84,32 @@ 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 {
> > + fn drop_before_gp(self: Pin<&mut Self>);
> > +}
>
> Should this have an associated type for the rcu-safe view?
>
> pub unsafe trait RcuFreeSafe {
> type RcuView<'a>;
>
> /// Access this value in a manner that is safe after
> /// `drop_before_gp` for one grace period.
> fn rcu_view<'a>(self: Pin<&'a Self>, _rcu: &'a RcuGuard) -> Self::RcuView<'a>;
>
> /// Drop this value in a manner where it may still be accessed via
> /// `rcu_view` for one grace period.
> ///
> /// # Safety
> ///
> /// All other accesses to this value must happen before the call to this
> /// method, except for accesses using `rcu_view`.
> fn drop_before_gp(self: Pin<&mut Self>);
> }
>
> The idea being that once you call `drop_before_gp()`, the value
> immediately becomes unusable as the type itself, but you can still use
> it via `rcu_view`. The `RcuView` type can then be a type that has a
> subset of the type's methods that is safe to use for one grace period
> after `drop_before_gp`.
>
> If you define the trait like this, then PollCondVar becomes RcuFreeSafe.
> It can't be RcuFreeSafe today because you must not create new waiters after
> `drop_before_gp()` is called. With this modified trait, it can simply
> not provide methods for registering new waiters from the RcuView type.
>
Good point! But I guess I could keep RcuFreeSafe as it is but remove the
`RcuFreeSafe::with_rcu()`, and this should be sufficient for
PollCondVar. I do want to wait for a meaningful usage of `rcu_view()` to
add that, but I think your idea of that is great. Or maybe you have a
potential usage in mind?
Regards,
Boqun
> Alice
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 13:49 ` sashiko-bot
2026-06-05 13:58 ` Boqun Feng
@ 2026-06-05 14:41 ` Boqun Feng
2026-06-05 14:54 ` Alice Ryhl
1 sibling, 1 reply; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 14:41 UTC (permalink / raw)
To: sashiko-reviews; +Cc: ojeda, dri-devel, linux-media, Alice Ryhl
On Fri, Jun 05, 2026 at 01:49:35PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Critical] RcuBox::into_foreign drops `self` instead of forgetting it, causing a guaranteed Use-After-Free.
> - [High] RcuBox::drop bypasses the custom Allocator A and hardcodes kvfree_call_rcu() for types that do not require dropping, causing undefined behavior for non-kvfree compatible allocators.
> --
>
> commit f69c06884595bb68a76f4f1dd4014ee9e2c9e31c
> Author: Alice Ryhl <aliceryhl@google.com>
>
> rust: rcu: add RcuBox type
>
> This adds an RcuBox container, which is like Box except that the value
> is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
> To allow containers to rely on the RCU properties of RcuBox, an
> extension of ForeignOwnable is added.
>
> > diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
> > new file mode 100644
> > index 0000000000000..943fe3e8974e0
> > --- /dev/null
> > +++ b/rust/kernel/sync/rcu/rcu_box.rs
>
> [ ... ]
>
> > +// SAFETY:
> > +// * The `RcuBoxInner<T>` was allocated with `A`.
> > +// * `NonNull::as_ptr` returns a non-null pointer.
> > +unsafe impl<T: Send + 'static, A: Allocator> ForeignOwnable for RcuBox<T, A> {
> > + const FOREIGN_ALIGN: usize = <Box<RcuBoxInner<T>, A> as ForeignOwnable>::FOREIGN_ALIGN;
> > +
> > + type Borrowed<'a> = &'a T;
> > + type BorrowedMut<'a> = &'a T;
> > +
> > + fn into_foreign(self) -> *mut c_void {
> > + self.0.as_ptr().cast()
> > + }
>
> [Severity: Critical]
> Since `self` is consumed by value here without being wrapped in `ManuallyDrop`
> or passed to `core::mem::forget()`, won't it be dropped at the end of this
> method?
>
> This seems like it would immediately schedule the memory for RCU free, causing
> a use-after-free for the foreign code when it later accesses the pointer.
>
[Cc Alice]
I fixed this locally by:
fn into_foreign(self) -> *mut c_void {
let ptr = self.0.as_ptr().cast();
// FORGET: Leaking it as a pointer, the caller is responsible to free it or unleak it by
// from_foreign().
core::mem::forget(self);
ptr
}
Regards,
Boqun
> > +
> > + unsafe fn from_foreign(ptr: *mut c_void) -> Self {
>
[..]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox
2026-06-05 14:20 ` Boqun Feng
@ 2026-06-05 14:54 ` Alice Ryhl
0 siblings, 0 replies; 19+ messages in thread
From: Alice Ryhl @ 2026-06-05 14:54 UTC (permalink / raw)
To: Boqun Feng
Cc: linux-kernel, rust-for-linux, rcu, Miguel Ojeda, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Liam R. Howlett, Andrew Ballance,
Paul E. McKenney, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Sumit Semwal,
Christian König, maple-tree, linux-mm, linux-media,
dri-devel, Philipp Stanner, Lyude Paul, Daniel Almeida,
Onur Özkan
On Fri, Jun 5, 2026 at 4:20 PM Boqun Feng <boqun@kernel.org> wrote:
>
> On Fri, Jun 05, 2026 at 02:04:08PM +0000, Alice Ryhl wrote:
> > On Fri, Jun 05, 2026 at 06:35:41AM -0700, Boqun Feng wrote:
> > > 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 | 31 +++++++++++++++
> > > rust/kernel/sync/rcu/rcu_box.rs | 68 +++++++++++++++++++++++++++++++--
> > > 2 files changed, 95 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> > > index 7da6b8d22277..7c26591bb318 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::{
> > > @@ -82,3 +84,32 @@ 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 {
> > > + fn drop_before_gp(self: Pin<&mut Self>);
> > > +}
> >
> > Should this have an associated type for the rcu-safe view?
> >
> > pub unsafe trait RcuFreeSafe {
> > type RcuView<'a>;
> >
> > /// Access this value in a manner that is safe after
> > /// `drop_before_gp` for one grace period.
> > fn rcu_view<'a>(self: Pin<&'a Self>, _rcu: &'a RcuGuard) -> Self::RcuView<'a>;
> >
> > /// Drop this value in a manner where it may still be accessed via
> > /// `rcu_view` for one grace period.
> > ///
> > /// # Safety
> > ///
> > /// All other accesses to this value must happen before the call to this
> > /// method, except for accesses using `rcu_view`.
> > fn drop_before_gp(self: Pin<&mut Self>);
> > }
> >
> > The idea being that once you call `drop_before_gp()`, the value
> > immediately becomes unusable as the type itself, but you can still use
> > it via `rcu_view`. The `RcuView` type can then be a type that has a
> > subset of the type's methods that is safe to use for one grace period
> > after `drop_before_gp`.
> >
> > If you define the trait like this, then PollCondVar becomes RcuFreeSafe.
> > It can't be RcuFreeSafe today because you must not create new waiters after
> > `drop_before_gp()` is called. With this modified trait, it can simply
> > not provide methods for registering new waiters from the RcuView type.
> >
>
> Good point! But I guess I could keep RcuFreeSafe as it is but remove the
> `RcuFreeSafe::with_rcu()`, and this should be sufficient for
> PollCondVar. I do want to wait for a meaningful usage of `rcu_view()` to
> add that, but I think your idea of that is great. Or maybe you have a
> potential usage in mind?
For the dma fence, the fence context would let you access the name c
string via the rcu view.
Alice
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 14:41 ` Boqun Feng
@ 2026-06-05 14:54 ` Alice Ryhl
2026-06-05 15:33 ` Boqun Feng
0 siblings, 1 reply; 19+ messages in thread
From: Alice Ryhl @ 2026-06-05 14:54 UTC (permalink / raw)
To: Boqun Feng; +Cc: sashiko-reviews, ojeda, dri-devel, linux-media
On Fri, Jun 5, 2026 at 4:41 PM Boqun Feng <boqun@kernel.org> wrote:
>
> On Fri, Jun 05, 2026 at 01:49:35PM +0000, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> > - [Critical] RcuBox::into_foreign drops `self` instead of forgetting it, causing a guaranteed Use-After-Free.
> > - [High] RcuBox::drop bypasses the custom Allocator A and hardcodes kvfree_call_rcu() for types that do not require dropping, causing undefined behavior for non-kvfree compatible allocators.
> > --
> >
> > commit f69c06884595bb68a76f4f1dd4014ee9e2c9e31c
> > Author: Alice Ryhl <aliceryhl@google.com>
> >
> > rust: rcu: add RcuBox type
> >
> > This adds an RcuBox container, which is like Box except that the value
> > is freed after waiting for one grace period (via {kvfree_,}call_rcu()).
> > To allow containers to rely on the RCU properties of RcuBox, an
> > extension of ForeignOwnable is added.
> >
> > > diff --git a/rust/kernel/sync/rcu/rcu_box.rs b/rust/kernel/sync/rcu/rcu_box.rs
> > > new file mode 100644
> > > index 0000000000000..943fe3e8974e0
> > > --- /dev/null
> > > +++ b/rust/kernel/sync/rcu/rcu_box.rs
> >
> > [ ... ]
> >
> > > +// SAFETY:
> > > +// * The `RcuBoxInner<T>` was allocated with `A`.
> > > +// * `NonNull::as_ptr` returns a non-null pointer.
> > > +unsafe impl<T: Send + 'static, A: Allocator> ForeignOwnable for RcuBox<T, A> {
> > > + const FOREIGN_ALIGN: usize = <Box<RcuBoxInner<T>, A> as ForeignOwnable>::FOREIGN_ALIGN;
> > > +
> > > + type Borrowed<'a> = &'a T;
> > > + type BorrowedMut<'a> = &'a T;
> > > +
> > > + fn into_foreign(self) -> *mut c_void {
> > > + self.0.as_ptr().cast()
> > > + }
> >
> > [Severity: Critical]
> > Since `self` is consumed by value here without being wrapped in `ManuallyDrop`
> > or passed to `core::mem::forget()`, won't it be dropped at the end of this
> > method?
> >
> > This seems like it would immediately schedule the memory for RCU free, causing
> > a use-after-free for the foreign code when it later accesses the pointer.
> >
>
> [Cc Alice]
>
> I fixed this locally by:
>
> fn into_foreign(self) -> *mut c_void {
> let ptr = self.0.as_ptr().cast();
>
> // FORGET: Leaking it as a pointer, the caller is responsible to free it or unleak it by
> // from_foreign().
> core::mem::forget(self);
>
> ptr
> }
I prefer to avoid use core::mem::forget.
ManuallyDrop(self).0.as_ptr().cast()
Alice
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 1/3] rust: rcu: add RcuBox type
2026-06-05 14:54 ` Alice Ryhl
@ 2026-06-05 15:33 ` Boqun Feng
0 siblings, 0 replies; 19+ messages in thread
From: Boqun Feng @ 2026-06-05 15:33 UTC (permalink / raw)
To: Alice Ryhl; +Cc: sashiko-reviews, ojeda, dri-devel, linux-media
On Fri, Jun 05, 2026 at 04:54:55PM +0200, Alice Ryhl wrote:
[..]
> >
> > [Cc Alice]
> >
> > I fixed this locally by:
> >
> > fn into_foreign(self) -> *mut c_void {
> > let ptr = self.0.as_ptr().cast();
> >
> > // FORGET: Leaking it as a pointer, the caller is responsible to free it or unleak it by
> > // from_foreign().
> > core::mem::forget(self);
> >
> > ptr
> > }
>
> I prefer to avoid use core::mem::forget.
>
> ManuallyDrop(self).0.as_ptr().cast()
>
Sounds good ;-)
Regards,
Boqun
> Alice
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-06-05 15:33 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 13:35 [PATCH 0/3] rust: sync: Introduce Rcu*Box Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: add RcuBox type Boqun Feng
2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:49 ` sashiko-bot
2026-06-05 13:58 ` Boqun Feng
2026-06-05 14:41 ` Boqun Feng
2026-06-05 14:54 ` Alice Ryhl
2026-06-05 15:33 ` Boqun Feng
2026-06-05 13:35 ` [PATCH 1/3] rust: rcu: Add " Boqun Feng
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: add load_rcu() Boqun Feng
2026-06-05 13:38 ` Boqun Feng
2026-06-05 13:51 ` sashiko-bot
2026-06-05 13:35 ` [PATCH 2/3] rust: maple_tree: Add load_rcu() Boqun Feng
2026-06-05 13:35 ` [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Boqun Feng
2026-06-05 13:46 ` sashiko-bot
2026-06-05 14:04 ` Alice Ryhl
2026-06-05 14:20 ` Boqun Feng
2026-06-05 14:54 ` Alice Ryhl
2026-06-05 14:04 ` Boqun Feng
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.