From nobody Mon Jun 8 05:25:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A0E92F7F1D for ; Fri, 5 Jun 2026 13:35:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666557; cv=none; b=fk9kejMbp5KDMbquaa9m/XIloe+GaOp9J9uUgJSJIn2yNPWitCDabA9ukO+HUuxCUGUcxxoeRwWDY47uWskh73DiNjzwxTTzHTPGZileyT9oiHCqYCbd9cb/f71WeqOARmY6V34aetEo0bJfocVQV2u3islj51bWu3ZNbz6MQ7Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666557; c=relaxed/simple; bh=wlb6NkJIxz4wYIsUfd+v9sn7xOMMQEdTW7zEFBMBkQ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JOSFTOt49pe2SqR1GtkGc+O2SB++0DP+Dzp9tuOH/Abj2wpfdE4OOSbUmZSqwCciC2OlnYWMwRa5fK3n/GSrllRcInrx1X/vK+Ld9SdDrE8tXUQPpkH7xwjeOTndedXRfqAtRekJj3rwUhgZXJWsO9FUwA2wOPCaeLPImUyzZMo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KC+dlDQu; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KC+dlDQu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 159A01F0089B; Fri, 5 Jun 2026 13:35:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780666553; bh=J7xoJI8ZQKXEZmPwD70InQg1kBZths2uP7Ky23DxVzU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KC+dlDQuLqP3rCxErxpehrd3GNGE3Vr0cTPgWZSw6PUJxdbN+0G0p0UdThXjimlJn +LYiEFSfMV678aDl9NPwnj0wiblRyUXunenVKEUldJerWiCpV/M0DtOGalY/xbTI4K R8L/WJeyh567HlbuTzPiCpo7V4GakqK/hScoGCp57ji37Wm/1b8dqWxkHG3KYg4hrX umGAXuRN4afcDy7tMpplcZXMK8SR2/Jpx/k400iGbVaU34s0BdQ84Sr2zg3K9g1lXX YZ0AeI/SKm48a+e4vqlaF3Z/S6GQr+TdDBkC65NOIfGNOod+po10Hd5gMvW0BRsKa+ To3F+BJJQLjRA== Received: from phl-compute-02.internal (phl-compute-02.internal [10.202.2.42]) by mailfauth.phl.internal (Postfix) with ESMTP id 62C58F40073; Fri, 5 Jun 2026 09:35:51 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-02.internal (MEProxy); Fri, 05 Jun 2026 09:35:51 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTEiXmK80I+r4b+5zY/r99GUAlO/A1JeBVvrzdPJND7AxnXROcGIdrV5+IW/GIICbk HTeSUzo2jG7T3ubuXDYawvMsJmw7SeRzXaVjmYhKpesaWDi/jHlBwDSw7cUJtOL4Zml+HR MdH91GjghnCvkbpZdNzjjo1bSZDmeAE+i20ut4ugSeq7SenIcQ9DZOfghRt4xUUc5uh5PM SgAN181ZxF1oco7PoJ9ho3wb1kPOXsEzQT5UZv0CFFx1JIqpGEiANmmsdwoALURinC3es0 +e8I9nc2247wfagtNBtbgyVV8BvBRc03DGsTzzDlNTrxNmxxQ9+IH7k1Xg1Flh6mzaY9bd AEOM693ILT2slEnUuoWD3iq+rjQAzyNUfHpc9WNRNUfsHLrNji0x8w3uLNBc6+3ow07+Dk SThl9X6VaY++QguKiFA37qMh2ASMDg/GN+HC+gkV+Enk9JIe/G8JD8fL7COLcBvKe9Pd4Y Y/pz1AXQuX3+LyaSHVKaG7PbY1QP84DdL2fbreWykz7+tMN/L34WkvzRSUoArdQLE3Izbw /NzdSmSLSeIeQ2A9wGqAKXuMlS0s5vywlwasYjNUKfk7zqhTktXnUqE3hD0nBt8EVGPDLe Xr7K221pN74VQYwYTrRRAfbbXvcTzF4fhG8Q7jry/h9aW3T2dWU6y2Gz2Fog X-ME-Proxy: Feedback-ID: i8dbe485b:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 5 Jun 2026 09:35:50 -0400 (EDT) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , 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 , =?UTF-8?q?Christian=20K=C3=B6nig?= , maple-tree@lists.infradead.org, linux-mm@kvack.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, Philipp Stanner , Lyude Paul , Daniel Almeida , =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH 1/3] rust: rcu: Add RcuBox type Date: Fri, 5 Jun 2026 06:35:38 -0700 Message-ID: <20260605133541.22569-3-boqun@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605133541.22569-1-boqun@kernel.org> References: <20260605133541.22569-1-boqun@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alice Ryhl 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 [boqun: Make RcuBox generic over Allocator and add tests] [boqun: Add type alias for Rcu*Box] Co-developed-by: Boqun Feng Signed-off-by: Boqun Feng --- 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_helpe= r.h index 446dbeaf0866..2011645c7cfb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include 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) =20 -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; =20 /// 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 valu= e 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` alloca= ted with `A`. +/// * This `RcuBox` holds exclusive permissions to rcu free the allocation. +pub struct RcuBox(NonNull>, PhantomD= ata); + +/// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuKBox}; +/// let rb =3D RcuKBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuKBox =3D RcuBox; + +/// Type alias for [`RcuBox`] with a [`Vmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuVBox}; +/// let rb =3D RcuVBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuVBox =3D RcuBox; + +/// Type alias for [`RcuBox`] with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuKVBox}; +/// let rb =3D RcuKVBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuKVBox =3D RcuBox; + +struct RcuBoxInner { + rcu_head: bindings::callback_head, + value: T, +} + +// Note that `T: Sync` is required since when moving an `RcuBox`, th= e previous owner may +// still access `&T` for one grace period. +// +// SAFETY: Ownership of the `RcuBox` allows for `&T` and dropping th= e `T`, so `T: Send + +// Sync` implies `RcuBox: Send`. +unsafe impl Send for RcuBox {} + +// SAFETY: `&RcuBox` allows for no operations other than those permi= tted by `&T`, so `T: +// Sync` implies `RcuBox: Sync`. +unsafe impl Sync for RcuBox {} + +impl RcuBox { + /// Create a new `RcuBox`. + pub fn new(x: T, flags: alloc::Flags) -> Result { + let b =3D 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 Deref for RcuBox { + type Target =3D T; + fn deref(&self) -> &T { + // SAFETY: While the `RcuBox` exists, the value remains valid. + unsafe { &(*self.0.as_ptr()).value } + } +} + +// SAFETY: +// * The `RcuBoxInner` was allocated with `A`. +// * `NonNull::as_ptr` returns a non-null pointer. +unsafe impl ForeignOwnable for RcuBox { + const FOREIGN_ALIGN: usize =3D , A> as ForeignOwnab= le>::FOREIGN_ALIGN; + + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'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 i= nvariants as `RcuBox`. + // 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::>()).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 ForeignOwnableRcu for RcuBox { + type RcuBorrowed<'a> =3D &'a T; + + unsafe fn rcu_borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: `RcuBox::drop` can only run after `from_foreign` is cal= led, and the value is + // valid until `RcuBox::drop` plus one grace period. + unsafe { &(*ptr.cast::>()).value } + } +} + +impl Drop for RcuBox { + fn drop(&mut self) { + // SAFETY: The `rcu_head` field is in-bounds of a valid allocation. + let rcu_head =3D unsafe { &raw mut (*self.0.as_ptr()).rcu_head }; + if core::mem::needs_drop::() { + // 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::)) }; + } else { + // SAFETY: All users will be gone in an rcu grace period. + // TODO: We are luckily since `kvfree_call_rcu()` works on bot= h kmalloc and vmalloc, + // maybe a new `Allocator` method is needed. + unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().c= ast()) }; + } + } +} + +/// Free this `RcuBoxInner`. +/// +/// # Safety +/// +/// `head` references the `rcu_head` field of an `RcuBoxInner` that has= no references to it. +/// Ownership of the `Box, A>` must be passed. +unsafe extern "C" fn drop_rcu_box(head: *mut bindings::ca= llback_head) { + // SAFETY: Caller provides a pointer to the `rcu_head` field of a `Rcu= BoxInner`. + let box_inner =3D unsafe { crate::container_of!(head, RcuBoxInner, = 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 =3D RcuBox::<_, alloc::allocator::Kmalloc>::new(42i32, allo= c::flags::GFP_KERNEL)?; + + assert_eq!(*rb, 42); + assert_eq!(*rb.with_rcu(&Guard::new()), 42); + + drop(rb); + + let rb =3D RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, allo= c::flags::GFP_KERNEL)?; + + assert_eq!(*rb, 42); + assert_eq!(*rb.with_rcu(&Guard::new()), 42); + + drop(rb); + + Ok(()) + } +} --=20 2.51.0 From nobody Mon Jun 8 05:25:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1BEB92FF144; Fri, 5 Jun 2026 13:35:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666555; cv=none; b=SFxueZVtycVjkJf3taC0n9fkdoeBFf16rXt6Pkx/ILFaR4QtBwzBgeeUXYhtvzICJDeaPMhNJ7N1RlK/B0HCidx07xMueaKDre6Q0zbU8zGB96Q4CW5bzUBMpz/b93A23qErRxX5xz2Bzgorkv0IC5aChVlrBkkT/9CR165D6NI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666555; c=relaxed/simple; bh=wlb6NkJIxz4wYIsUfd+v9sn7xOMMQEdTW7zEFBMBkQ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Emdo0w19HVoYbnwDqbT4T7yLk3xNjYSHRTBGF/5f3CeC5ZRedg5qh3IZB7p99HUw8vv/0aym3pup4QhzljFsUTdgb5Z2qcK8jcJr2w0sOaPavfC3HLx5QgYt8PyLvNgAnZ1MhgQsGyeVS/txAAEWDlBMVWKQS7O+tKcgxPZY5FM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RLIzQAOX; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RLIzQAOX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 530D91F0089C; Fri, 5 Jun 2026 13:35:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780666551; bh=J7xoJI8ZQKXEZmPwD70InQg1kBZths2uP7Ky23DxVzU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=RLIzQAOXSv4hE7ojVXJugD+1SN+MumAULe7by1699ebbTRp7wPsmhGDLhwNMftGnh LApa/yoXAg9mTheG0LCKlMV0uVBhWUJ1q9wWK2SsbM7L3qt6PUs83WYBlrhikTUnoI JzsJ8fEMtLCKpXqBcaCQ4qGF2gyna2U1mBAT/Pa8TDuA33/C6v6/Ndj/bplsC2TlTP 8Nt816jhJh6xdl4W8Ecy/CS+Zc7AhKVOYoxlBy+eVNT7hie5WPDnchWNtHNiCGOGPB aZWtrm/a0PD6sgmsJGgrDRbjMtw7cx+b8iautky6RvMLtJb+dw86emsUacr5A9KpCQ 0wAgHmLmL4pAg== Received: from phl-compute-03.internal (phl-compute-03.internal [10.202.2.43]) by mailfauth.phl.internal (Postfix) with ESMTP id 99776F4006F; Fri, 5 Jun 2026 09:35:49 -0400 (EDT) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-03.internal (MEProxy); Fri, 05 Jun 2026 09:35:49 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTEiXmK80I+r4b+5zY/r99GUAlO/A1JeBVvrzdPJND7AxnXROcGIdrV5+IW/GIICbk HTeSUzo2jG7T3ubuXDYawvMsJmw7SeRzXaVjmYhKpesaWDi/jHlBwDSw7cUJtOL4Zml+HR MdH91GjghnCvkbpZdNzjjo1bSZDmeAE+i20ut4ugSeq7SenIcQ9DZOfghRt4xUUc5uh5PM SgAN181ZxF1oco7PoJ9ho3wb1kPOXsEzQT5UZv0CFFx1JIqpGEiANmmsdwoALURinC3es0 +e8I9nc2247wfagtNBtbgyVV8BvBRc03DGsTzzDlNTrxNmxxQ9+IH7k1Xg1Flh6mzaY9dd 3j2HutalZEtkeyprDlRU9NKhJ58LI3mLCHfCFgQmnG8PQOAMLqGwkAsaebO44mK2vlmnuR MYFHdgugU/DvFz/CsV1BMgjxu2YWfHY6dIBjFGFfCo2b6Xjw2QrWJdS8Cbk5cZhEtbfaXp at5DrHXdm5HOWa4n7c5BxK7E+OBH3DkzTtugpB2k3UIWu4hVz5MxXmr3BagAdtITLDJQD/ JSeHg9SfgIo764hoaOP43Tt18sx9L0beNvDwrs016gQNdq0dFaZs0o0pblPddusYiVOxGW AUTPP59waZPr0Ka3jmv3nLYXE/JZVbgYPX9x3z4UPuAgW/ns2QILb4zckoVQ X-ME-Proxy: Feedback-ID: i8dbe485b:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 5 Jun 2026 09:35:48 -0400 (EDT) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , 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 , =?UTF-8?q?Christian=20K=C3=B6nig?= , maple-tree@lists.infradead.org, linux-mm@kvack.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, Philipp Stanner , Lyude Paul , Daniel Almeida , =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH 1/3] rust: rcu: add RcuBox type Date: Fri, 5 Jun 2026 06:35:37 -0700 Message-ID: <20260605133541.22569-2-boqun@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605133541.22569-1-boqun@kernel.org> References: <20260605133541.22569-1-boqun@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alice Ryhl 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 [boqun: Make RcuBox generic over Allocator and add tests] [boqun: Add type alias for Rcu*Box] Co-developed-by: Boqun Feng Signed-off-by: Boqun Feng --- 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_helpe= r.h index 446dbeaf0866..2011645c7cfb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include 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) =20 -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; =20 /// 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 valu= e 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` alloca= ted with `A`. +/// * This `RcuBox` holds exclusive permissions to rcu free the allocation. +pub struct RcuBox(NonNull>, PhantomD= ata); + +/// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuKBox}; +/// let rb =3D RcuKBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuKBox =3D RcuBox; + +/// Type alias for [`RcuBox`] with a [`Vmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuVBox}; +/// let rb =3D RcuVBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuVBox =3D RcuBox; + +/// Type alias for [`RcuBox`] with a [`KVmalloc`] allocator. +/// +/// # Examples +/// +/// ``` +/// # use kernel::sync::rcu::{self, RcuKVBox}; +/// let rb =3D RcuKVBox::new(42, GFP_KERNEL)?; +/// +/// assert_eq!(*rb, 42); +/// assert_eq!(*rb.with_rcu(&rcu::read_lock()), 42); +/// # Ok::<(), Error>(()) +/// ``` +pub type RcuKVBox =3D RcuBox; + +struct RcuBoxInner { + rcu_head: bindings::callback_head, + value: T, +} + +// Note that `T: Sync` is required since when moving an `RcuBox`, th= e previous owner may +// still access `&T` for one grace period. +// +// SAFETY: Ownership of the `RcuBox` allows for `&T` and dropping th= e `T`, so `T: Send + +// Sync` implies `RcuBox: Send`. +unsafe impl Send for RcuBox {} + +// SAFETY: `&RcuBox` allows for no operations other than those permi= tted by `&T`, so `T: +// Sync` implies `RcuBox: Sync`. +unsafe impl Sync for RcuBox {} + +impl RcuBox { + /// Create a new `RcuBox`. + pub fn new(x: T, flags: alloc::Flags) -> Result { + let b =3D 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 Deref for RcuBox { + type Target =3D T; + fn deref(&self) -> &T { + // SAFETY: While the `RcuBox` exists, the value remains valid. + unsafe { &(*self.0.as_ptr()).value } + } +} + +// SAFETY: +// * The `RcuBoxInner` was allocated with `A`. +// * `NonNull::as_ptr` returns a non-null pointer. +unsafe impl ForeignOwnable for RcuBox { + const FOREIGN_ALIGN: usize =3D , A> as ForeignOwnab= le>::FOREIGN_ALIGN; + + type Borrowed<'a> =3D &'a T; + type BorrowedMut<'a> =3D &'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 i= nvariants as `RcuBox`. + // 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::>()).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 ForeignOwnableRcu for RcuBox { + type RcuBorrowed<'a> =3D &'a T; + + unsafe fn rcu_borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: `RcuBox::drop` can only run after `from_foreign` is cal= led, and the value is + // valid until `RcuBox::drop` plus one grace period. + unsafe { &(*ptr.cast::>()).value } + } +} + +impl Drop for RcuBox { + fn drop(&mut self) { + // SAFETY: The `rcu_head` field is in-bounds of a valid allocation. + let rcu_head =3D unsafe { &raw mut (*self.0.as_ptr()).rcu_head }; + if core::mem::needs_drop::() { + // 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::)) }; + } else { + // SAFETY: All users will be gone in an rcu grace period. + // TODO: We are luckily since `kvfree_call_rcu()` works on bot= h kmalloc and vmalloc, + // maybe a new `Allocator` method is needed. + unsafe { bindings::kvfree_call_rcu(rcu_head, self.0.as_ptr().c= ast()) }; + } + } +} + +/// Free this `RcuBoxInner`. +/// +/// # Safety +/// +/// `head` references the `rcu_head` field of an `RcuBoxInner` that has= no references to it. +/// Ownership of the `Box, A>` must be passed. +unsafe extern "C" fn drop_rcu_box(head: *mut bindings::ca= llback_head) { + // SAFETY: Caller provides a pointer to the `rcu_head` field of a `Rcu= BoxInner`. + let box_inner =3D unsafe { crate::container_of!(head, RcuBoxInner, = 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 =3D RcuBox::<_, alloc::allocator::Kmalloc>::new(42i32, allo= c::flags::GFP_KERNEL)?; + + assert_eq!(*rb, 42); + assert_eq!(*rb.with_rcu(&Guard::new()), 42); + + drop(rb); + + let rb =3D RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, allo= c::flags::GFP_KERNEL)?; + + assert_eq!(*rb, 42); + assert_eq!(*rb.with_rcu(&Guard::new()), 42); + + drop(rb); + + Ok(()) + } +} --=20 2.51.0 From nobody Mon Jun 8 05:25:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B119530C345; Fri, 5 Jun 2026 13:35:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666558; cv=none; b=nLyWeA7sOdy5+M0fWImm3ZiRF0PTNnPjRWIl0AKvs8stiiguwBb85cKaCDP6iK+dAsEhZGdxtqxBdAyelQb0hLdO/9fKA6d63AdlTMylXOYxWfIo9bD938Y4o3P9bL8iVyoiA2h8MKu2/KCetqN9ak1/c1b/EjC9Yz/b7UJB3yM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666558; c=relaxed/simple; bh=7oEWBugMDCTnwu6G8nNrOIl6WlQQUXQtkAdp3xI/oZQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nqp5Tf4OKIS3sOR/iYZIVa33iv2wVlfNB4+kYd8mzua4zuzKjepwoc6o/zECZ0IvJp+xXB1CAP5UncWiXYbdye0mMaok+w9WhkAvBTnzjcNIhq1VtRIUPjX9mZ0h3j4+de0azYOD85J0Z26+Fz8zERGaT6t19fYsflVJQyRTjFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=M22+aP1y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="M22+aP1y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 649301F00898; Fri, 5 Jun 2026 13:35:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780666554; bh=EWiBixMYQYNU31zDIl5csE1S/wX47DtIBhxQyh/JIig=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=M22+aP1y/jBD8OI5tHbQb+G2ZmQLForEPqc8/xH8JjcqEWhSYdBqcMX2eUNgcolDK BX4eT/N0oL5sRuy/Dy6y6tFs3iAecWaSVA6KV3bqGQ5/GAnvhE0E9YB/gKqBEYzVNp DXmLrBoFP9j1tbG62n+D3s1xn0si+3SYaeo6lZ1vKj8d9t+4/vgXkROalELTyawkwE NPd3R/54fWQefHdGc7tdjntLAHK5X4dOrA6+K0qyn9+hIImneB7hsn2IAEFAwY4WMv 0Hk6TeWkWRrzuqd0gU/H9LF6I2z9w1YWL6thZnUdvP/8dLMFYTmQxK4m8mqjAyWB2v 7EEPwryQ/5Gaw== Received: from phl-compute-05.internal (phl-compute-05.internal [10.202.2.45]) by mailfauth.phl.internal (Postfix) with ESMTP id B4019F4006F; Fri, 5 Jun 2026 09:35:52 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-05.internal (MEProxy); Fri, 05 Jun 2026 09:35:52 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTEiXmK80I+r4b+5zY/r99GUAlO/A1JeBVvrzdPJND7AxnXROcGIdrV5+IW/GIICbk HTeSUzo2jG7T3ubuXDYawvMsJmw7SeRzXaVjmYhKpesaWDi/jHlBwDSw7cUJtOL4Zml+HR MdH91GjghnCvkbpZdNzjjo1bSZDmeAE+i20ut4ugSeq7SenIcQ9DZOfghRt4xUUc5uh5PM SgAN181ZxF1oco7PoJ9ho3wb1kPOXsEzQT5UZv0CFFx1JIqpGEiANmmsdwoALURinC3es0 +e8I9nc2247wfagtNBtbgyVV8BvBRc03DGsTzzDlNTrxNmxxQ9+IH7k1Xg1Flh6mzaY9ZF D5JI/kUrl5038ewH2/yRBGMWygreujWb7iMUHrlowYIRmldbRWWDCC9SW9qFe9HhzwOP5R ANLHL8xhWBwyW6Z4xw3ZMLR+S85zuSDhz5Ie5yDVfNvn81u9jPFArGesQGDKJbpUGEj4Xn 2dwkkQHzSA3mlt/Y/sVSUjgZi0drAp7nl7QMcSQqMTIa7ue0D4oe8kxDOnuOJ+sVzxQuul cf4anHpwCUK2rfRE87LFm+P0SY/bv3hsIRCFwuYbxFAx+MvwufNYnz9hkdJg9SKg8oEnyD /YwRPqQACwTSX771arNNzrzPgtmXD21/gBVN4f/8UHX/Rt7jIkyJ5iaDOeog X-ME-Proxy: Feedback-ID: i8dbe485b:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 5 Jun 2026 09:35:52 -0400 (EDT) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , 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 , =?UTF-8?q?Christian=20K=C3=B6nig?= , maple-tree@lists.infradead.org, linux-mm@kvack.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, Philipp Stanner , Lyude Paul , Daniel Almeida , =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH 2/3] rust: maple_tree: add load_rcu() Date: Fri, 5 Jun 2026 06:35:39 -0700 Message-ID: <20260605133541.22569-4-boqun@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605133541.22569-1-boqun@kernel.org> References: <20260605133541.22569-1-boqun@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alice Ryhl 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 --- 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}, }; =20 @@ -233,6 +237,54 @@ pub fn erase(&self, index: usize) -> Option { unsafe { T::try_from_foreign(ret) } } =20 + /// Load the value at the given index with rcu. + /// + /// # Examples + /// + /// Read the value under an rcu read lock. Even if the value is remove= d, it remains accessible + /// for one rcu grace period. + /// + /// ```ignore + /// use kernel::{ + /// maple_tree::MapleTree, + /// sync::rcu::{self, RcuBox}, + /// }; + /// + /// let tree =3D KBox::pin_init(MapleTree::>::new(), GFP_K= ERNEL)?; + /// + /// let ten =3D RcuBox::new(10, GFP_KERNEL)?; + /// tree.insert(100, ten, GFP_KERNEL)?; + /// + /// let rcu_read_lock =3D rcu::Guard::new(); + /// let ten =3D tree.load_rcu(100, &rcu_read_lock); + /// assert_eq!(ten, Some(&10)); + /// + /// // Even if the value gets removed, we may continue to access it fo= r 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> + where + T: ForeignOwnableRcu, + { + // SAFETY: `self.tree` contains a valid maple tree. + let ret =3D 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 t= his 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> { --=20 2.51.0 From nobody Mon Jun 8 05:25:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DC813161AB for ; Fri, 5 Jun 2026 13:35:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666559; cv=none; b=gUwsTH2obQscKLxYNZuISeL1nCbi1VJksmqzx2HIA3PQbjquv8D7jtOXCzbOsXf9AVqntVU+SrSoKqmCTfc9+UPtg6ZPr6rSSUmC1MNBv0lSn4JWL1NNp7F1ZVUh12qa0JH2HoiVicpo8cSaz9r3SsJViCZ1L3BGSrJVd/Aax0Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666559; c=relaxed/simple; bh=7oEWBugMDCTnwu6G8nNrOIl6WlQQUXQtkAdp3xI/oZQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FR47gI7NPxieuNl848S4Q+GvXsn2NqW6mOZuD9xO4kx50bBk2GEVcW+2FN/HS1koGJgRWy5LGL8UPeP1cbKnR4giLavdRAvL7hbISGbzkachU5hZZcfgC4L5+JgrHwNhNjKLFL31cnDp9yhLZPu9iilcEtZKTrmpivnqWThC67U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GLSTGxoy; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GLSTGxoy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CF00B1F008A2; Fri, 5 Jun 2026 13:35:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780666556; bh=EWiBixMYQYNU31zDIl5csE1S/wX47DtIBhxQyh/JIig=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=GLSTGxoyzEE0A3zgCstKowYyJmvozQaxwWfv91puJGVJzhjLyTfSCFzzs1AAq+Y0D lIJpAlGXx0/+XiHVFlj9vqUub9KV8nKtm06wKr0iNcQyXhQ7VekUFJdudg6GrxJ1v3 7Eqv7ZPm8+NElnC5cwimKoyVHWnKEtt0g1/MYQG4mSBPJW0Vi7UAk4mL8I4M9L6y5V Oarsv+jjdC7fFB97lu+t84GbR6X8I3GqMgLQo5kwX2RjU0jYc24WUFUmUXhrRMk8mj i6pq5fnOGiFnuuXwoJEk5SMagPPwPlALGxDXcG8IKG1xmZWUbgmqH7ugyi+Iantven yDzNhExGGzNCg== Received: from phl-compute-09.internal (phl-compute-09.internal [10.202.2.49]) by mailfauth.phl.internal (Postfix) with ESMTP id 29B0DF40071; Fri, 5 Jun 2026 09:35:54 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-09.internal (MEProxy); Fri, 05 Jun 2026 09:35:54 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTFtzx3Tos4VS/WdxOab7aAtqT83KRYoC/e4xL0nOCb6pQty9DSyT2t/Oxqun+SkVO cMZEcCN5WOjp6C4j+AAnIhEdX3CSbRJ8Ue2tNsJBDrFL6ULtBgvWkBHotQCeWp8k5uQsbz k0FRlet+ZGAnwVkNRqfp6f3UC3RPwOllI7lUv1O3gyUDJYMh/GLR4owHw0MCxoW4il/3Lb lg6wo6OlsJz47diU5HyF0fJo4ssmn4HWhMDDXvcMbgn1kwB5FB8PtdkejMZSrdA2X0rrEw 1NqdIXS1VHAvWHyGi709gDhKFxTkm5K5pOAbaKiTP2ufZXGBKQdT4BlTzCV7z6NbqlWRdS TNmwv5DFwJa9S8PHhxbDWzMLZbfceTkxemjD8vkgsy7eLPnM2nk8z0FiJ6ZFamfDHrSbKe 9ALtNJxC6vU4yzqsSz/479whQYrNVh6ZXTIphpow0CB2YcI2FyWxcnifqvDL6INy1MicNf VPzL7bQVvvbPuuJSqbvJhIClCjKIw7zTQchWCg8kQw796AOIfksJ8Qy6i64wI9ZnSK03k9 1mCjRqzaX9qlX9QYXS7rQBAWBRtutkpQIqnc1dKX0+TCKCObtYUFEci8B8/TZu5Zz3Y95M dBvvtqAAf7890Hekqu4mItc0DdTgWKqnQV59t4dAXGqO2oQ9ZcA2rCPbpDEA X-ME-Proxy: Feedback-ID: i8dbe485b:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 5 Jun 2026 09:35:53 -0400 (EDT) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , 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 , =?UTF-8?q?Christian=20K=C3=B6nig?= , maple-tree@lists.infradead.org, linux-mm@kvack.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, Philipp Stanner , Lyude Paul , Daniel Almeida , =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH 2/3] rust: maple_tree: Add load_rcu() Date: Fri, 5 Jun 2026 06:35:40 -0700 Message-ID: <20260605133541.22569-5-boqun@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605133541.22569-1-boqun@kernel.org> References: <20260605133541.22569-1-boqun@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alice Ryhl 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 --- 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}, }; =20 @@ -233,6 +237,54 @@ pub fn erase(&self, index: usize) -> Option { unsafe { T::try_from_foreign(ret) } } =20 + /// Load the value at the given index with rcu. + /// + /// # Examples + /// + /// Read the value under an rcu read lock. Even if the value is remove= d, it remains accessible + /// for one rcu grace period. + /// + /// ```ignore + /// use kernel::{ + /// maple_tree::MapleTree, + /// sync::rcu::{self, RcuBox}, + /// }; + /// + /// let tree =3D KBox::pin_init(MapleTree::>::new(), GFP_K= ERNEL)?; + /// + /// let ten =3D RcuBox::new(10, GFP_KERNEL)?; + /// tree.insert(100, ten, GFP_KERNEL)?; + /// + /// let rcu_read_lock =3D rcu::Guard::new(); + /// let ten =3D tree.load_rcu(100, &rcu_read_lock); + /// assert_eq!(ten, Some(&10)); + /// + /// // Even if the value gets removed, we may continue to access it fo= r 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> + where + T: ForeignOwnableRcu, + { + // SAFETY: `self.tree` contains a valid maple tree. + let ret =3D 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 t= his 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> { --=20 2.51.0 From nobody Mon Jun 8 05:25:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67EB531B101; Fri, 5 Jun 2026 13:35:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666560; cv=none; b=QuLGY6kWBBnL1bAaSyWFKVZbBPKwkCQS8R7A3TMFiEOIDi7/GNRbNl0RUa/S9i1bs7tjkMYEyn8Sg21RC2+baAa1r44f78f8uK7YN94TJXthua+mi+s/tAVk42mRboiF1aOhMOEtL/MSPDNx7yWmPwDK5B/bJgudaSJk/kSYTFc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780666560; c=relaxed/simple; bh=tEimvk7OykDDRIUJs/A3HLeKRmTl3/7VQfcmhS4wJFM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BS9frXli6M65SC1F7pMO5j0a3PfGZONbeoaFsr9Y+xpWNINC1O81fMOHq875sBGC2zIc2LwmAfeucQtIzA4iRuRP/6k0ZmJREiKk0SlWllERCCrvOVoEsKN18MMfVXNt3vxERj0nAJgvaV1mHrrkvjcuBUoKld0KciU/IztBAIc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lXVXqKQU; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lXVXqKQU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5FF2D1F0089E; Fri, 5 Jun 2026 13:35:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780666557; bh=3KosL0D3Q2eEjvojKVK/tB6tB4IEDd35FarHmws6fUs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=lXVXqKQU/sP/FRbSGZUw52acvlgIharWh19NaZUaft+jpptgVn7wpE+l1Py6LbksH 5yW9iK06yCralSUxHMWDfgKlmDZyubI6ImpHprLY0j4elpQCyEuw/GDHfv5YdmvhSc 6YICmB+FjlBC2e5GHGz7uViHP8MCXuRunpGroN6HKOo4emRSL8mj3znxf8spKEIASW lRjM9FYzCbmSmFPW6nW2Jpyqn619h5P15VJVsTeQyqkj7vc1+tK8SCpBTz2lsKnvVw 0C9YUz5wZ0diAnzZRNCY6cTFwLFg6WDDVhfbvLWO12unifOEjP1oYtNp5NZBUF3NBI o+v2fwOmPz1jw== Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailfauth.phl.internal (Postfix) with ESMTP id AED08F4006F; Fri, 5 Jun 2026 09:35:55 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Fri, 05 Jun 2026 09:35:55 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTEiXmK80I+r4b+5zY/r99GUAlO/A1JeBVvrzdPJND7AxnXROcGIdrV5+IW/GIICbk HTeSUzo2jG7T3ubuXDYawvMsJmw7SeRzXaVjmYhKpesaWDi/jHlBwDSw7cUJtOL4Zml+HR MdH91GjghnCvkbpZdNzjjo1bSZDmeAE+i20ut4ugSeq7SenIcQ9DZOfghRt4xUUc5uh5PM SgAN181ZxF1oco7PoJ9ho3wb1kPOXsEzQT5UZv0CFFx1JIqpGEiANmmsdwoALURinC3es0 +e8I9nc2247wfagtNBtbgyVV8BvBRc03DGsTzzDlNTrxNmxxQ9+IH7k1Xg1Flh6mzaY9AF F1Rbx4zjkpU7Vu9L15KSs/58k3UtTFycBLbXnKzW7G3Az8oIg8YJ7B1llLRzq9ZQymYw+g 2fPji44Zl+dmnKR9uYe/gkEuH7gJ9937pSdsAM+jHiIhPy6/Q3Ya1wrMQnGQgK/yBtLMlz YUObbhrUwbZHNvplmabTTmM68ir7kNT02nLZMMPNC7GcsuPZ3JhN9G4NGFO8U2/zbN2LhN qlb41qjjYJSqfOr+cv1dH5tLNyYc8aMoVJZIMB9KUDldh4Ho9IlsJ2x/ZYZJl0fSmk1QXV FZ7uk0R3q0t0R5NcBwbJCLKAGRtG8d3QOaxly+IElSGTUJZLozO/dAinufEw X-ME-Proxy: Feedback-ID: i8dbe485b:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 5 Jun 2026 09:35:54 -0400 (EDT) From: Boqun Feng To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, rcu@vger.kernel.org Cc: Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , 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 , =?UTF-8?q?Christian=20K=C3=B6nig?= , maple-tree@lists.infradead.org, linux-mm@kvack.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, Philipp Stanner , Lyude Paul , Daniel Almeida , =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [RFC PATCH 3/3] rust: rcu: Introduce RcuFreeBox Date: Fri, 5 Jun 2026 06:35:41 -0700 Message-ID: <20260605133541.22569-6-boqun@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605133541.22569-1-boqun@kernel.org> References: <20260605133541.22569-1-boqun@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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` 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 --- 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) =20 +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 + o= ne 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 `Se= lf` has non-trivial +/// `drop()` function. +pub unsafe trait RcuFreeSafe { + fn drop_before_gp(self: Pin<&mut Self>); +} + +macro_rules! impl_not_drop { + ($($t:ty, )*) =3D> { + // SAFETY: Dropping `T` has no side effect means `T` is always rea= dy 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 @@ =20 use core::{ marker::PhantomData, + mem::ManuallyDrop, ops::Deref, ptr::NonNull, // }; @@ -29,17 +30,18 @@ =20 use super::{ ForeignOwnableRcu, - Guard, // + Guard, + RcuFreeSafe, // }; =20 -/// 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` alloca= ted 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(NonNull>, PhantomD= ata); =20 /// Type alias for [`RcuBox`] with a [`Kmalloc`] allocator. @@ -205,6 +207,50 @@ fn drop(&mut self) { drop(unsafe { Box::<_, A>::from_raw(box_inner) }); } =20 +/// A box that is freed with RCU. +/// +/// Currently we require `T` being `Send` because of an implementation lim= itation. 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(RcuBox, A>); + +impl RcuFreeBox { + /// Create a new `RcuFreeBox`. + pub fn new(x: T, flags: alloc::Flags) -> Result { + 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 Deref for RcuFreeBox { + type Target =3D T; + + fn deref(&self) -> &T { + self.0.deref() + } +} + +impl Drop for RcuFreeBox { + fn drop(&mut self) { + // CAST: `ManuallyDrop` is transparet to `T`, adn `RcuBox` owns= the object per type + // invariants. + let ptr =3D self.0 .0.as_ptr().cast::(); + + // SAFETY: Per the invariants of `RcuBox`, `ptr` owns the pointed = object. And we are not + // going to move it. + let pin =3D unsafe { Pin::new_unchecked(&mut *ptr) }; + + pin.drop_before_gp(); + + // `needs_drop::()` returns `false`, hence `kvfree_c= all_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 { =20 drop(rb); =20 + let rb =3D 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 =3D RcuBox::<_, alloc::allocator::Vmalloc>::new(42i32, allo= c::flags::GFP_KERNEL)?; =20 assert_eq!(*rb, 42); @@ -225,6 +278,13 @@ fn rcu_box_basic() -> Result { =20 drop(rb); =20 + let rb =3D 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(()) } } --=20 2.51.0