Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH v1] rust: rcu: Add abstraction for call_rcu()
@ 2026-05-20 13:17 Philipp Stanner
  2026-05-20 13:59 ` Boqun Feng
  2026-05-27  7:42 ` Alvin Sun
  0 siblings, 2 replies; 9+ messages in thread
From: Philipp Stanner @ 2026-05-20 13:17 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Paul E. McKenney, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Joel Fernandes (NVIDIA), Philipp Stanner, Peter Zijlstra (Intel),
	Tamir Duberstein
  Cc: rust-for-linux, linux-kernel, rcu

call_rcu() can be expected to be needed by a great variety of users.
This functionality is almost always used for deallocating resources
after all accessors are gone. Hence, it appears reasonable to implement
the abstractions in such a way that the user merely passes data, which
is later (after a grace period) dropped.

In the rare cases where the user needs special action to take place,
this could be achieved through implementing a custom drop() method.

Implement a first minimal abstraction for call_rcu().

Signed-off-by: Philipp Stanner <phasta@kernel.org>
---
 rust/helpers/rcu.c      |  1 +
 rust/kernel/sync.rs     |  1 +
 rust/kernel/sync/rcu.rs | 89 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/rust/helpers/rcu.c b/rust/helpers/rcu.c
index 481274c05857..c9cfc99c93d5 100644
--- a/rust/helpers/rcu.c
+++ b/rust/helpers/rcu.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#include <linux/types.h> /* for callback_head */
 #include <linux/rcupdate.h>
 
 __rust_helper void rust_helper_rcu_read_lock(void)
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 993dbf2caa0e..1ddca3847b19 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -31,6 +31,7 @@
 pub use locked_by::LockedBy;
 pub use refcount::Refcount;
 pub use set_once::SetOnce;
+pub use rcu::Callback;
 
 /// Represents a lockdep class.
 ///
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..caf71fa46f5e 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -4,7 +4,15 @@
 //!
 //! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
 
-use crate::{bindings, types::NotThreadSafe};
+use crate::{
+    bindings,
+    prelude::*,
+    types::{
+        NotThreadSafe,
+        Opaque,
+    },
+    alloc::Flags,
+};
 
 /// Evidence that the RCU read side lock is held on the current thread/CPU.
 ///
@@ -50,3 +58,82 @@ fn drop(&mut self) {
 pub fn read_lock() -> Guard {
     Guard::new()
 }
+
+
+/// An RCU callback object. Carries the user's data to drop() it once a grace period ellapsed.
+///
+/// This object serves to implement C's `call_rcu()` method. Since it is almost
+/// always used to free a resource once a grace period ellapsed, the only thing
+/// this implementation does is drop the user's data. In the rare cases in which
+/// the user needs more action to take place, said actions need to be implemented
+/// on the user's data via the [`Drop`] trait.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::rcu::Callback;
+///
+/// struct Foo {};
+///
+/// impl Drop for Foo {
+///     fn drop(&mut self) {
+///         pr_info!("rcu::Foo Dropping.\n");
+///     }
+/// }
+///
+/// let data = Foo {};
+///
+/// let cb = Callback::new(data, GFP_KERNEL)?;
+/// cb.submit();
+///
+/// Ok::<(), Error>(())
+/// ```
+#[repr(C)]
+#[pin_data]
+pub struct Callback<T: Send + 'static> {
+    /// The RCU head. Only used (and initialized) by the C backend.
+    #[pin]
+    inner: Opaque<bindings::callback_head>,
+    /// The user's data. This should implement [`Drop`] if the user wants specific
+    /// actions, besides mere deallocation, to happen.
+    #[pin]
+    data: T,
+}
+
+impl<T: Send + 'static> Callback<T> {
+    /// Create a new callback.
+    pub fn new(data: impl PinInit<T>, flags: Flags) -> Result<Pin<KBox<Self>>> {
+        let cb = try_pin_init!(Self {
+            inner: Opaque::uninit(), // Only needed for the C backend, who will initialize it.
+            data <- data,
+        });
+
+        KBox::pin_init(cb, flags)
+    }
+
+    extern "C" fn callback(rcu_head: *mut bindings::callback_head) {
+        let cb_ptr = rcu_head as *mut Self;
+
+        // SAFETY: All [`Callback`] objects in this module are always created
+        // as `Pin<KBox<Self>>`. `Pin` is a transparent container. The action
+        // below merely serves re-creating the KBox so that it can drop properly.
+        let _cb = unsafe { KBox::from_raw(cb_ptr) };
+
+        // Self::data drops, ensuring the desired cleanup operation.
+    }
+
+    fn as_raw(&self) -> *mut bindings::callback_head {
+        self.inner.get()
+    }
+
+    /// Arm a [`Callback`]. One grace period after this function was called,
+    /// the callback object will be dropped.
+    pub fn submit(self: Pin<KBox<Self>>) {
+        // SAFETY: The memory is not moved by this code or the C backend.
+        let cb = unsafe { Pin::into_inner_unchecked(self) };
+        let ptr = KBox::into_raw(cb);
+        // SAFETY: `ptr` was just created validly above. `Self::callback` relies
+        // on the RCU module / code never being unloaded.
+        unsafe { bindings::call_rcu((*ptr).as_raw(), Some(Self::callback)) };
+    }
+}
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-05-28 15:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 13:17 [PATCH v1] rust: rcu: Add abstraction for call_rcu() Philipp Stanner
2026-05-20 13:59 ` Boqun Feng
2026-05-21  7:25   ` Philipp Stanner
2026-05-26 16:10     ` Boqun Feng
2026-05-27  5:52       ` Alice Ryhl
2026-05-27  5:59         ` Boqun Feng
2026-05-27  6:31           ` Philipp Stanner
2026-05-28 15:06             ` Boqun Feng
2026-05-27  7:42 ` Alvin Sun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox