* [PATCH v18 0/8] rust: add `Ownable` trait and `Owned` type
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Asahi Lina, Oliver Mangold, Viresh Kumar, Boqun Feng, Asahi Lina,
Igor Korotin, Andreas Hindborg
Add a new trait `Ownable` and type `Owned` for types that specify their
own way of performing allocation and destruction. This is useful for
types from the C side.
Implement `ForeignOwnable` for `Owned`.
Convert `Page` to be `Ownable` and add a `from_raw` method.
Add the trait `OwnableRefCounted` that allows conversion between
`ARef` and `Owned`. This is analogous to conversion between `Arc` and
`UniqueArc`.
Patches 1-4 implement `Ownable` and applies it to `Page`. These patches
can be merged on their own.
Patches 5-7 add `Ownable` -> `ARef` interop and can be merged later if
consensus on their shape cannot be reached.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
Changes in v18:
- Rebase on `rust-next` (2026-06-24).
- Drop the `'static` bound on `ForeignOwnable for Owned` (Gary).
- Make `Ownable::release` take a raw pointer instead of `&mut self` (Alice, Sashiko).
- Drop `types::ARef` re-export (Alice).
- Drop unneeded `#[repr(transparent)]` on `Owned` (Gary).
- Fix `FOREIGN_ALIGN` for `Owned` to report the pointee alignment (Sashiko).
- Remove `BorrowedPage`; use `&Page` directly (Alice).
- Update Rust Binder for the `Owned<Page>` conversion (Alice).
- Update `pwm.rs` for the `RefCounted`/`AlwaysRefCounted` split (Sashiko).
- Fix documentation nits: missing `// INVARIANT:` comments, stale `Page` docs, and a stray `mut` (Sashiko).
- Expand the `use` statements touched by the rename patch to the multi-line style (Onur).
- Link to v17: https://msgid.link/20260604-unique-ref-v17-0-7b4c3d2930b9@kernel.org
Changes in v17:
- Rebase on v7.1-rc2.
- Reorder patches so that `Ownable` can merge without `OwnableRefCounted` (Alice).
- Add `#[inline]` directives to short functions added by the series (Gary).
- Link to v16: https://msgid.link/20260224-unique-ref-v16-0-c21afcb118d3@kernel.org
Changes in v16:
- Simplify pointer to reference cast in `Page::from_raw`.
- Use `NonNull<Page>` rather than `Owned<Page>` for `BorrowedPage` internals.
- Use "convertible to reference" wording when converting pointers to references.
- Fix formatting for `Page::from_raw` docs.
- Leave imports alone when adding safety comment to aref example.
- Use `KBox::into_nonnull` for examples.
- Add patch for `KBox::into_nonnull`.
- Change invariants and safety comments of `Ownable` and make the trait safe.
- Make `Ownable::release` take a mutable reference.
- Fix error handling in example for `Ownable`
- Link to v15: https://msgid.link/20260220-unique-ref-v15-0-893ed86b06cc@kernel.org
Changes in v15:
- Update series with original SoB's.
- Rename `AlwaysRefCounted` in `kernel::usb`.
- Rename `Owned::get_pin_mut` to `Owned::as_pin_mut`.
- Link to v14: https://msgid.link/20260204-unique-ref-v14-0-17cb29ebacbb@kernel.org
Changes in v14:
- Rebase on v6.19-rc7.
- Rewrite cover letter.
- Update documentation and safety comments based on v13 feedback.
- Update commit messages.
- Reorder implementation blocks in owned.rs.
- Update example in owned.rs to use try operator rather than `expect`.
- Reformat use statements.
- Add patch: rust: page: convert to `Ownable`.
- Add patch: rust: implement `ForeignOwnable` for `Owned`.
- Add patch: rust: page: add `from_raw()`.
- Link to v13: https://lore.kernel.org/r/20251117-unique-ref-v13-0-b5b243df1250@pm.me
Changes in v13:
- Rebase onto v6.18-rc1 (Andreas's work).
- Documentation and style fixes contributed by Andreas
- Link to v12: https://lore.kernel.org/r/20251001-unique-ref-v12-0-fa5c31f0c0c4@pm.me
Changes in v12:
-
- Rebase onto v6.17-rc1 (Andreas's work).
- moved kernel/types/ownable.rs to kernel/owned.rs
- Drop OwnableMut, make DerefMut depend on Unpin instead. I understood
ML discussion as that being okay, but probably needs further scrunity.
- Lots of more documentation changes suggested by reviewers.
- Usage example for Ownable/Owned.
- Link to v11: https://lore.kernel.org/r/20250618-unique-ref-v11-0-49eadcdc0aa6@pm.me
Changes in v11:
- Rework of documentation. I tried to honor all requests for changes "in
spirit" plus some clearifications and corrections of my own.
- Dropping `SimpleOwnedRefCounted` by request from Alice, as it creates a
potentially problematic blanket implementation (which a derive macro that
could be created later would not have).
- Dropping Miguel's "kbuild: provide `RUSTC_HAS_DO_NOT_RECOMMEND` symbol"
patch, as it is not needed anymore after dropping `SimpleOwnedRefCounted`.
(I can add it again, if it is considered useful anyway).
- Link to v10: https://lore.kernel.org/r/20250502-unique-ref-v10-0-25de64c0307f@pm.me
Changes in v10:
- Moved kernel/ownable.rs to kernel/types/ownable.rs
- Fixes in documentation / comments as suggested by Andreas Hindborg
- Added Reviewed-by comment for Andreas Hindborg
- Fix rustfmt of pid_namespace.rs
- Link to v9: https://lore.kernel.org/r/20250325-unique-ref-v9-0-e91618c1de26@pm.me
Changes in v9:
- Rebase onto v6.14-rc7
- Move Ownable/OwnedRefCounted/Ownable, etc., into separate module
- Documentation fixes to Ownable/OwnableMut/OwnableRefCounted
- Add missing SAFETY documentation to ARef example
- Link to v8: https://lore.kernel.org/r/20250313-unique-ref-v8-0-3082ffc67a31@pm.me
Changes in v8:
- Fix Co-developed-by and Suggested-by tags as suggested by Miguel and Boqun
- Some small documentation fixes in Owned/Ownable patch
- removing redundant trait constraint on DerefMut for Owned as suggested by Boqun Feng
- make SimpleOwnedRefCounted no longer implement RefCounted as suggested by Boqun Feng
- documentation for RefCounted as suggested by Boqun Feng
- Link to v7: https://lore.kernel.org/r/20250310-unique-ref-v7-0-4caddb78aa05@pm.me
Changes in v7:
- Squash patch to make Owned::from_raw/into_raw public into parent
- Added Signed-off-by to other people's commits
- Link to v6: https://lore.kernel.org/r/20250310-unique-ref-v6-0-1ff53558617e@pm.me
Changes in v6:
- Changed comments/formatting as suggested by Miguel Ojeda
- Included and used new config flag RUSTC_HAS_DO_NOT_RECOMMEND,
thus no changes to types.rs will be needed when the attribute
becomes available.
- Fixed commit message for Owned patch.
- Link to v5: https://lore.kernel.org/r/20250307-unique-ref-v5-0-bffeb633277e@pm.me
Changes in v5:
- Rebase the whole thing on top of the Ownable/Owned traits by Asahi Lina.
- Rename AlwaysRefCounted to RefCounted and make AlwaysRefCounted a
marker trait instead to allow to obtain an ARef<T> from an &T,
which (as Alice pointed out) is unsound when combined with UniqueRef/Owned.
- Change the Trait design and naming to implement this feature,
UniqueRef/UniqueRefCounted is dropped in favor of Ownable/Owned and
OwnableRefCounted is used to provide the functions to convert
between Owned and ARef.
- Link to v4: https://lore.kernel.org/r/20250305-unique-ref-v4-1-a8fdef7b1c2c@pm.me
Changes in v4:
- Just a minor change in naming by request from Andreas Hindborg,
try_shared_to_unique() -> try_from_shared(),
unique_to_shared() -> into_shared(),
which is more in line with standard Rust naming conventions.
- Link to v3: https://lore.kernel.org/r/Z8Wuud2UQX6Yukyr@mango
To: Danilo Krummrich <dakr@kernel.org>
To: Lorenzo Stoakes <ljs@kernel.org>
To: Vlastimil Babka <vbabka@kernel.org>
To: "Liam R. Howlett" <liam@infradead.org>
To: Uladzislau Rezki <urezki@gmail.com>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Daniel Almeida <daniel.almeida@collabora.com>
To: Tamir Duberstein <tamird@kernel.org>
To: Alexandre Courbot <acourbot@nvidia.com>
To: Onur Özkan <work@onurozkan.dev>
To: Lyude Paul <lyude@redhat.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Arve Hjønnevåg <arve@android.com>
To: Todd Kjos <tkjos@android.com>
To: Christian Brauner <brauner@kernel.org>
To: Carlos Llamas <cmllamas@google.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
To: Dave Ertman <david.m.ertman@intel.com>
To: Ira Weiny <ira.weiny@intel.com>
To: Leon Romanovsky <leon@kernel.org>
To: Paul Moore <paul@paul-moore.com>
To: Serge Hallyn <sergeh@kernel.org>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Alexander Viro <viro@zeniv.linux.org.uk>
To: Jan Kara <jack@suse.cz>
To: Igor Korotin <igor.korotin@linux.dev>
To: Viresh Kumar <vireshk@kernel.org>
To: Nishanth Menon <nm@ti.com>
To: Stephen Boyd <sboyd@kernel.org>
To: Bjorn Helgaas <bhelgaas@google.com>
To: Krzysztof Wilczyński <kwilczynski@kernel.org>
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
To: Michal Wilczynski <m.wilczynski@samsung.com>
Cc: Philipp Stanner <phasta@kernel.org>
Cc: rust-for-linux@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: driver-core@lists.linux.dev
Cc: linux-block@vger.kernel.org
Cc: linux-security-module@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: linux-pci@vger.kernel.org
Cc: linux-pwm@vger.kernel.org
---
Andreas Hindborg (3):
rust: alloc: add `KBox::into_non_null`
rust: implement `ForeignOwnable` for `Owned`
rust: page: add `from_raw()`
Asahi Lina (2):
rust: types: Add Ownable/Owned types
rust: page: convert to `Ownable`
Oliver Mangold (3):
rust: rename `AlwaysRefCounted` to `RefCounted`.
rust: Add missing SAFETY documentation for `ARef` example
rust: Add `OwnableRefCounted`
drivers/android/binder/page_range.rs | 10 +-
rust/kernel/alloc/allocator.rs | 19 +-
rust/kernel/alloc/allocator/iter.rs | 6 +-
rust/kernel/alloc/kbox.rs | 9 +
rust/kernel/auxiliary.rs | 10 +-
rust/kernel/block/mq/request.rs | 19 +-
rust/kernel/cred.rs | 16 +-
rust/kernel/device.rs | 12 +-
rust/kernel/device/property.rs | 11 +-
rust/kernel/drm/device.rs | 9 +-
rust/kernel/drm/gem/mod.rs | 16 +-
rust/kernel/fs/file.rs | 23 ++-
rust/kernel/i2c.rs | 13 +-
rust/kernel/lib.rs | 1 +
rust/kernel/mm.rs | 22 ++-
rust/kernel/mm/mmput_async.rs | 12 +-
rust/kernel/opp.rs | 16 +-
rust/kernel/owned.rs | 371 +++++++++++++++++++++++++++++++++++
rust/kernel/page.rs | 136 +++++--------
rust/kernel/pci.rs | 10 +-
rust/kernel/pid_namespace.rs | 15 +-
rust/kernel/platform.rs | 10 +-
rust/kernel/pwm.rs | 12 +-
rust/kernel/sync/aref.rs | 82 +++++---
rust/kernel/task.rs | 13 +-
rust/kernel/types.rs | 12 ++
rust/kernel/usb.rs | 17 +-
27 files changed, 721 insertions(+), 181 deletions(-)
---
base-commit: 43a393185e33e573a374c1d4f7ddf6481484ef8d
change-id: 20250305-unique-ref-29fcd675f9e9
Best regards,
--
Andreas Hindborg <a.hindborg@kernel.org>
^ permalink raw reply
* [PATCH v18 6/8] rust: Add missing SAFETY documentation for `ARef` example
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Oliver Mangold
In-Reply-To: <20260625-unique-ref-v18-0-4e06b5896d47@kernel.org>
From: Oliver Mangold <oliver.mangold@pm.me>
SAFETY comment in rustdoc example was just 'TODO'. Fixed.
Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Co-developed-by: Andreas Hindborg <a.hindborg@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/sync/aref.rs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index fb7466a362741..d0865aeb9371b 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -142,7 +142,9 @@ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
///
/// struct Empty {}
///
- /// # // SAFETY: TODO.
+ /// // SAFETY: The `RefCounted` implementation for `Empty` does not count references and never
+ /// // frees the underlying object. Thus we can act as owning an increment on the refcount for
+ /// // the object that we pass to the newly created `ARef`.
/// unsafe impl RefCounted for Empty {
/// fn inc_ref(&self) {}
/// unsafe fn dec_ref(_obj: NonNull<Self>) {}
@@ -150,7 +152,7 @@ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
///
/// let mut data = Empty {};
/// let ptr = NonNull::<Empty>::new(&mut data).unwrap();
- /// # // SAFETY: TODO.
+ /// // SAFETY: We keep `data` around longer than the `ARef`.
/// let data_ref: ARef<Empty> = unsafe { ARef::from_raw(ptr) };
/// let raw_ptr: NonNull<Empty> = ARef::into_raw(data_ref);
///
--
2.51.2
^ permalink raw reply related
* [PATCH v18 2/8] rust: types: Add Ownable/Owned types
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Asahi Lina, Oliver Mangold, Boqun Feng
In-Reply-To: <20260625-unique-ref-v18-0-4e06b5896d47@kernel.org>
From: Asahi Lina <lina+kernel@asahilina.net>
By analogy to `AlwaysRefCounted` and `ARef`, an `Ownable` type is a
(typically C FFI) type that *may* be owned by Rust, but need not be. Unlike
`AlwaysRefCounted`, this mechanism expects the reference to be unique
within Rust, and does not allow cloning.
Conceptually, this is similar to a `KBox<T>`, except that it delegates
resource management to the `T` instead of using a generic allocator.
[ om:
- Split code into separate file and `pub use` it from types.rs.
- Make from_raw() and into_raw() public.
- Remove OwnableMut, and make DerefMut dependent on Unpin instead.
- Usage example/doctest for Ownable/Owned.
- Fixes to documentation and commit message.
]
Link: https://lore.kernel.org/all/20250202-rust-page-v1-1-e3170d7fe55e@asahilina.net/
Signed-off-by: Asahi Lina <lina+kernel@asahilina.net>
Co-developed-by: Oliver Mangold <oliver.mangold@pm.me>
Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
[ Andreas: Updated documentation, examples, and formatting. Change safety
requirements, safety comments. ]
Co-developed-by: Andreas Hindborg <a.hindborg@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/lib.rs | 1 +
rust/kernel/owned.rs | 188 +++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/sync/aref.rs | 5 ++
rust/kernel/types.rs | 5 ++
4 files changed, 199 insertions(+)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9512af7156df2..eb5256204a174 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -101,6 +101,7 @@
pub mod of;
#[cfg(CONFIG_PM_OPP)]
pub mod opp;
+pub mod owned;
pub mod page;
#[cfg(CONFIG_PCI)]
pub mod pci;
diff --git a/rust/kernel/owned.rs b/rust/kernel/owned.rs
new file mode 100644
index 0000000000000..7fe9ec3e55126
--- /dev/null
+++ b/rust/kernel/owned.rs
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Unique owned pointer types for objects with custom drop logic.
+//!
+//! These pointer types are useful for C-allocated objects which by API-contract
+//! are owned by Rust, but need to be freed through the C API.
+
+use core::{
+ mem::ManuallyDrop,
+ ops::{
+ Deref,
+ DerefMut, //
+ },
+ pin::Pin,
+ ptr::NonNull, //
+};
+
+/// Types that specify their own way of performing allocation and destruction. Typically, this trait
+/// is implemented on types from the C side.
+///
+/// Implementing this trait allows types to be referenced via the [`Owned<Self>`] pointer type. This
+/// is useful when it is desirable to tie the lifetime of the reference to an owned object, rather
+/// than pass around a bare reference. [`Ownable`] types can define custom drop logic that is
+/// executed when the owned reference [`Owned<Self>`] pointing to the object is dropped.
+///
+/// Note: The underlying object is not required to provide internal reference counting, because it
+/// represents a unique, owned reference. If reference counting (on the Rust side) is required,
+/// [`AlwaysRefCounted`](crate::sync::aref::AlwaysRefCounted) should be implemented.
+///
+/// # Examples
+///
+/// A minimal example implementation of [`Ownable`] and its usage with [`Owned`] looks like
+/// this:
+///
+/// ```
+/// # #![expect(clippy::disallowed_names)]
+/// # use core::cell::Cell;
+/// # use core::ptr::NonNull;
+/// # use kernel::sync::global_lock;
+/// # use kernel::alloc::{flags, kbox::KBox, AllocError};
+/// # use kernel::types::{Owned, Ownable};
+///
+/// // Let's count the allocations to see if freeing works.
+/// kernel::sync::global_lock! {
+/// // SAFETY: we call `init()` right below, before doing anything else.
+/// unsafe(uninit) static FOO_ALLOC_COUNT: Mutex<usize> = 0;
+/// }
+/// // SAFETY: We call `init()` only once, here.
+/// unsafe { FOO_ALLOC_COUNT.init() };
+///
+/// struct Foo;
+///
+/// impl Foo {
+/// fn new() -> Result<Owned<Self>> {
+/// // We are just using a `KBox` here to handle the actual allocation, as our `Foo` is
+/// // not actually a C-allocated object.
+/// let result = KBox::new(
+/// Foo {},
+/// flags::GFP_KERNEL,
+/// )?;
+/// let result = KBox::into_non_null(result);
+/// // Count new allocation
+/// *FOO_ALLOC_COUNT.lock() += 1;
+/// // SAFETY:
+/// // - We just allocated the `Self`, thus it is valid and we own it.
+/// // - We can transfer this ownership to the `from_raw` method.
+/// Ok(unsafe { Owned::from_raw(result) })
+/// }
+/// }
+///
+/// impl Ownable for Foo {
+/// unsafe fn release(this: NonNull<Self>) {
+/// // SAFETY: The [`KBox<Self>`] is still alive. We can pass ownership to the [`KBox`], as
+/// // by requirement on calling this function.
+/// drop(unsafe { KBox::from_raw(this.as_ptr()) });
+/// // Count released allocation
+/// *FOO_ALLOC_COUNT.lock() -= 1;
+/// }
+/// }
+///
+/// {
+/// let foo = Foo::new()?;
+/// assert!(*FOO_ALLOC_COUNT.lock() == 1);
+/// }
+/// // `foo` is out of scope now, so we expect no live allocations.
+/// assert!(*FOO_ALLOC_COUNT.lock() == 0);
+/// # Ok::<(), Error>(())
+/// ```
+pub trait Ownable {
+ /// Tear down this `Ownable`.
+ ///
+ /// Implementers of `Ownable` can use this function to clean up the use of `Self`. This can
+ /// include freeing the underlying object.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that they have exclusive ownership of the `Self` pointed to by `this`,
+ /// and that this ownership is transferred to the `release` method. `this` must not be used
+ /// after calling this method, as the underlying object may have been freed.
+ unsafe fn release(this: NonNull<Self>);
+}
+
+/// A mutable reference to an owned `T`.
+///
+/// The [`Ownable`] is automatically freed or released when an instance of [`Owned`] is
+/// dropped.
+///
+/// # Invariants
+///
+/// - Until `T::release` is called, this `Owned<T>` exclusively owns the underlying `T`.
+/// - The `T` value is pinned.
+pub struct Owned<T: Ownable> {
+ ptr: NonNull<T>,
+}
+
+impl<T: Ownable> Owned<T> {
+ /// Creates a new instance of [`Owned`].
+ ///
+ /// This function takes over ownership of the underlying object.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that:
+ /// - `ptr` points to a valid instance of `T`.
+ /// - Until `T::release` is called, the returned `Owned<T>` exclusively owns the underlying `T`.
+ #[inline]
+ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
+ // INVARIANT: By function safety requirement we satisfy the first invariant of `Self`.
+ // We treat `T` as pinned from now on.
+ Self { ptr }
+ }
+
+ /// Consumes the [`Owned`], returning a raw pointer.
+ ///
+ /// This function does not drop the underlying `T`. When this function returns, ownership of the
+ /// underlying `T` is with the caller.
+ #[inline]
+ pub fn into_raw(me: Self) -> NonNull<T> {
+ ManuallyDrop::new(me).ptr
+ }
+
+ /// Get a pinned mutable reference to the data owned by this `Owned<T>`.
+ #[inline]
+ pub fn as_pin_mut(&mut self) -> Pin<&mut T> {
+ // SAFETY: The type invariants guarantee that the object is valid, and that we can safely
+ // return a mutable reference to it.
+ let unpinned = unsafe { self.ptr.as_mut() };
+
+ // SAFETY: By type invariant `T` is pinned.
+ unsafe { Pin::new_unchecked(unpinned) }
+ }
+}
+
+// SAFETY: It is safe to send an [`Owned<T>`] to another thread when the underlying `T` is [`Send`],
+// because of the ownership invariant. Sending an [`Owned<T>`] is equivalent to sending the `T`.
+unsafe impl<T: Ownable + Send> Send for Owned<T> {}
+
+// SAFETY: It is safe to send [`&Owned<T>`] to another thread when the underlying `T` is [`Sync`],
+// because of the ownership invariant. Sending an [`&Owned<T>`] is equivalent to sending the `&T`.
+unsafe impl<T: Ownable + Sync> Sync for Owned<T> {}
+
+impl<T: Ownable> Deref for Owned<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The type invariants guarantee that the object is valid.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<T: Ownable + Unpin> DerefMut for Owned<T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: The type invariants guarantee that the object is valid, and that we can safely
+ // return a mutable reference to it.
+ unsafe { self.ptr.as_mut() }
+ }
+}
+
+impl<T: Ownable> Drop for Owned<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: By existence of `&mut self` we exclusively own `self` and the underlying `T`. As
+ // we are dropping `self`, we can transfer ownership of the `T` to the `release` method.
+ unsafe { T::release(self.ptr) };
+ }
+}
diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index b721b2e00b986..3bd5eb8a1a526 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -34,6 +34,11 @@
/// Rust code, the recommendation is to use [`Arc`](crate::sync::Arc) to create reference-counted
/// instances of a type.
///
+/// Note: Implementing this trait allows types to be wrapped in an [`ARef<Self>`]. It requires an
+/// internal reference count and provides only shared references. If unique references are required
+/// [`Ownable`](crate::types::Ownable) should be implemented which allows types to be wrapped in an
+/// [`Owned<Self>`](crate::types::Owned).
+///
/// # Safety
///
/// Implementers must ensure that increments to the reference count keep the object alive in memory
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index ac316fd7b538f..c41eab0ec983c 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -15,6 +15,11 @@
pub mod for_lt;
pub use for_lt::ForLt;
+pub use crate::owned::{
+ Ownable,
+ Owned, //
+};
+
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
/// Ownership is transferred from Rust to a foreign language by calling [`Self::into_foreign`] and
--
2.51.2
^ permalink raw reply related
* [PATCH v18 8/8] rust: page: add `from_raw()`
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Andreas Hindborg
In-Reply-To: <20260625-unique-ref-v18-0-4e06b5896d47@kernel.org>
From: Andreas Hindborg <a.hindborg@samsung.com>
Add a method to `Page` that allows construction of an instance from `struct
page` pointer.
Signed-off-by: Andreas Hindborg <a.hindborg@samsung.com>
Reviewed-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/page.rs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 6dc1c2395acaf..c88fda09ead5a 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -143,6 +143,20 @@ pub fn nid(&self) -> i32 {
unsafe { bindings::page_to_nid(self.as_ptr()) }
}
+ /// Create a `&Page` from a raw `struct page` pointer.
+ ///
+ /// # Safety
+ ///
+ /// `ptr` must be convertible to a shared reference with a lifetime of `'a`.
+ #[inline]
+ pub unsafe fn from_raw<'a>(ptr: *const bindings::page) -> &'a Self {
+ // INVARIANT: By the function safety requirements, `ptr` refers to a valid `struct page`, so
+ // the returned reference upholds the type invariant of `Page`.
+ // SAFETY: By function safety requirements, `ptr` is not null and is convertible to a shared
+ // reference.
+ unsafe { &*ptr.cast() }
+ }
+
/// Runs a piece of code with this page mapped to an address.
///
/// The page is unmapped when this call returns.
--
2.51.2
^ permalink raw reply related
* [PATCH v18 4/8] rust: page: convert to `Ownable`
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Asahi Lina
In-Reply-To: <20260625-unique-ref-v18-0-4e06b5896d47@kernel.org>
From: Asahi Lina <lina@asahilina.net>
This allows Page references to be returned as borrowed references,
without necessarily owning the struct page.
Remove `BorrowedPage` and update users to use `Owned<Page>`.
Signed-off-by: Asahi Lina <lina@asahilina.net>
[ Andreas: Fix formatting and add a safety comment, update users. ]
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/android/binder/page_range.rs | 10 +--
rust/kernel/alloc/allocator.rs | 19 +++---
rust/kernel/alloc/allocator/iter.rs | 6 +-
rust/kernel/page.rs | 122 +++++++++--------------------------
4 files changed, 46 insertions(+), 111 deletions(-)
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index e54a90e62402a..7941eb85b4ef4 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -33,7 +33,7 @@
sync::{aref::ARef, Mutex, SpinLock},
task::Pid,
transmute::FromBytes,
- types::Opaque,
+ types::{Opaque, Owned},
uaccess::UserSliceReader,
};
@@ -198,7 +198,7 @@ unsafe impl Send for Inner {}
#[repr(C)]
struct PageInfo {
lru: bindings::list_head,
- page: Option<Page>,
+ page: Option<Owned<Page>>,
range: *const ShrinkablePageRange,
}
@@ -206,7 +206,7 @@ impl PageInfo {
/// # Safety
///
/// The caller ensures that writing to `me.page` is ok, and that the page is not currently set.
- unsafe fn set_page(me: *mut PageInfo, page: Page) {
+ unsafe fn set_page(me: *mut PageInfo, page: Owned<Page>) {
// SAFETY: This pointer offset is in bounds.
let ptr = unsafe { &raw mut (*me).page };
@@ -229,13 +229,13 @@ unsafe fn get_page<'a>(me: *const PageInfo) -> Option<&'a Page> {
let ptr = unsafe { &raw const (*me).page };
// SAFETY: The pointer is valid for reading.
- unsafe { (*ptr).as_ref() }
+ unsafe { (*ptr).as_deref() }
}
/// # Safety
///
/// The caller ensures that writing to `me.page` is ok for the duration of 'a.
- unsafe fn take_page(me: *mut PageInfo) -> Option<Page> {
+ unsafe fn take_page(me: *mut PageInfo) -> Option<Owned<Page>> {
// SAFETY: This pointer offset is in bounds.
let ptr = unsafe { &raw mut (*me).page };
diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index cd4203f27aed0..c7b9b069cf75d 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -169,7 +169,7 @@ unsafe fn realloc(
}
impl Vmalloc {
- /// Convert a pointer to a [`Vmalloc`] allocation to a [`page::BorrowedPage`].
+ /// Convert a pointer to a [`Vmalloc`] allocation to a [`Page`](page::Page) reference.
///
/// # Examples
///
@@ -202,20 +202,17 @@ impl Vmalloc {
///
/// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation.
/// - `ptr` must remain valid for the entire duration of `'a`.
- pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {
+ pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> &'a page::Page {
// SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };
- // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer
- // to `Vmalloc` memory.
- let page = unsafe { NonNull::new_unchecked(page) };
-
// SAFETY:
- // - `page` is a valid pointer to a `struct page`, given that by the safety requirements of
- // this function `ptr` is a valid pointer to a `Vmalloc` allocation.
- // - By the safety requirements of this function `ptr` is valid for the entire lifetime of
- // `'a`.
- unsafe { page::BorrowedPage::from_raw(page) }
+ // - `vmalloc_to_page` returns a valid, non-null pointer to a `struct page` for a valid
+ // pointer to `Vmalloc` memory, given that by the safety requirements of this function
+ // `ptr` is a valid pointer to a `Vmalloc` allocation.
+ // - By the safety requirements of this function `ptr`, and hence the `struct page`, is
+ // valid for the entire lifetime of `'a`.
+ unsafe { &*page.cast() }
}
}
diff --git a/rust/kernel/alloc/allocator/iter.rs b/rust/kernel/alloc/allocator/iter.rs
index 02fda3ea5cae6..8dcc16ed89893 100644
--- a/rust/kernel/alloc/allocator/iter.rs
+++ b/rust/kernel/alloc/allocator/iter.rs
@@ -9,7 +9,7 @@
ptr::NonNull, //
};
-/// An [`Iterator`] of [`page::BorrowedPage`] items owned by a [`Vmalloc`] allocation.
+/// An [`Iterator`] of [`Page`](page::Page) references owned by a [`Vmalloc`] allocation.
///
/// # Guarantees
///
@@ -28,11 +28,11 @@ pub struct VmallocPageIter<'a> {
size: usize,
/// The current page index of the [`Iterator`].
index: usize,
- _p: PhantomData<page::BorrowedPage<'a>>,
+ _p: PhantomData<&'a page::Page>,
}
impl<'a> Iterator for VmallocPageIter<'a> {
- type Item = page::BorrowedPage<'a>;
+ type Item = &'a page::Page;
fn next(&mut self) -> Option<Self::Item> {
let offset = self.index.checked_mul(page::PAGE_SIZE)?;
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 8affd8262891b..6dc1c2395acaf 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -12,16 +12,16 @@
code::*,
Result, //
},
+ types::{
+ Opaque,
+ Ownable,
+ Owned, //
+ },
uaccess::UserSliceReader, //
};
-use core::{
- marker::PhantomData,
- mem::ManuallyDrop,
- ops::Deref,
- ptr::{
- self,
- NonNull, //
- }, //
+use core::ptr::{
+ self,
+ NonNull, //
};
/// A bitwise shift for the page size.
@@ -65,93 +65,29 @@ pub const fn page_align(addr: usize) -> Option<usize> {
Some(sum & PAGE_MASK)
}
-/// Representation of a non-owning reference to a [`Page`].
-///
-/// This type provides a borrowed version of a [`Page`] that is owned by some other entity, e.g. a
-/// [`Vmalloc`] allocation such as [`VBox`].
-///
-/// # Example
-///
-/// ```
-/// # use kernel::{bindings, prelude::*};
-/// use kernel::page::{BorrowedPage, Page, PAGE_SIZE};
-/// # use core::{mem::MaybeUninit, ptr, ptr::NonNull };
-///
-/// fn borrow_page<'a>(vbox: &'a mut VBox<MaybeUninit<[u8; PAGE_SIZE]>>) -> BorrowedPage<'a> {
-/// let ptr = ptr::from_ref(&**vbox);
-///
-/// // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
-/// let page = unsafe { bindings::vmalloc_to_page(ptr.cast()) };
-///
-/// // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid
-/// // pointer to `Vmalloc` memory.
-/// let page = unsafe { NonNull::new_unchecked(page) };
-///
-/// // SAFETY:
-/// // - `self.0` is a valid pointer to a `struct page`.
-/// // - `self.0` is valid for the entire lifetime of `self`.
-/// unsafe { BorrowedPage::from_raw(page) }
-/// }
-///
-/// let mut vbox = VBox::<[u8; PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
-/// let page = borrow_page(&mut vbox);
-///
-/// // SAFETY: There is no concurrent read or write to this page.
-/// unsafe { page.fill_zero_raw(0, PAGE_SIZE)? };
-/// # Ok::<(), Error>(())
-/// ```
-///
-/// # Invariants
-///
-/// The borrowed underlying pointer to a `struct page` is valid for the entire lifetime `'a`.
-///
-/// [`VBox`]: kernel::alloc::VBox
-/// [`Vmalloc`]: kernel::alloc::allocator::Vmalloc
-pub struct BorrowedPage<'a>(ManuallyDrop<Page>, PhantomData<&'a Page>);
-
-impl<'a> BorrowedPage<'a> {
- /// Constructs a [`BorrowedPage`] from a raw pointer to a `struct page`.
- ///
- /// # Safety
- ///
- /// - `ptr` must point to a valid `bindings::page`.
- /// - `ptr` must remain valid for the entire lifetime `'a`.
- pub unsafe fn from_raw(ptr: NonNull<bindings::page>) -> Self {
- let page = Page { page: ptr };
-
- // INVARIANT: The safety requirements guarantee that `ptr` is valid for the entire lifetime
- // `'a`.
- Self(ManuallyDrop::new(page), PhantomData)
- }
-}
-
-impl<'a> Deref for BorrowedPage<'a> {
- type Target = Page;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-/// Trait to be implemented by types which provide an [`Iterator`] implementation of
-/// [`BorrowedPage`] items, such as [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
+/// Trait to be implemented by types which provide an [`Iterator`] of [`Page`] references, such as
+/// [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
pub trait AsPageIter {
/// The [`Iterator`] type, e.g. [`VmallocPageIter`](kernel::alloc::allocator::VmallocPageIter).
- type Iter<'a>: Iterator<Item = BorrowedPage<'a>>
+ type Iter<'a>: Iterator<Item = &'a Page>
where
Self: 'a;
- /// Returns an [`Iterator`] of [`BorrowedPage`] items over all pages owned by `self`.
+ /// Returns an [`Iterator`] of [`Page`] references over all pages owned by `self`.
fn page_iter(&mut self) -> Self::Iter<'_>;
}
-/// A pointer to a page that owns the page allocation.
+/// A `struct page`.
+///
+/// A `Page` is accessed through a shared reference or through an owning [`Owned<Page>`]; the latter
+/// frees the page allocation when it is dropped.
///
/// # Invariants
///
-/// The pointer is valid, and has ownership over the page.
+/// The `Page` is backed by a valid `struct page`.
+#[repr(transparent)]
pub struct Page {
- page: NonNull<bindings::page>,
+ page: Opaque<bindings::page>,
}
// SAFETY: Pages have no logic that relies on them staying on a given thread, so moving them across
@@ -185,19 +121,20 @@ impl Page {
/// # Ok::<(), kernel::alloc::AllocError>(())
/// ```
#[inline]
- pub fn alloc_page(flags: Flags) -> Result<Self, AllocError> {
+ pub fn alloc_page(flags: Flags) -> Result<Owned<Self>, AllocError> {
// SAFETY: Depending on the value of `gfp_flags`, this call may sleep. Other than that, it
// is always safe to call this method.
let page = unsafe { bindings::alloc_pages(flags.as_raw(), 0) };
let page = NonNull::new(page).ok_or(AllocError)?;
- // INVARIANT: We just successfully allocated a page, so we now have ownership of the newly
- // allocated page. We transfer that ownership to the new `Page` object.
- Ok(Self { page })
+ // SAFETY: We just successfully allocated a page, so we now have ownership of the newly
+ // allocated page. We transfer that ownership to the new `Owned<Page>` object.
+ // Since `Page` is transparent, we can cast the pointer directly.
+ Ok(unsafe { Owned::from_raw(page.cast()) })
}
/// Returns a raw pointer to the page.
pub fn as_ptr(&self) -> *mut bindings::page {
- self.page.as_ptr()
+ Opaque::cast_into(&self.page)
}
/// Get the node id containing this page.
@@ -372,10 +309,11 @@ pub unsafe fn copy_from_user_slice_raw(
}
}
-impl Drop for Page {
+impl Ownable for Page {
#[inline]
- fn drop(&mut self) {
- // SAFETY: By the type invariants, we have ownership of the page and can free it.
- unsafe { bindings::__free_pages(self.page.as_ptr(), 0) };
+ unsafe fn release(this: NonNull<Self>) {
+ // SAFETY: By the function safety requirements, we have ownership of the page and can free
+ // it. Since Page is transparent, we can cast the raw pointer directly.
+ unsafe { bindings::__free_pages(this.as_ptr().cast(), 0) };
}
}
--
2.51.2
^ permalink raw reply related
* [PATCH v18 7/8] rust: Add `OwnableRefCounted`
From: Andreas Hindborg @ 2026-06-25 10:15 UTC (permalink / raw)
To: Danilo Krummrich, Lorenzo Stoakes, Vlastimil Babka,
Liam R. Howlett, Uladzislau Rezki, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
Trevor Gross, Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Lyude Paul, Greg Kroah-Hartman,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas, Rafael J. Wysocki, Dave Ertman, Ira Weiny,
Leon Romanovsky, Paul Moore, Serge Hallyn, David Airlie,
Simona Vetter, Alexander Viro, Jan Kara, Igor Korotin,
Viresh Kumar, Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Pavel Tikhomirov, Michal Wilczynski
Cc: Andreas Hindborg, Philipp Stanner, rust-for-linux, linux-kernel,
linux-mm, driver-core, linux-block, linux-security-module,
dri-devel, linux-fsdevel, linux-pm, linux-pci, linux-pwm,
Oliver Mangold
In-Reply-To: <20260625-unique-ref-v18-0-4e06b5896d47@kernel.org>
From: Oliver Mangold <oliver.mangold@pm.me>
Types implementing one of these traits can safely convert between an
`ARef<T>` and an `Owned<T>`.
This is useful for types which generally are accessed through an `ARef`
but have methods which can only safely be called when the reference is
unique, like e.g. `block::mq::Request::end_ok()`.
Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
[ Andreas: Fix formatting, update documentation, fix error handling in
examples. ]
Co-developed-by: Andreas Hindborg <a.hindborg@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/owned.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++--
rust/kernel/sync/aref.rs | 16 +++++-
rust/kernel/types.rs | 1 +
3 files changed, 151 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/owned.rs b/rust/kernel/owned.rs
index e79936c00002c..bb4223c0f725a 100644
--- a/rust/kernel/owned.rs
+++ b/rust/kernel/owned.rs
@@ -14,20 +14,26 @@
pin::Pin,
ptr::NonNull, //
};
+use kernel::{
+ sync::aref::ARef,
+ types::RefCounted, //
+};
use kernel::types::ForeignOwnable;
/// Types that specify their own way of performing allocation and destruction. Typically, this trait
/// is implemented on types from the C side.
///
-/// Implementing this trait allows types to be referenced via the [`Owned<Self>`] pointer type. This
-/// is useful when it is desirable to tie the lifetime of the reference to an owned object, rather
-/// than pass around a bare reference. [`Ownable`] types can define custom drop logic that is
-/// executed when the owned reference [`Owned<Self>`] pointing to the object is dropped.
+/// Implementing this trait allows types to be referenced via the [`Owned<Self>`] pointer type.
+/// - This is useful when it is desirable to tie the lifetime of an object reference to an owned
+/// object, rather than pass around a bare reference.
+/// - [`Ownable`] types can define custom drop logic that is executed when the owned reference
+/// of type [`Owned<_>`] pointing to the object is dropped.
///
/// Note: The underlying object is not required to provide internal reference counting, because it
/// represents a unique, owned reference. If reference counting (on the Rust side) is required,
-/// [`RefCounted`](crate::types::RefCounted) should be implemented.
+/// [`RefCounted`] should be implemented. [`OwnableRefCounted`] should be implemented if conversion
+/// between unique and shared (reference counted) ownership is needed.
///
/// # Examples
///
@@ -239,3 +245,127 @@ unsafe fn borrow_mut<'a>(ptr: *mut kernel::ffi::c_void) -> Self::BorrowedMut<'a>
unsafe { Pin::new_unchecked(inner) }
}
}
+
+/// A trait for objects that can be wrapped in either one of the reference types [`Owned`] and
+/// [`ARef`].
+///
+/// # Examples
+///
+/// A minimal example implementation of [`OwnableRefCounted`], [`Ownable`] and its usage with
+/// [`ARef`] and [`Owned`] looks like this:
+///
+/// ```
+/// # #![expect(clippy::disallowed_names)]
+/// # use core::cell::Cell;
+/// # use core::ptr::NonNull;
+/// # use kernel::alloc::{flags, kbox::KBox, AllocError};
+/// # use kernel::sync::aref::{ARef, RefCounted};
+/// # use kernel::types::{Owned, Ownable, OwnableRefCounted};
+///
+/// // An internally refcounted struct for demonstration purposes.
+/// //
+/// // # Invariants
+/// //
+/// // - `refcount` is always non-zero for a valid object.
+/// // - `refcount` is >1 if there is more than one Rust reference to it.
+/// //
+/// struct Foo {
+/// refcount: Cell<usize>,
+/// }
+///
+/// impl Foo {
+/// fn new() -> Result<Owned<Self>> {
+/// // We are just using a `KBox` here to handle the actual allocation, as our `Foo` is
+/// // not actually a C-allocated object.
+/// // INVARIANT: We initialize `refcount` to 1, satisfying the invariants.
+/// let result = KBox::new(
+/// Foo {
+/// refcount: Cell::new(1),
+/// },
+/// flags::GFP_KERNEL,
+/// )?;
+/// let result = KBox::into_non_null(result);
+/// // SAFETY:
+/// // - We just allocated the `Self`, thus it is valid and we own it.
+/// // - We can transfer this ownership to the `from_raw` method.
+/// Ok(unsafe { Owned::from_raw(result) })
+/// }
+/// }
+///
+/// // SAFETY: We increment and decrement each time the respective function is called and only free
+/// // the `Foo` when the refcount reaches zero.
+/// unsafe impl RefCounted for Foo {
+/// fn inc_ref(&self) {
+/// self.refcount.replace(self.refcount.get() + 1);
+/// }
+///
+/// unsafe fn dec_ref(this: NonNull<Self>) {
+/// // SAFETY: By requirement on calling this function, the refcount is non-zero,
+/// // implying the underlying object is valid.
+/// let refcount = unsafe { &this.as_ref().refcount };
+/// let new_refcount = refcount.get() - 1;
+/// if new_refcount == 0 {
+/// // The `Foo` will be dropped when `KBox` goes out of scope.
+/// // SAFETY: The [`KBox<Foo>`] is still alive as the old refcount is 1. We can pass
+/// // ownership to the [`KBox`] as by requirement on calling this function,
+/// // the `Self` will no longer be used by the caller.
+/// unsafe { KBox::from_raw(this.as_ptr()) };
+/// } else {
+/// refcount.replace(new_refcount);
+/// }
+/// }
+/// }
+///
+/// impl OwnableRefCounted for Foo {
+/// fn try_from_shared(this: ARef<Self>) -> Result<Owned<Self>, ARef<Self>> {
+/// if this.refcount.get() == 1 {
+/// // SAFETY: The `Foo` is still alive and has no other Rust references as the refcount
+/// // is 1.
+/// Ok(unsafe { Owned::from_raw(ARef::into_raw(this)) })
+/// } else {
+/// Err(this)
+/// }
+/// }
+/// }
+///
+/// impl Ownable for Foo {
+/// unsafe fn release(this: NonNull<Self>) {
+/// // SAFETY: Using `dec_ref()` from [`RefCounted`] to release is okay, as the refcount is
+/// // always 1 for an [`Owned<Foo>`].
+/// unsafe { Foo::dec_ref(this) };
+/// }
+/// }
+///
+/// let foo = Foo::new()?;
+/// let foo = ARef::from(foo);
+/// {
+/// let bar = foo.clone();
+/// assert!(Owned::try_from(bar).is_err());
+/// }
+/// assert!(Owned::try_from(foo).is_ok());
+/// # Ok::<(), Error>(())
+/// ```
+pub trait OwnableRefCounted: RefCounted + Ownable + Sized {
+ /// Checks if the [`ARef`] is unique and converts it to an [`Owned`] if that is the case.
+ /// Otherwise it returns again an [`ARef`] to the same underlying object.
+ fn try_from_shared(this: ARef<Self>) -> Result<Owned<Self>, ARef<Self>>;
+
+ /// Converts the [`Owned`] into an [`ARef`].
+ #[inline]
+ fn into_shared(this: Owned<Self>) -> ARef<Self> {
+ // SAFETY: `Owned::into_raw` returns a pointer to a valid `Self`, and the `Owned` owned the
+ // reference count that we now transfer to the new `ARef`.
+ unsafe { ARef::from_raw(Owned::into_raw(this)) }
+ }
+}
+
+impl<T: OwnableRefCounted> TryFrom<ARef<T>> for Owned<T> {
+ type Error = ARef<T>;
+ /// Tries to convert the [`ARef`] to an [`Owned`] by calling
+ /// [`try_from_shared()`](OwnableRefCounted::try_from_shared). In case the [`ARef`] is not
+ /// unique, it returns again an [`ARef`] to the same underlying object.
+ #[inline]
+ fn try_from(b: ARef<T>) -> Result<Owned<T>, Self::Error> {
+ T::try_from_shared(b)
+ }
+}
diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index d0865aeb9371b..77eb390139079 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -23,6 +23,10 @@
ops::Deref,
ptr::NonNull, //
};
+use kernel::types::{
+ OwnableRefCounted,
+ Owned, //
+};
/// Types that are internally reference counted.
///
@@ -35,7 +39,10 @@
/// Note: Implementing this trait allows types to be wrapped in an [`ARef<Self>`]. It requires an
/// internal reference count and provides only shared references. If unique references are required
/// [`Ownable`](crate::types::Ownable) should be implemented which allows types to be wrapped in an
-/// [`Owned<Self>`](crate::types::Owned).
+/// [`Owned<Self>`](crate::types::Owned). Implementing the trait
+/// [`OwnableRefCounted`] allows to convert between unique and
+/// shared references (i.e. [`Owned<Self>`](crate::types::Owned) and
+/// [`ARef<Self>`](crate::types::Owned)).
///
/// # Safety
///
@@ -188,6 +195,13 @@ fn from(b: &T) -> Self {
}
}
+impl<T: OwnableRefCounted> From<Owned<T>> for ARef<T> {
+ #[inline]
+ fn from(b: Owned<T>) -> Self {
+ T::into_shared(b)
+ }
+}
+
impl<T: RefCounted> Drop for ARef<T> {
fn drop(&mut self) {
// SAFETY: The type invariants guarantee that the `ARef` owns the reference we're about to
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 5ef763717e59a..6aa760952cb63 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -18,6 +18,7 @@
pub use crate::{
owned::{
Ownable,
+ OwnableRefCounted,
Owned, //
},
sync::aref::{
--
2.51.2
^ permalink raw reply related
* Re: [PATCH v4 2/2] rust: introduce abstractions for fwctl
From: Zhi Wang @ 2026-06-25 8:24 UTC (permalink / raw)
To: Danilo Krummrich
Cc: rust-for-linux, linux-kernel, jgg, gary, joelagnelf, aliceryhl,
kwilczynski, ojeda, alex.gaynor, boqun.feng, bjorn3_gh, lossin,
a.hindborg, tmgross, cjia, smitra, ankita, aniketa, kwankhede,
targupta, kjaju, alkumar, acourbot, jhubbard, zhiwang,
daniel.almeida
In-Reply-To: <DJHGQN654CJR.281FZSV2S5AI8@kernel.org>
On Wed, 24 Jun 2026 19:41:45 +0200
"Danilo Krummrich" <dakr@kernel.org> wrote:
> On Wed Jun 24, 2026 at 11:17 AM CEST, Zhi Wang wrote:
> > +impl<T: Operations> Device<T> {
snip
> I have recently been working on getting rid of Devres for
> Registration types and device resources in favor of Rust-native
> lifetimes using higher-ranked types (HRT). This has a couple of
> advantages, e.g. it simplifies accessing device resources from
> destructors and (with pin-init self-referential support) allows us to
> share (Rust) references between private data structures.
>
> This has been merged for existing device resources, bus device
> private data and auxiliary registration data [1]. There's also a
> follow-up series to support invariance [2] and another one to enable
> it for the DRM subsystem [3].
>
Hi Danilo:
Thanks so much for the pointers. I went through the HRT series. If we
are getting rid of the Devres with HRT, then my PATCH 1 is not required
as well. I actually got this callback example from DRM subsystem.
> Class devices infrastructure should follow that same pattern, i.e. the
> fwctl::Registration type should gain a lifetime and the fwctl private
> data provided via fwctl callbacks should be tied to the lifetime of
> the Registration, i.e. the lifetime the underlying bus device is
> bound to the driver.
>
Got it.
> Please find a diff in [4] implementing this for fwctl and a diff in
> [5] demonstrating how this is used in nova-core. (Feel free to pick
> up the provided code and use it in any way.)
>
Thanks so much. I will take a look and re-spin it today.
> (Please ignore that I use fwctl::DeviceType::Mlx5 in the nova-core
> diff, it is just there to make the code compile, so I could do a
> smoke test.)
>
> Thanks,
> Danilo
>
> [1]
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2c7c65933600e8db2ec1a78dec5008de876dd3ad
> [2]
> https://lore.kernel.org/driver-core/20260618230834.812007-1-dakr@kernel.org/
> [3]
> https://lore.kernel.org/driver-core/20260620184924.2247517-1-dakr@kernel.org/
>
> [4] FWCTL diff:
>
> diff --git a/rust/kernel/fwctl.rs b/rust/kernel/fwctl.rs
> index f5f802f5299c..65abea866b22 100644
> --- a/rust/kernel/fwctl.rs
> +++ b/rust/kernel/fwctl.rs
> @@ -8,16 +8,20 @@
> bindings,
> container_of,
> device,
> - devres::Devres,
> prelude::*,
> sync::aref::{
> ARef,
> AlwaysRefCounted, //
> },
> - types::Opaque, //
> + types::{
> + ForLt,
> + Opaque, //
> + }, //
> };
> use core::{
> + cell::UnsafeCell,
> marker::PhantomData,
> + mem,
> ptr::NonNull,
> slice, //
> };
> @@ -89,8 +93,12 @@ pub enum FwRpcResponse {
> /// vtable used by the core `fwctl` layer to manage per-FD user
> contexts and /// handle RPC requests.
> pub trait Operations: Sized + Send + Sync {
> - /// Driver data embedded alongside the `fwctl_device` allocation.
> - type DeviceData: Send + Sync;
> + /// Data owned by the [`Registration`] and accessible during
> callbacks.
> + ///
> + /// This is a [`ForLt`](trait@ForLt) type whose lifetime is tied
> to the parent bus device
> + /// binding scope. Drivers use this to store references to
> resources bound to this scope, such as PCI
> + /// BARs or typed bus device references.
> + type RegistrationData: ForLt;
>
> /// fwctl device type identifier.
> const DEVICE_TYPE: DeviceType;
> @@ -99,57 +107,68 @@ pub trait Operations: Sized + Send + Sync {
> ///
> /// Returns a [`PinInit`] initializer for `Self`. The instance
> is dropped /// automatically when the FD is closed (after
> [`close`](Self::close)).
> - fn open(device: &Device<Self>) -> impl PinInit<Self, Error>;
> + fn open<'a>(
> + device: &'a Device<Self>,
> + reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
> + ) -> impl PinInit<Self, Error>;
>
> /// Called when the user context is closed.
> ///
> /// The driver may perform additional cleanup here that requires
> access /// to the owning [`Device`]. `Self` is dropped automatically
> after this /// returns.
> - fn close(_this: Pin<&mut Self>, _device: &Device<Self>) {}
> + fn close<'a>(
> + _this: Pin<&mut Self>,
> + _device: &'a Device<Self>,
> + _reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
> + ) {
> + }
>
> /// Return device information to userspace.
> ///
> /// The default implementation returns no device-specific data.
> - fn info(_this: Pin<&Self>, _device: &Device<Self>) ->
> Result<KVec<u8>, Error> {
> + fn info<'a>(
> + _this: Pin<&Self>,
> + _device: &'a Device<Self>,
> + _reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
> + ) -> Result<KVec<u8>, Error> {
> Ok(KVec::new())
> }
>
> /// Handle a userspace RPC request.
> - fn fw_rpc(
> + fn fw_rpc<'a>(
> this: Pin<&Self>,
> - device: &Device<Self>,
> + device: &'a Device<Self>,
> + reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
> scope: RpcScope,
> rpc_in: &mut [u8],
> ) -> Result<FwRpcResponse, Error>;
> }
>
> -/// A fwctl device with embedded driver data.
> +/// A fwctl device.
> ///
> -/// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C
> -/// `fwctl_alloc_device()` layout convention.
> +/// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C
> `fwctl_alloc_device()` layout +/// convention. Contains a pointer to
> the [`Registration`]'s data, set at registration time and +///
> cleared on unregistration. ///
> /// # Invariants
> ///
> /// - `dev` is embedded at offset 0 and is initialised by fwctl.
> /// - The fwctl refcount owns the allocation lifetime.
> -/// - `data` is dropped from the fwctl core release hook before
> `kfree()`. +/// - `registration_data` is either `NonNull::dangling()`
> (before registration / after +/// unregistration) or points to a
> valid `RegistrationData` owned by the [`Registration`]. #[repr(C)]
> pub struct Device<T: Operations> {
> dev: Opaque<bindings::fwctl_device>,
> - data: T::DeviceData,
> + registration_data: UnsafeCell<NonNull<<T::RegistrationData as
> ForLt>::Of<'static>>>, }
>
> impl<T: Operations> Device<T> {
> - /// Allocate a new fwctl device with embedded driver data.
> + /// Allocate a new fwctl device.
> ///
> /// Returns an [`ARef`] that can be passed to
> [`Registration::new()`]
> - /// to make the device visible to userspace. The caller may
> inspect or
> - /// configure the device between allocation and registration.
> - pub fn new(
> - parent: &device::Device<device::Bound>,
> - data: impl PinInit<T::DeviceData, Error>,
> - ) -> Result<ARef<Self>> {
> + /// to make the device visible to userspace.
> + pub fn new(parent: &device::Device<device::Bound>) ->
> Result<ARef<Self>> { const_assert!(
> core::mem::offset_of!(Self, dev) == 0,
> "struct fwctl_device must be at offset 0"
> @@ -157,8 +176,7 @@ pub fn new(
>
> let ops =
> core::ptr::from_ref::<bindings::fwctl_ops>(&VTable::<T>::VTABLE).cast_mut();
> - // SAFETY: `ops` is static, `parent` is bound, and `size`
> includes the
> - // offset-0 `fwctl_device` plus `DeviceData`.
> + // SAFETY: `ops` is static, `parent` is bound, and `size`
> covers the full `Device<T>`. let raw = unsafe {
> bindings::_fwctl_alloc_device(parent.as_raw(), ops,
> core::mem::size_of::<Self>()) };
> @@ -169,42 +187,21 @@ pub fn new(
>
> let this = raw.cast::<Self>();
>
> + // INVARIANT: Set `registration_data` to dangling (no
> registration yet). // SAFETY: `this` points to the allocation just
> returned by fwctl.
> - let data_ptr = unsafe {
> core::ptr::addr_of_mut!((*this).data) };
> - // SAFETY: `data_ptr` addresses the uninitialised tail data.
> - unsafe { data.__pinned_init(data_ptr) }.inspect_err(|_| {
> - // SAFETY: `raw` still owns the initial reference.
> - unsafe { bindings::fwctl_put(raw) };
> - })?;
> -
> - // SAFETY: `raw` is a live fwctl_device allocated above.
> - unsafe { (*raw).release_data =
> Some(Self::release_data_callback) }; -
> - // SAFETY: `raw` owns the initial reference and `DeviceData`
> is ready.
> - Ok(unsafe {
> ARef::from_raw(NonNull::new(raw.cast::<Self>()).ok_or(ENOMEM)?) })
> - }
> + unsafe {
> + core::ptr::addr_of_mut!((*this).registration_data)
> + .write(UnsafeCell::new(NonNull::dangling()));
> + };
>
> - /// Returns a reference to the embedded driver data.
> - pub fn data(&self) -> &T::DeviceData {
> - &self.data
> + // SAFETY: `raw` owns the initial reference.
> + Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(this)) })
> }
>
> fn as_raw(&self) -> *mut bindings::fwctl_device {
> self.dev.get()
> }
>
> - /// # Safety
> - ///
> - /// `raw` must point to an offset-0 `fwctl_device` embedded in
> `Device<T>`.
> - /// fwctl calls this exactly once from the device release path.
> - unsafe extern "C" fn release_data_callback(raw: *mut
> bindings::fwctl_device) {
> - let this = raw.cast::<Self>();
> -
> - // SAFETY: fwctl invokes this callback once during the final
> device
> - // release, before freeing the allocation.
> - unsafe {
> core::ptr::drop_in_place(core::ptr::addr_of_mut!((*this).data)) };
> - }
> -
> /// # Safety
> ///
> /// `ptr` must point to a valid `fwctl_device` embedded in a
> `Device<T>`. @@ -213,18 +210,17 @@ unsafe fn from_raw<'a>(ptr: *mut
> bindings::fwctl_device) -> &'a Self { unsafe { &*ptr.cast() }
> }
>
> - /// Returns the parent device.
> + /// Returns a reference to the registration data.
> + ///
> + /// # Safety
> ///
> - /// The parent is guaranteed to be bound while any fwctl
> callback is
> - /// running (ensured by the `registration_lock` read lock on the
> ioctl
> - /// path and by `Devres` on the teardown path).
> - pub fn parent(&self) -> &device::Device<device::Bound> {
> - // SAFETY: fwctl sets the parent during allocation.
> - let parent_dev = unsafe { (*self.as_raw()).dev.parent };
> - // SAFETY: The parent stays live while fwctl ops run.
> - let dev: &device::Device = unsafe {
> device::Device::from_raw(parent_dev) };
> - // SAFETY: Devres teardown keeps the parent bound here.
> - unsafe { dev.as_bound() }
> + /// The caller must ensure the device is registered, i.e. that
> this is called within a fwctl
> + /// callback protected by `registration_lock`. The pointer cast
> from `Of<'static>` to `Of<'_>`
> + /// is sound because [`ForLt`] guarantees covariance.
> + unsafe fn registration_data_unchecked(&self) ->
> &<T::RegistrationData as ForLt>::Of<'_> {
> + // SAFETY: Caller guarantees the device is registered, so
> the pointer is valid.
> + // Lifetimes do not affect layout, so the cast is sound.
> + unsafe {
> (*self.registration_data.get()).cast::<_>().as_ref() } }
> }
>
> @@ -251,62 +247,98 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
> }
> }
>
> -// SAFETY: `Device<T>` is refcounted by the fwctl core and may be
> released from -// any thread. The embedded driver data is `Send`.
> +// SAFETY: `Device<T>` is refcounted by the fwctl core and may be
> released from any thread. unsafe impl<T: Operations> Send for
> Device<T> {}
> -// SAFETY: Shared access to the embedded `fwctl_device` is protected
> by the -// fwctl core, and the embedded driver data is `Sync`.
> +// SAFETY: Shared access to the embedded `fwctl_device` is protected
> by the fwctl core. The +// `registration_data` field is only mutated
> before registration and after unregistration (both +//
> single-threaded with respect to callbacks). unsafe impl<T:
> Operations> Sync for Device<T> {}
> /// A registered fwctl device.
> ///
> -/// Must live inside a [`Devres`] to guarantee that
> [`fwctl_unregister`] runs -/// before the parent driver unbinds.
> `Devres` prevents the `Registration` -/// from being moved to a
> context that could outlive the parent device. +/// Carries the
> lifetime `'a` of the parent device to ensure that
> [`fwctl_unregister`] runs (via +/// [`Drop`]) before the parent
> driver unbinds. Owns the +///
> [`RegistrationData`](Operations::RegistrationData) which is
> accessible during callbacks via the +/// pointer stored in
> [`Device`]. /// -/// On drop the device is unregistered (all user
> contexts are closed and -/// `ops` is set to `NULL`) and the [`ARef`]
> is released. +/// On drop the device is unregistered (all user
> contexts are closed and `ops` is set to `NULL`) +/// and the
> registration data is dropped. /// /// [`fwctl_unregister`]:
> srctree/drivers/fwctl/main.c -pub struct Registration<T: Operations> {
> +pub struct Registration<'a, T: Operations> {
> dev: ARef<Device<T>>,
> + _reg_data: Pin<KBox<<T::RegistrationData as ForLt>::Of<'a>>>,
> }
>
> -impl<T: Operations> Registration<T> {
> - /// Register a previously allocated fwctl device.
> - pub fn new<'a>(
> - parent: &'a device::Device<device::Bound>,
> - dev: &'a Device<T>,
> - ) -> impl PinInit<Devres<Self>, Error> + 'a {
> - pin_init::pin_init_scope(move || {
> - // SAFETY: `dev` is a valid fwctl_device backed by an
> ARef.
> - let ret = unsafe {
> bindings::fwctl_register(dev.as_raw()) };
> - if ret != 0 {
> - return Err(Error::from_errno(ret));
> - }
> +impl<'a, T: Operations> Registration<'a, T>
> +where
> + for<'b> <T::RegistrationData as ForLt>::Of<'b>: Send + Sync,
> +{
> + /// Register a previously allocated fwctl device with the given
> registration data.
> + ///
> + /// The `reg_data` is owned by the registration and accessible
> during callbacks via
> + /// `Device::registration_data_unchecked()`.
> + ///
> + /// # Safety
> + ///
> + /// Callers must not `mem::forget()` the returned
> [`Registration`] or otherwise prevent its
> + /// [`Drop`] implementation from running, since
> `fwctl_unregister` must be called before the
> + /// parent device is unbound.
> + pub unsafe fn new(
> + _parent: &'a device::Device<device::Bound>,
> + dev: &Device<T>,
> + reg_data: impl PinInit<<T::RegistrationData as
> ForLt>::Of<'a>, Error>,
> + ) -> Result<Self> {
> + let reg_data: Pin<KBox<<T::RegistrationData as
> ForLt>::Of<'a>>> =
> + KBox::pin_init(reg_data, GFP_KERNEL)?;
> +
> + // Store the registration data pointer in the device before
> registration, so that it is
> + // visible once callbacks can be invoked.
> + //
> + // SAFETY: Lifetimes do not affect layout, so the pointer
> cast is sound.
> + let ptr: NonNull<<T::RegistrationData as
> ForLt>::Of<'static>> =
> + unsafe {
> mem::transmute(NonNull::from(Pin::get_ref(reg_data.as_ref()))) }; +
> + // SAFETY: No concurrent access; the device is not yet
> registered.
> + unsafe { *dev.registration_data.get() = ptr };
> +
> + // SAFETY: `dev` is a valid fwctl_device backed by an ARef.
> + let ret = unsafe { bindings::fwctl_register(dev.as_raw()) };
> + if ret != 0 {
> + // SAFETY: No concurrent readers; registration failed.
> + unsafe { *dev.registration_data.get() =
> NonNull::dangling() };
> + return Err(Error::from_errno(ret));
> + }
>
> - Ok(Devres::new(parent, Self { dev: dev.into() }))
> + Ok(Self {
> + dev: dev.into(),
> + _reg_data: reg_data,
> })
> }
> }
>
> -impl<T: Operations> Drop for Registration<T> {
> +impl<T: Operations> Drop for Registration<'_, T> {
> fn drop(&mut self) {
> - // SAFETY: `Registration` lives inside a `Devres`, which
> guarantees
> - // that drop runs while the parent device is still bound.
> + // SAFETY: The Registration lifetime guarantees that the
> parent device is still bound.
> + // `fwctl_unregister` takes the write lock, closes all user
> contexts, and sets ops=NULL.
> + // After it returns, no callbacks can be running or will run.
> unsafe { bindings::fwctl_unregister(self.dev.as_raw()) };
> - // ARef<Device<T>> is dropped after this, calling fwctl_put.
> +
> + // SAFETY: `fwctl_unregister` guarantees no concurrent
> readers.
> + unsafe { *self.dev.registration_data.get() =
> NonNull::dangling() }; +
> + // `self._reg_data` is dropped here — safe because no
> concurrent readers remain. }
> }
>
> -// SAFETY: `Registration` can be sent between threads; the underlying
> -// fwctl_device uses internal locking.
> -unsafe impl<T: Operations> Send for Registration<T> {}
> +// SAFETY: `Registration` can be sent between threads; the
> underlying fwctl_device uses internal +// locking.
> +unsafe impl<T: Operations> Send for Registration<'_, T> {}
>
> -// SAFETY: `Registration` provides no mutable access; the underlying
> -// fwctl_device is protected by internal locking.
> -unsafe impl<T: Operations> Sync for Registration<T> {}
> +// SAFETY: `Registration` provides no mutable access; the underlying
> fwctl_device is protected by +// internal locking.
> +unsafe impl<T: Operations> Sync for Registration<'_, T> {}
>
> /// Internal per-FD user context wrapping `struct fwctl_uctx` and
> `T`. ///
> @@ -376,7 +408,10 @@ impl<T: Operations> VTable<T> {
> // SAFETY: Rust fwctl devices use the offset-0 `Device<T>`
> layout. let device = unsafe { Device::<T>::from_raw(raw_fwctl) };
>
> - let initializer = T::open(device);
> + // SAFETY: open_uctx is called under registration_lock read;
> the device is registered.
> + let reg_data = unsafe { device.registration_data_unchecked()
> }; +
> + let initializer = T::open(device, reg_data);
>
> let uctx_offset = core::mem::offset_of!(UserCtx<T>, uctx);
> // SAFETY: `uctx_size` reserves space for the full
> `UserCtx<T>`. @@ -396,13 +431,17 @@ impl<T: Operations> VTable<T> {
> // SAFETY: fwctl keeps the owning device live for this
> callback. let device = unsafe { Device::<T>::from_raw((*uctx).fwctl)
> };
> + // SAFETY: close_uctx is called under registration_lock
> write (from fwctl_unregister) or
> + // under registration_lock read (from fwctl_fops_release);
> the device is registered.
> + let reg_data = unsafe { device.registration_data_unchecked()
> }; +
> // SAFETY: close is called for an opened Rust user context.
> let ctx = unsafe { UserCtx::<T>::from_raw_mut(uctx) };
>
> {
> // SAFETY: fwctl never moves an opened user context.
> let pinned = unsafe { Pin::new_unchecked(&mut ctx.uctx)
> };
> - T::close(pinned, device);
> + T::close(pinned, device, reg_data);
> }
>
> // SAFETY: close is the last callback before fwctl frees the
> allocation. @@ -421,10 +460,13 @@ impl<T: Operations> VTable<T> {
> let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };
> let device = ctx.device();
>
> + // SAFETY: info is called under registration_lock read; the
> device is registered.
> + let reg_data = unsafe { device.registration_data_unchecked()
> }; +
> // SAFETY: fwctl never moves an opened user context.
> let pinned = unsafe { Pin::new_unchecked(&ctx.uctx) };
>
> - match T::info(pinned, device) {
> + match T::info(pinned, device, reg_data) {
> Ok(kvec) if kvec.is_empty() => {
> // SAFETY: `length` is a valid out-parameter.
> unsafe { *length = 0 };
> @@ -461,6 +503,9 @@ impl<T: Operations> VTable<T> {
> let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };
> let device = ctx.device();
>
> + // SAFETY: fw_rpc is called under registration_lock read;
> the device is registered.
> + let reg_data = unsafe { device.registration_data_unchecked()
> }; +
> // SAFETY: fwctl passes a valid in/out buffer for this
> callback. let rpc_in_slice: &mut [u8] =
> unsafe { slice::from_raw_parts_mut(rpc_in.cast::<u8>(),
> in_len) }; @@ -468,7 +513,7 @@ impl<T: Operations> VTable<T> {
> // SAFETY: fwctl never moves an opened user context.
> let pinned = unsafe { Pin::new_unchecked(&ctx.uctx) };
>
> - match T::fw_rpc(pinned, device, scope, rpc_in_slice) {
> + match T::fw_rpc(pinned, device, reg_data, scope,
> rpc_in_slice) { Ok(FwRpcResponse::InPlace(len)) => {
> // SAFETY: `out_len` is valid.
> unsafe { *out_len = len };
>
> [5] nova-core diff:
>
> diff --git a/drivers/gpu/nova-core/driver.rs
> b/drivers/gpu/nova-core/driver.rs index 5738d4ac521b..34d595b655fc
> 100644 --- a/drivers/gpu/nova-core/driver.rs
> +++ b/drivers/gpu/nova-core/driver.rs
> @@ -3,6 +3,7 @@
> use kernel::{
> auxiliary,
> device::Core,
> + fwctl,
> pci,
> pci::{
> Class,
> @@ -15,7 +16,7 @@
> Atomic,
> Relaxed, //
> },
> - types::ForLt,
> + types::ForLt, //
> };
>
> use crate::gpu::Gpu;
> @@ -23,6 +24,34 @@
> /// Counter for generating unique auxiliary device IDs.
> static AUXILIARY_ID_COUNTER: Atomic<u32> = Atomic::new(0);
>
> +struct FwctlRegData<'a> {
> + _bar: Bar0<'a>,
> +}
> +
> +struct FwctlUctx;
> +
> +impl fwctl::Operations for FwctlUctx {
> + type RegistrationData = ForLt!(FwctlRegData<'_>);
> + const DEVICE_TYPE: fwctl::DeviceType = fwctl::DeviceType::Mlx5;
> +
> + fn open(
> + _device: &fwctl::Device<Self>,
> + _reg_data: &FwctlRegData<'_>,
> + ) -> impl PinInit<Self, Error> {
> + Ok(FwctlUctx)
> + }
> +
> + fn fw_rpc(
> + _this: core::pin::Pin<&Self>,
> + _device: &fwctl::Device<Self>,
> + _reg_data: &FwctlRegData<'_>,
> + _scope: fwctl::RpcScope,
> + _rpc_in: &mut [u8],
> + ) -> Result<fwctl::FwRpcResponse, Error> {
> + Err(ENOTSUPP)
> + }
> +}
> +
> #[pin_data]
> pub(crate) struct NovaCore<'bound> {
> #[pin]
> @@ -30,6 +59,7 @@ pub(crate) struct NovaCore<'bound> {
> bar: pci::Bar<'bound, BAR0_SIZE>,
> #[allow(clippy::type_complexity)]
> _reg: auxiliary::Registration<'bound, ForLt!(())>,
> + _fwctl_reg: fwctl::Registration<'bound, FwctlUctx>,
> }
>
> pub(crate) struct NovaCoreDriver;
> @@ -78,6 +108,8 @@ fn probe<'bound>(
> pdev.enable_device_mem()?;
> pdev.set_master();
>
> + let fwctl_dev =
> fwctl::Device::<FwctlUctx>::new(pdev.as_ref())?; +
> Ok(try_pin_init!(NovaCore {
> bar: pdev.iomap_region_sized::<BAR0_SIZE>(0,
> c"nova-core/bar0")?, // TODO: Use `&bar` self-referential pin-init
> syntax once available. @@ -95,6 +127,17 @@ fn probe<'bound>(
> crate::MODULE_NAME,
> (),
> )?,
> + // SAFETY: `_fwctl_reg` is dropped before `bar`
> (struct field drop order), and its
> + // drop calls `fwctl_unregister` before the parent
> device is unbound.
> + _fwctl_reg: unsafe {
> + fwctl::Registration::new(
> + pdev.as_ref(),
> + &fwctl_dev,
> + Ok(FwctlRegData {
> + _bar: &*core::ptr::from_ref(bar),
> + }),
> + )?
> + },
> }))
> })
> }
> diff --git a/drivers/gpu/nova-core/nova_core.rs
> b/drivers/gpu/nova-core/nova_core.rs index 735b8e17c6b6..d5f050b2b55e
> 100644 --- a/drivers/gpu/nova-core/nova_core.rs
> +++ b/drivers/gpu/nova-core/nova_core.rs
> @@ -74,6 +74,7 @@ fn init(module: &'static kernel::ThisModule) ->
> impl PinInit<Self, Error> { description: "Nova Core GPU driver",
> license: "GPL v2",
> firmware: [],
> + imports_ns: ["FWCTL"],
> }
>
> kernel::module_firmware!(firmware::ModInfoBuilder);
^ permalink raw reply
* Re: [PATCH v2] rust_binder: reject context manager self-transaction
From: Alice Ryhl @ 2026-06-25 5:52 UTC (permalink / raw)
To: Carlos Llamas
Cc: Keshav Verma, Greg Kroah-Hartman, Arve Hjønnevåg,
Todd Kjos, Christian Brauner, Miguel Ojeda, Boqun Feng, Gary Guo,
linux-kernel, rust-for-linux, stable
In-Reply-To: <ajx4bYtQRvBZp4M0@google.com>
On Thu, Jun 25, 2026 at 2:38 AM Carlos Llamas <cmllamas@google.com> wrote:
>
> On Tue, Jun 23, 2026 at 11:37:11AM +0000, Alice Ryhl wrote:
> > On Mon, Jun 22, 2026 at 08:28:01PM +0530, Keshav Verma wrote:
> > > Rust binder resolved handle 0 to the context manager node, but it does not
> > > reject the case where the caller owns the same node.
> > >
> > > The C binder driver rejects transactions from the context-manager process
> > > to handle 0 after resolving the target node. Match that behavior in Rust
> > > Binder by rejecting handle 0 transactions when the resolved context-manager
> > > node is owned by the calling process.
> > >
> > > This applies to both synchronous and oneway transactions because both paths
> > > resolve the target through Process::get_transaction_node().
> > >
> > > Cc: stable@kernel.org
> > > Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver")
> > > Signed-off-by: Keshav Verma <iganschel@gmail.com>
> > > ---
> > > Changes in v2:
> > > - Compare the underlying OS process task instead of Rust Binder `Process` object.
> >
> > I would prefer to compare the Binder Process object. Rejecting
> > transactions between different fds owned by the same process doesn't
> > really have any benefit and makes fuzz testing much harder.
> >
> > Alice
>
> Hey Alice,
>
> The restrictions were added in the C version in order to patch
> vulnerabilities associated with this "self-transaction" behavior.
> See: http://git.kernel.org/torvalds/c/4b836a1426cb
>
> I haven't really looked much into this, but do we even need this for the
> Rust version? Is this even fixing anything at all?
Even if there's no vulnerability, self-transactions are still very
weird and may introduce edge cases I can't think of. I would prefer to
reject them.
Alice
^ permalink raw reply
* Re: [PATCH v2] rust_binder: reject context manager self-transaction
From: Carlos Llamas @ 2026-06-25 0:38 UTC (permalink / raw)
To: Alice Ryhl
Cc: Keshav Verma, Greg Kroah-Hartman, Arve Hjønnevåg,
Todd Kjos, Christian Brauner, Miguel Ojeda, Boqun Feng, Gary Guo,
linux-kernel, rust-for-linux, stable
In-Reply-To: <ajpv5xkakp06ArMj@google.com>
On Tue, Jun 23, 2026 at 11:37:11AM +0000, Alice Ryhl wrote:
> On Mon, Jun 22, 2026 at 08:28:01PM +0530, Keshav Verma wrote:
> > Rust binder resolved handle 0 to the context manager node, but it does not
> > reject the case where the caller owns the same node.
> >
> > The C binder driver rejects transactions from the context-manager process
> > to handle 0 after resolving the target node. Match that behavior in Rust
> > Binder by rejecting handle 0 transactions when the resolved context-manager
> > node is owned by the calling process.
> >
> > This applies to both synchronous and oneway transactions because both paths
> > resolve the target through Process::get_transaction_node().
> >
> > Cc: stable@kernel.org
> > Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver")
> > Signed-off-by: Keshav Verma <iganschel@gmail.com>
> > ---
> > Changes in v2:
> > - Compare the underlying OS process task instead of Rust Binder `Process` object.
>
> I would prefer to compare the Binder Process object. Rejecting
> transactions between different fds owned by the same process doesn't
> really have any benefit and makes fuzz testing much harder.
>
> Alice
Hey Alice,
The restrictions were added in the C version in order to patch
vulnerabilities associated with this "self-transaction" behavior.
See: http://git.kernel.org/torvalds/c/4b836a1426cb
I haven't really looked much into this, but do we even need this for the
Rust version? Is this even fixing anything at all?
--
Carlos Llamas
^ permalink raw reply
* Re: [GIT PULL] Rust for v7.2 (addendum)
From: pr-tracker-bot @ 2026-06-24 22:16 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Linus Torvalds, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, rust-for-linux, linux-kernel
In-Reply-To: <20260624144713.585327-1-ojeda@kernel.org>
The pull request you sent on Wed, 24 Jun 2026 16:47:13 +0200:
> https://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux.git tags/rust-7.2-2
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/ab9de95c9cf952332ab79453b4b5d1bfca8e514f
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [GIT PULL] Rust fixes for 7.2
From: pr-tracker-bot @ 2026-06-24 22:16 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Linus Torvalds, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, rust-for-linux, linux-kernel
In-Reply-To: <20260624144630.585128-1-ojeda@kernel.org>
The pull request you sent on Wed, 24 Jun 2026 16:46:30 +0200:
> https://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux.git tags/rust-fixes-7.2
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/dcebfd2c1404d1c931e86ec5168cdc006d503909
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH v17 06/10] rust: rename `AlwaysRefCounted` to `RefCounted`.
From: Andreas Hindborg @ 2026-06-24 19:17 UTC (permalink / raw)
To: Onur Özkan
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
Dave Ertman, Ira Weiny, Leon Romanovsky, Paul Moore, Serge Hallyn,
Rafael J. Wysocki, David Airlie, Simona Vetter, Alexander Viro,
Christian Brauner, Jan Kara, Daniel Almeida, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Boqun Feng, Uladzislau Rezki,
Lorenzo Stoakes, Vlastimil Babka, Liam R. Howlett, Igor Korotin,
Pavel Tikhomirov, linux-kernel, rust-for-linux, linux-block,
linux-security-module, dri-devel, linux-fsdevel, linux-mm,
linux-pm, linux-pci, driver-core, Oliver Mangold, Viresh Kumar
In-Reply-To: <20260623175814.87191-1-work@onurozkan.dev>
Onur Özkan <work@onurozkan.dev> writes:
> On Thu, 04 Jun 2026 22:11:18 +0200
> Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
>> From: Oliver Mangold <oliver.mangold@pm.me>
>>
>> There are types where it may both be reference counted in some cases and
>> owned in others. In such cases, obtaining `ARef<T>` from `&T` would be
>> unsound as it allows creation of `ARef<T>` copy from `&Owned<T>`.
>>
>> Therefore, we split `AlwaysRefCounted` into `RefCounted` (which `ARef<T>`
>> would require) and a marker trait to indicate that the type is always
>> reference counted (and not `Ownable`) so the `&T` -> `ARef<T>` conversion
>> is possible.
>>
>> - Rename `AlwaysRefCounted` to `RefCounted`.
>> - Add a new unsafe trait `AlwaysRefCounted`.
>> - Implement the new trait `AlwaysRefCounted` for the newly renamed
>> `RefCounted` implementations. This leaves functionality of existing
>> implementers of `AlwaysRefCounted` intact.
>>
>> Suggested-by: Alice Ryhl <aliceryhl@google.com>
>> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
>> Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
>> [ Andreas: Updated commit message and rebase on rust-6.20-7.0 ]
>> Acked-by: Igor Korotin <igor.korotin.linux@gmail.com>
>> Acked-by: Danilo Krummrich <dakr@kernel.org>
>> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
>> Reviewed-by: Gary Guo <gary@garyguo.net>
>> Co-developed-by: Andreas Hindborg <a.hindborg@kernel.org>
>> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
>> ---
>> rust/kernel/auxiliary.rs | 7 +++++-
>> rust/kernel/block/mq/request.rs | 15 ++++++++-----
>> rust/kernel/cred.rs | 13 +++++++++--
>> rust/kernel/device.rs | 12 ++++++++--
>> rust/kernel/device/property.rs | 11 +++++++--
>> rust/kernel/drm/device.rs | 9 ++++++--
>> rust/kernel/drm/gem/mod.rs | 16 ++++++++++----
>> rust/kernel/fs/file.rs | 16 ++++++++++----
>> rust/kernel/i2c.rs | 13 ++++++++---
>> rust/kernel/mm.rs | 15 +++++++++----
>> rust/kernel/mm/mmput_async.rs | 9 ++++++--
>> rust/kernel/opp.rs | 10 ++++++---
>> rust/kernel/owned.rs | 2 +-
>> rust/kernel/pci.rs | 10 ++++++++-
>> rust/kernel/pid_namespace.rs | 12 ++++++++--
>> rust/kernel/platform.rs | 7 +++++-
>> rust/kernel/sync/aref.rs | 49 ++++++++++++++++++++++++++---------------
>> rust/kernel/task.rs | 13 +++++++++--
>> rust/kernel/types.rs | 3 ++-
>> rust/kernel/usb.rs | 17 +++++++++++---
>> 20 files changed, 195 insertions(+), 64 deletions(-)
>>
>> diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
>> index 93c0db1f6655..49f07740f657 100644
>> --- a/rust/kernel/auxiliary.rs
>> +++ b/rust/kernel/auxiliary.rs
>> @@ -19,6 +19,7 @@
>> to_result, //
>> },
>> prelude::*,
>> + sync::aref::{AlwaysRefCounted, RefCounted},
>
> This patch has multiple horizontal use statements around.
Thanks, I'll take another pass to fix that.
Best regards,
Andreas Hindborg
^ permalink raw reply
* Re: [PATCH v17 08/10] rust: aref: update formatting of use statements
From: Andreas Hindborg @ 2026-06-24 19:16 UTC (permalink / raw)
To: Onur Özkan
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
Dave Ertman, Ira Weiny, Leon Romanovsky, Paul Moore, Serge Hallyn,
Rafael J. Wysocki, David Airlie, Simona Vetter, Alexander Viro,
Christian Brauner, Jan Kara, Daniel Almeida, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Boqun Feng, Uladzislau Rezki,
Lorenzo Stoakes, Vlastimil Babka, Liam R. Howlett, Igor Korotin,
Pavel Tikhomirov, linux-kernel, rust-for-linux, linux-block,
linux-security-module, dri-devel, linux-fsdevel, linux-mm,
linux-pm, linux-pci, driver-core
In-Reply-To: <20260623175531.85421-1-work@onurozkan.dev>
Onur Özkan <work@onurozkan.dev> writes:
> On Thu, 04 Jun 2026 22:11:20 +0200
> Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
>> Update formatting if use statements in preparation for next commit.
>
> I guess you meant "formatting use statements"? Also, why not doing this in
> the next commit directly?
Because it is an unrelated change.
Best regards,
Andreas Hindborg
^ permalink raw reply
* Re: [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt
From: Danilo Krummrich @ 2026-06-24 18:06 UTC (permalink / raw)
To: Gary Guo
Cc: gregkh, rafael, ojeda, boqun, bjorn3_gh, lossin, a.hindborg,
aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski, driver-core, linux-kernel, nova-gpu, dri-devel,
linux-pwm, rust-for-linux
In-Reply-To: <DJHDQ7X94BXR.2XYSWSK79USDB@garyguo.net>
On Wed Jun 24, 2026 at 5:20 PM CEST, Gary Guo wrote:
> This is still unsound, as I mentioned in the last version that the prover must
> be kept.
Heh, I think I just forgot to finish this up, good catch!
^ permalink raw reply
* Re: [PATCH v4 2/2] rust: introduce abstractions for fwctl
From: Danilo Krummrich @ 2026-06-24 17:41 UTC (permalink / raw)
To: Zhi Wang
Cc: rust-for-linux, linux-kernel, jgg, gary, joelagnelf, aliceryhl,
kwilczynski, ojeda, alex.gaynor, boqun.feng, bjorn3_gh, lossin,
a.hindborg, tmgross, cjia, smitra, ankita, aniketa, kwankhede,
targupta, kjaju, alkumar, acourbot, jhubbard, zhiwang,
daniel.almeida
In-Reply-To: <20260624091758.1678092-3-zhiw@nvidia.com>
On Wed Jun 24, 2026 at 11:17 AM CEST, Zhi Wang wrote:
> +impl<T: Operations> Device<T> {
[...]
> + /// Returns the parent device.
> + ///
> + /// The parent is guaranteed to be bound while any fwctl callback is
> + /// running (ensured by the `registration_lock` read lock on the ioctl
> + /// path and by `Devres` on the teardown path).
> + pub fn parent(&self) -> &device::Device<device::Bound> {
> + // SAFETY: fwctl sets the parent during allocation.
> + let parent_dev = unsafe { (*self.as_raw()).dev.parent };
> + // SAFETY: The parent stays live while fwctl ops run.
> + let dev: &device::Device = unsafe { device::Device::from_raw(parent_dev) };
> + // SAFETY: Devres teardown keeps the parent bound here.
> + unsafe { dev.as_bound() }
> + }
You can't return a Device<Bound> from fwctl::Device; it is reference counted and
can outlive driver unbind. But I think this method isn't needed anyways, so I'd
just drop it.
> +impl<T: Operations> Registration<T> {
> + /// Register a previously allocated fwctl device.
> + pub fn new<'a>(
> + parent: &'a device::Device<device::Bound>,
> + dev: &'a Device<T>,
> + ) -> impl PinInit<Devres<Self>, Error> + 'a {
> + pin_init::pin_init_scope(move || {
> + // SAFETY: `dev` is a valid fwctl_device backed by an ARef.
> + let ret = unsafe { bindings::fwctl_register(dev.as_raw()) };
> + if ret != 0 {
> + return Err(Error::from_errno(ret));
> + }
> +
> + Ok(Devres::new(parent, Self { dev: dev.into() }))
> + })
I have recently been working on getting rid of Devres for Registration types and
device resources in favor of Rust-native lifetimes using higher-ranked types
(HRT). This has a couple of advantages, e.g. it simplifies accessing device
resources from destructors and (with pin-init self-referential support) allows
us to share (Rust) references between private data structures.
This has been merged for existing device resources, bus device private data and
auxiliary registration data [1]. There's also a follow-up series to support
invariance [2] and another one to enable it for the DRM subsystem [3].
Class devices infrastructure should follow that same pattern, i.e. the
fwctl::Registration type should gain a lifetime and the fwctl private data
provided via fwctl callbacks should be tied to the lifetime of the Registration,
i.e. the lifetime the underlying bus device is bound to the driver.
Please find a diff in [4] implementing this for fwctl and a diff in [5]
demonstrating how this is used in nova-core. (Feel free to pick up the provided
code and use it in any way.)
(Please ignore that I use fwctl::DeviceType::Mlx5 in the nova-core diff, it is
just there to make the code compile, so I could do a smoke test.)
Thanks,
Danilo
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2c7c65933600e8db2ec1a78dec5008de876dd3ad
[2] https://lore.kernel.org/driver-core/20260618230834.812007-1-dakr@kernel.org/
[3] https://lore.kernel.org/driver-core/20260620184924.2247517-1-dakr@kernel.org/
[4] FWCTL diff:
diff --git a/rust/kernel/fwctl.rs b/rust/kernel/fwctl.rs
index f5f802f5299c..65abea866b22 100644
--- a/rust/kernel/fwctl.rs
+++ b/rust/kernel/fwctl.rs
@@ -8,16 +8,20 @@
bindings,
container_of,
device,
- devres::Devres,
prelude::*,
sync::aref::{
ARef,
AlwaysRefCounted, //
},
- types::Opaque, //
+ types::{
+ ForLt,
+ Opaque, //
+ }, //
};
use core::{
+ cell::UnsafeCell,
marker::PhantomData,
+ mem,
ptr::NonNull,
slice, //
};
@@ -89,8 +93,12 @@ pub enum FwRpcResponse {
/// vtable used by the core `fwctl` layer to manage per-FD user contexts and
/// handle RPC requests.
pub trait Operations: Sized + Send + Sync {
- /// Driver data embedded alongside the `fwctl_device` allocation.
- type DeviceData: Send + Sync;
+ /// Data owned by the [`Registration`] and accessible during callbacks.
+ ///
+ /// This is a [`ForLt`](trait@ForLt) type whose lifetime is tied to the parent bus device
+ /// binding scope. Drivers use this to store references to resources bound to this scope, such as PCI
+ /// BARs or typed bus device references.
+ type RegistrationData: ForLt;
/// fwctl device type identifier.
const DEVICE_TYPE: DeviceType;
@@ -99,57 +107,68 @@ pub trait Operations: Sized + Send + Sync {
///
/// Returns a [`PinInit`] initializer for `Self`. The instance is dropped
/// automatically when the FD is closed (after [`close`](Self::close)).
- fn open(device: &Device<Self>) -> impl PinInit<Self, Error>;
+ fn open<'a>(
+ device: &'a Device<Self>,
+ reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
+ ) -> impl PinInit<Self, Error>;
/// Called when the user context is closed.
///
/// The driver may perform additional cleanup here that requires access
/// to the owning [`Device`]. `Self` is dropped automatically after this
/// returns.
- fn close(_this: Pin<&mut Self>, _device: &Device<Self>) {}
+ fn close<'a>(
+ _this: Pin<&mut Self>,
+ _device: &'a Device<Self>,
+ _reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
+ ) {
+ }
/// Return device information to userspace.
///
/// The default implementation returns no device-specific data.
- fn info(_this: Pin<&Self>, _device: &Device<Self>) -> Result<KVec<u8>, Error> {
+ fn info<'a>(
+ _this: Pin<&Self>,
+ _device: &'a Device<Self>,
+ _reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
+ ) -> Result<KVec<u8>, Error> {
Ok(KVec::new())
}
/// Handle a userspace RPC request.
- fn fw_rpc(
+ fn fw_rpc<'a>(
this: Pin<&Self>,
- device: &Device<Self>,
+ device: &'a Device<Self>,
+ reg_data: &'a <Self::RegistrationData as ForLt>::Of<'a>,
scope: RpcScope,
rpc_in: &mut [u8],
) -> Result<FwRpcResponse, Error>;
}
-/// A fwctl device with embedded driver data.
+/// A fwctl device.
///
-/// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C
-/// `fwctl_alloc_device()` layout convention.
+/// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C `fwctl_alloc_device()` layout
+/// convention. Contains a pointer to the [`Registration`]'s data, set at registration time and
+/// cleared on unregistration.
///
/// # Invariants
///
/// - `dev` is embedded at offset 0 and is initialised by fwctl.
/// - The fwctl refcount owns the allocation lifetime.
-/// - `data` is dropped from the fwctl core release hook before `kfree()`.
+/// - `registration_data` is either `NonNull::dangling()` (before registration / after
+/// unregistration) or points to a valid `RegistrationData` owned by the [`Registration`].
#[repr(C)]
pub struct Device<T: Operations> {
dev: Opaque<bindings::fwctl_device>,
- data: T::DeviceData,
+ registration_data: UnsafeCell<NonNull<<T::RegistrationData as ForLt>::Of<'static>>>,
}
impl<T: Operations> Device<T> {
- /// Allocate a new fwctl device with embedded driver data.
+ /// Allocate a new fwctl device.
///
/// Returns an [`ARef`] that can be passed to [`Registration::new()`]
- /// to make the device visible to userspace. The caller may inspect or
- /// configure the device between allocation and registration.
- pub fn new(
- parent: &device::Device<device::Bound>,
- data: impl PinInit<T::DeviceData, Error>,
- ) -> Result<ARef<Self>> {
+ /// to make the device visible to userspace.
+ pub fn new(parent: &device::Device<device::Bound>) -> Result<ARef<Self>> {
const_assert!(
core::mem::offset_of!(Self, dev) == 0,
"struct fwctl_device must be at offset 0"
@@ -157,8 +176,7 @@ pub fn new(
let ops = core::ptr::from_ref::<bindings::fwctl_ops>(&VTable::<T>::VTABLE).cast_mut();
- // SAFETY: `ops` is static, `parent` is bound, and `size` includes the
- // offset-0 `fwctl_device` plus `DeviceData`.
+ // SAFETY: `ops` is static, `parent` is bound, and `size` covers the full `Device<T>`.
let raw = unsafe {
bindings::_fwctl_alloc_device(parent.as_raw(), ops, core::mem::size_of::<Self>())
};
@@ -169,42 +187,21 @@ pub fn new(
let this = raw.cast::<Self>();
+ // INVARIANT: Set `registration_data` to dangling (no registration yet).
// SAFETY: `this` points to the allocation just returned by fwctl.
- let data_ptr = unsafe { core::ptr::addr_of_mut!((*this).data) };
- // SAFETY: `data_ptr` addresses the uninitialised tail data.
- unsafe { data.__pinned_init(data_ptr) }.inspect_err(|_| {
- // SAFETY: `raw` still owns the initial reference.
- unsafe { bindings::fwctl_put(raw) };
- })?;
-
- // SAFETY: `raw` is a live fwctl_device allocated above.
- unsafe { (*raw).release_data = Some(Self::release_data_callback) };
-
- // SAFETY: `raw` owns the initial reference and `DeviceData` is ready.
- Ok(unsafe { ARef::from_raw(NonNull::new(raw.cast::<Self>()).ok_or(ENOMEM)?) })
- }
+ unsafe {
+ core::ptr::addr_of_mut!((*this).registration_data)
+ .write(UnsafeCell::new(NonNull::dangling()));
+ };
- /// Returns a reference to the embedded driver data.
- pub fn data(&self) -> &T::DeviceData {
- &self.data
+ // SAFETY: `raw` owns the initial reference.
+ Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(this)) })
}
fn as_raw(&self) -> *mut bindings::fwctl_device {
self.dev.get()
}
- /// # Safety
- ///
- /// `raw` must point to an offset-0 `fwctl_device` embedded in `Device<T>`.
- /// fwctl calls this exactly once from the device release path.
- unsafe extern "C" fn release_data_callback(raw: *mut bindings::fwctl_device) {
- let this = raw.cast::<Self>();
-
- // SAFETY: fwctl invokes this callback once during the final device
- // release, before freeing the allocation.
- unsafe { core::ptr::drop_in_place(core::ptr::addr_of_mut!((*this).data)) };
- }
-
/// # Safety
///
/// `ptr` must point to a valid `fwctl_device` embedded in a `Device<T>`.
@@ -213,18 +210,17 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_device) -> &'a Self {
unsafe { &*ptr.cast() }
}
- /// Returns the parent device.
+ /// Returns a reference to the registration data.
+ ///
+ /// # Safety
///
- /// The parent is guaranteed to be bound while any fwctl callback is
- /// running (ensured by the `registration_lock` read lock on the ioctl
- /// path and by `Devres` on the teardown path).
- pub fn parent(&self) -> &device::Device<device::Bound> {
- // SAFETY: fwctl sets the parent during allocation.
- let parent_dev = unsafe { (*self.as_raw()).dev.parent };
- // SAFETY: The parent stays live while fwctl ops run.
- let dev: &device::Device = unsafe { device::Device::from_raw(parent_dev) };
- // SAFETY: Devres teardown keeps the parent bound here.
- unsafe { dev.as_bound() }
+ /// The caller must ensure the device is registered, i.e. that this is called within a fwctl
+ /// callback protected by `registration_lock`. The pointer cast from `Of<'static>` to `Of<'_>`
+ /// is sound because [`ForLt`] guarantees covariance.
+ unsafe fn registration_data_unchecked(&self) -> &<T::RegistrationData as ForLt>::Of<'_> {
+ // SAFETY: Caller guarantees the device is registered, so the pointer is valid.
+ // Lifetimes do not affect layout, so the cast is sound.
+ unsafe { (*self.registration_data.get()).cast::<_>().as_ref() }
}
}
@@ -251,62 +247,98 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
}
}
-// SAFETY: `Device<T>` is refcounted by the fwctl core and may be released from
-// any thread. The embedded driver data is `Send`.
+// SAFETY: `Device<T>` is refcounted by the fwctl core and may be released from any thread.
unsafe impl<T: Operations> Send for Device<T> {}
-// SAFETY: Shared access to the embedded `fwctl_device` is protected by the
-// fwctl core, and the embedded driver data is `Sync`.
+// SAFETY: Shared access to the embedded `fwctl_device` is protected by the fwctl core. The
+// `registration_data` field is only mutated before registration and after unregistration (both
+// single-threaded with respect to callbacks).
unsafe impl<T: Operations> Sync for Device<T> {}
/// A registered fwctl device.
///
-/// Must live inside a [`Devres`] to guarantee that [`fwctl_unregister`] runs
-/// before the parent driver unbinds. `Devres` prevents the `Registration`
-/// from being moved to a context that could outlive the parent device.
+/// Carries the lifetime `'a` of the parent device to ensure that [`fwctl_unregister`] runs (via
+/// [`Drop`]) before the parent driver unbinds. Owns the
+/// [`RegistrationData`](Operations::RegistrationData) which is accessible during callbacks via the
+/// pointer stored in [`Device`].
///
-/// On drop the device is unregistered (all user contexts are closed and
-/// `ops` is set to `NULL`) and the [`ARef`] is released.
+/// On drop the device is unregistered (all user contexts are closed and `ops` is set to `NULL`)
+/// and the registration data is dropped.
///
/// [`fwctl_unregister`]: srctree/drivers/fwctl/main.c
-pub struct Registration<T: Operations> {
+pub struct Registration<'a, T: Operations> {
dev: ARef<Device<T>>,
+ _reg_data: Pin<KBox<<T::RegistrationData as ForLt>::Of<'a>>>,
}
-impl<T: Operations> Registration<T> {
- /// Register a previously allocated fwctl device.
- pub fn new<'a>(
- parent: &'a device::Device<device::Bound>,
- dev: &'a Device<T>,
- ) -> impl PinInit<Devres<Self>, Error> + 'a {
- pin_init::pin_init_scope(move || {
- // SAFETY: `dev` is a valid fwctl_device backed by an ARef.
- let ret = unsafe { bindings::fwctl_register(dev.as_raw()) };
- if ret != 0 {
- return Err(Error::from_errno(ret));
- }
+impl<'a, T: Operations> Registration<'a, T>
+where
+ for<'b> <T::RegistrationData as ForLt>::Of<'b>: Send + Sync,
+{
+ /// Register a previously allocated fwctl device with the given registration data.
+ ///
+ /// The `reg_data` is owned by the registration and accessible during callbacks via
+ /// `Device::registration_data_unchecked()`.
+ ///
+ /// # Safety
+ ///
+ /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
+ /// [`Drop`] implementation from running, since `fwctl_unregister` must be called before the
+ /// parent device is unbound.
+ pub unsafe fn new(
+ _parent: &'a device::Device<device::Bound>,
+ dev: &Device<T>,
+ reg_data: impl PinInit<<T::RegistrationData as ForLt>::Of<'a>, Error>,
+ ) -> Result<Self> {
+ let reg_data: Pin<KBox<<T::RegistrationData as ForLt>::Of<'a>>> =
+ KBox::pin_init(reg_data, GFP_KERNEL)?;
+
+ // Store the registration data pointer in the device before registration, so that it is
+ // visible once callbacks can be invoked.
+ //
+ // SAFETY: Lifetimes do not affect layout, so the pointer cast is sound.
+ let ptr: NonNull<<T::RegistrationData as ForLt>::Of<'static>> =
+ unsafe { mem::transmute(NonNull::from(Pin::get_ref(reg_data.as_ref()))) };
+
+ // SAFETY: No concurrent access; the device is not yet registered.
+ unsafe { *dev.registration_data.get() = ptr };
+
+ // SAFETY: `dev` is a valid fwctl_device backed by an ARef.
+ let ret = unsafe { bindings::fwctl_register(dev.as_raw()) };
+ if ret != 0 {
+ // SAFETY: No concurrent readers; registration failed.
+ unsafe { *dev.registration_data.get() = NonNull::dangling() };
+ return Err(Error::from_errno(ret));
+ }
- Ok(Devres::new(parent, Self { dev: dev.into() }))
+ Ok(Self {
+ dev: dev.into(),
+ _reg_data: reg_data,
})
}
}
-impl<T: Operations> Drop for Registration<T> {
+impl<T: Operations> Drop for Registration<'_, T> {
fn drop(&mut self) {
- // SAFETY: `Registration` lives inside a `Devres`, which guarantees
- // that drop runs while the parent device is still bound.
+ // SAFETY: The Registration lifetime guarantees that the parent device is still bound.
+ // `fwctl_unregister` takes the write lock, closes all user contexts, and sets ops=NULL.
+ // After it returns, no callbacks can be running or will run.
unsafe { bindings::fwctl_unregister(self.dev.as_raw()) };
- // ARef<Device<T>> is dropped after this, calling fwctl_put.
+
+ // SAFETY: `fwctl_unregister` guarantees no concurrent readers.
+ unsafe { *self.dev.registration_data.get() = NonNull::dangling() };
+
+ // `self._reg_data` is dropped here — safe because no concurrent readers remain.
}
}
-// SAFETY: `Registration` can be sent between threads; the underlying
-// fwctl_device uses internal locking.
-unsafe impl<T: Operations> Send for Registration<T> {}
+// SAFETY: `Registration` can be sent between threads; the underlying fwctl_device uses internal
+// locking.
+unsafe impl<T: Operations> Send for Registration<'_, T> {}
-// SAFETY: `Registration` provides no mutable access; the underlying
-// fwctl_device is protected by internal locking.
-unsafe impl<T: Operations> Sync for Registration<T> {}
+// SAFETY: `Registration` provides no mutable access; the underlying fwctl_device is protected by
+// internal locking.
+unsafe impl<T: Operations> Sync for Registration<'_, T> {}
/// Internal per-FD user context wrapping `struct fwctl_uctx` and `T`.
///
@@ -376,7 +408,10 @@ impl<T: Operations> VTable<T> {
// SAFETY: Rust fwctl devices use the offset-0 `Device<T>` layout.
let device = unsafe { Device::<T>::from_raw(raw_fwctl) };
- let initializer = T::open(device);
+ // SAFETY: open_uctx is called under registration_lock read; the device is registered.
+ let reg_data = unsafe { device.registration_data_unchecked() };
+
+ let initializer = T::open(device, reg_data);
let uctx_offset = core::mem::offset_of!(UserCtx<T>, uctx);
// SAFETY: `uctx_size` reserves space for the full `UserCtx<T>`.
@@ -396,13 +431,17 @@ impl<T: Operations> VTable<T> {
// SAFETY: fwctl keeps the owning device live for this callback.
let device = unsafe { Device::<T>::from_raw((*uctx).fwctl) };
+ // SAFETY: close_uctx is called under registration_lock write (from fwctl_unregister) or
+ // under registration_lock read (from fwctl_fops_release); the device is registered.
+ let reg_data = unsafe { device.registration_data_unchecked() };
+
// SAFETY: close is called for an opened Rust user context.
let ctx = unsafe { UserCtx::<T>::from_raw_mut(uctx) };
{
// SAFETY: fwctl never moves an opened user context.
let pinned = unsafe { Pin::new_unchecked(&mut ctx.uctx) };
- T::close(pinned, device);
+ T::close(pinned, device, reg_data);
}
// SAFETY: close is the last callback before fwctl frees the allocation.
@@ -421,10 +460,13 @@ impl<T: Operations> VTable<T> {
let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };
let device = ctx.device();
+ // SAFETY: info is called under registration_lock read; the device is registered.
+ let reg_data = unsafe { device.registration_data_unchecked() };
+
// SAFETY: fwctl never moves an opened user context.
let pinned = unsafe { Pin::new_unchecked(&ctx.uctx) };
- match T::info(pinned, device) {
+ match T::info(pinned, device, reg_data) {
Ok(kvec) if kvec.is_empty() => {
// SAFETY: `length` is a valid out-parameter.
unsafe { *length = 0 };
@@ -461,6 +503,9 @@ impl<T: Operations> VTable<T> {
let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };
let device = ctx.device();
+ // SAFETY: fw_rpc is called under registration_lock read; the device is registered.
+ let reg_data = unsafe { device.registration_data_unchecked() };
+
// SAFETY: fwctl passes a valid in/out buffer for this callback.
let rpc_in_slice: &mut [u8] =
unsafe { slice::from_raw_parts_mut(rpc_in.cast::<u8>(), in_len) };
@@ -468,7 +513,7 @@ impl<T: Operations> VTable<T> {
// SAFETY: fwctl never moves an opened user context.
let pinned = unsafe { Pin::new_unchecked(&ctx.uctx) };
- match T::fw_rpc(pinned, device, scope, rpc_in_slice) {
+ match T::fw_rpc(pinned, device, reg_data, scope, rpc_in_slice) {
Ok(FwRpcResponse::InPlace(len)) => {
// SAFETY: `out_len` is valid.
unsafe { *out_len = len };
[5] nova-core diff:
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 5738d4ac521b..34d595b655fc 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -3,6 +3,7 @@
use kernel::{
auxiliary,
device::Core,
+ fwctl,
pci,
pci::{
Class,
@@ -15,7 +16,7 @@
Atomic,
Relaxed, //
},
- types::ForLt,
+ types::ForLt, //
};
use crate::gpu::Gpu;
@@ -23,6 +24,34 @@
/// Counter for generating unique auxiliary device IDs.
static AUXILIARY_ID_COUNTER: Atomic<u32> = Atomic::new(0);
+struct FwctlRegData<'a> {
+ _bar: Bar0<'a>,
+}
+
+struct FwctlUctx;
+
+impl fwctl::Operations for FwctlUctx {
+ type RegistrationData = ForLt!(FwctlRegData<'_>);
+ const DEVICE_TYPE: fwctl::DeviceType = fwctl::DeviceType::Mlx5;
+
+ fn open(
+ _device: &fwctl::Device<Self>,
+ _reg_data: &FwctlRegData<'_>,
+ ) -> impl PinInit<Self, Error> {
+ Ok(FwctlUctx)
+ }
+
+ fn fw_rpc(
+ _this: core::pin::Pin<&Self>,
+ _device: &fwctl::Device<Self>,
+ _reg_data: &FwctlRegData<'_>,
+ _scope: fwctl::RpcScope,
+ _rpc_in: &mut [u8],
+ ) -> Result<fwctl::FwRpcResponse, Error> {
+ Err(ENOTSUPP)
+ }
+}
+
#[pin_data]
pub(crate) struct NovaCore<'bound> {
#[pin]
@@ -30,6 +59,7 @@ pub(crate) struct NovaCore<'bound> {
bar: pci::Bar<'bound, BAR0_SIZE>,
#[allow(clippy::type_complexity)]
_reg: auxiliary::Registration<'bound, ForLt!(())>,
+ _fwctl_reg: fwctl::Registration<'bound, FwctlUctx>,
}
pub(crate) struct NovaCoreDriver;
@@ -78,6 +108,8 @@ fn probe<'bound>(
pdev.enable_device_mem()?;
pdev.set_master();
+ let fwctl_dev = fwctl::Device::<FwctlUctx>::new(pdev.as_ref())?;
+
Ok(try_pin_init!(NovaCore {
bar: pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0")?,
// TODO: Use `&bar` self-referential pin-init syntax once available.
@@ -95,6 +127,17 @@ fn probe<'bound>(
crate::MODULE_NAME,
(),
)?,
+ // SAFETY: `_fwctl_reg` is dropped before `bar` (struct field drop order), and its
+ // drop calls `fwctl_unregister` before the parent device is unbound.
+ _fwctl_reg: unsafe {
+ fwctl::Registration::new(
+ pdev.as_ref(),
+ &fwctl_dev,
+ Ok(FwctlRegData {
+ _bar: &*core::ptr::from_ref(bar),
+ }),
+ )?
+ },
}))
})
}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 735b8e17c6b6..d5f050b2b55e 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -74,6 +74,7 @@ fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
description: "Nova Core GPU driver",
license: "GPL v2",
firmware: [],
+ imports_ns: ["FWCTL"],
}
kernel::module_firmware!(firmware::ModInfoBuilder);
^ permalink raw reply related
* Re: [PATCH v3 0/3] Add and use abstraction for synchronize_rcu()
From: Boqun Feng @ 2026-06-24 15:30 UTC (permalink / raw)
To: Philipp Stanner
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Alexander Viro, Christian Brauner, Jan Kara,
Lyude Paul, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Christian Schrefl, rust-for-linux, linux-kernel, linux-fsdevel,
rcu
In-Reply-To: <20260624150704.1504001-2-phasta@kernel.org>
On Wed, Jun 24, 2026 at 05:07:01PM +0200, Philipp Stanner wrote:
> Changes since v2:
> - Compromise docu for "RCU versus atomic context" a bit better
> - Add respective R-b's
>
> Changes since v1:
> - Vertically format imports. (Onur)
> - Make the new function #[inline]. (Alice)
> - Add Alice's R-b.
>
> Philipp Stanner (3):
> rust: sync: Add abstraction for synchronize_rcu()
> rust: revocable: Use safe synchronize_rcu() abstraction
> rust: sync: Use safe synchronize_rcu() abstraction in poll
>
> rust/kernel/revocable.rs | 9 ++++++---
> rust/kernel/sync/poll.rs | 10 ++++++----
> rust/kernel/sync/rcu.rs | 16 ++++++++++++++++
> 3 files changed, 28 insertions(+), 7 deletions(-)
>
Queue it in rust-sync
https://git.kernel.org/pub/scm/linux/kernel/git/boqun/linux.git/ rust-sync
Thank you all!
Regards,
Boqun
>
> base-commit: 43a393185e33e573a374c1d4f7ddf6481484ef8d
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt
From: Gary Guo @ 2026-06-24 15:20 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, acourbot, ecourtney,
m.wilczynski, david.m.ertman, ira.weiny, leon, daniel.almeida,
bhelgaas, kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
In-Reply-To: <20260618230834.812007-3-dakr@kernel.org>
On Fri Jun 19, 2026 at 12:08 AM BST, Danilo Krummrich wrote:
> Add a new ForLt trait as a base for CovariantForLt:
>
> - ForLt (non-unsafe): represents a type generic over a lifetime, with
> no covariance guarantee.
>
> - CovariantForLt (unsafe): becomes a subtrait of ForLt that
> additionally proves the type is covariant over its lifetime
> parameter, providing a safe cast_ref() method.
>
> This split allows non-covariant types (e.g. types behind a Mutex) to
> implement ForLt and participate in DevresLt / registration data patterns
> that use HRTB closures for sound access, without requiring a covariance
> proof that would fail to compile.
>
> Both macros share the UnsafeForLtImpl helper type, distinguished by
> a const generic N: ForLt! emits N = 0 (no covariance proof),
> CovariantForLt! emits N = 1 (with compile-time covariance proof).
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/types.rs | 1 +
> rust/kernel/types/for_lt.rs | 72 +++++++++++++++++++++++++++++--------
> rust/macros/for_lt.rs | 53 +++++++++++++++++++++------
> rust/macros/lib.rs | 19 +++++++++-
> 4 files changed, 118 insertions(+), 27 deletions(-)
>
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index cbe6907042d3..c1ed05d1046c 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -14,6 +14,7 @@
> #[doc(hidden)]
> pub mod for_lt;
> pub use for_lt::CovariantForLt;
> +pub use for_lt::ForLt;
>
> /// Used to transfer ownership to and from foreign (non-Rust) languages.
> ///
> diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
> index a11f7509633c..0b53494080b7 100644
> --- a/rust/kernel/types/for_lt.rs
> +++ b/rust/kernel/types/for_lt.rs
> @@ -1,17 +1,59 @@
> // SPDX-License-Identifier: Apache-2.0 OR MIT
>
> -//! Provide implementation and test of the `CovariantForLt` trait and macro.
> +//! Provide implementation and test of the `ForLt` and `CovariantForLt` traits and macros.
> //!
> -//! This module is hidden and user should just use `CovariantForLt!` directly.
> +//! This module is hidden and users should just use `ForLt!` / `CovariantForLt!` directly.
>
> use core::marker::PhantomData;
>
> /// Representation of types generic over a lifetime.
> ///
> -/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter
> -/// can be soundly shortened.
> +/// # Macro
> +///
> +/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
> +/// type that implements this trait.
> ///
> -/// The lifetime involved must be covariant.
> +/// The full syntax is
> +///
> +/// ```
> +/// # use kernel::types::ForLt;
> +/// # fn expect_lt<F: ForLt>() {}
> +/// # struct TypeThatUse<'a>(&'a ());
> +/// # expect_lt::<
> +/// ForLt!(for<'a> TypeThatUse<'a>)
> +/// # >();
> +/// ```
> +///
> +/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
> +/// is `TypeThatUse<'b>`.
> +///
> +/// You may also use a short-hand syntax which works similar to lifetime elision.
> +/// The macro also accepts types that do not involve a lifetime at all.
> +///
> +/// ```
> +/// # use kernel::types::ForLt;
> +/// # fn expect_lt<F: ForLt>() {}
> +/// # struct TypeThatUse<'a>(&'a ());
> +/// # expect_lt::<
> +/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
> +/// # >();
> +/// # expect_lt::<
> +/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
> +/// # >();
> +/// # expect_lt::<
> +/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
> +/// # >();
> +/// ```
> +pub trait ForLt {
> + /// The type parameterized by the lifetime.
> + type Of<'a>: 'a;
> +}
> +pub use macros::ForLt;
> +
> +/// [`trait@ForLt`] subtrait for types that are covariant over their lifetime parameter.
> +///
> +/// Provides a safe [`cast_ref`](CovariantForLt::cast_ref) method for types that are proven to be
> +/// covariant. The `CovariantForLt!` macro syntax is the same as `ForLt!`.
> ///
> /// # Macro
> ///
> @@ -84,10 +126,7 @@
> /// # Safety
> ///
> /// `Self::Of<'a>` must be covariant over the lifetime `'a`.
> -pub unsafe trait CovariantForLt {
> - /// The type parameterized by the lifetime.
> - type Of<'a>: 'a;
> -
> +pub unsafe trait CovariantForLt: ForLt {
> /// Cast a reference to a shorter lifetime.
> #[inline(always)]
> fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {
> @@ -99,25 +138,28 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
>
> /// This is intended to be an "unsafe-to-refer-to" type.
> ///
> -/// Must only be used by the `CovariantForLt!` macro.
> +/// Must only be used by the `ForLt!` / `CovariantForLt!` macros.
> ///
> /// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.
> ///
> /// `WF` is a type that the macro can use to assert some specific type is well-formed.
> ///
> /// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove
> -/// additional properties.
> +/// additional properties. `ForLt!` emits `N = 0`; `CovariantForLt!` emits `N = 1` after a
> +/// covariance proof.
> #[doc(hidden)]
> pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
>
> -// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
> +// This is a helper trait for implementation of `ForLt` / `CovariantForLt` to be able to use HRTB.
> #[doc(hidden)]
> pub trait WithLt<'a> {
> type Of: 'a;
> }
>
> -// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
> -// `UnsafeForLtImpl` and it will fail to evaluate if the type is not covariant.
> -unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 0> {
> +impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for UnsafeForLtImpl<T, WF, N> {
> type Of<'a> = <T as WithLt<'a>>::Of;
> }
> +
> +// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated in the `N` const generic
> +// and it will fail to evaluate if the type is not covariant. Only `N = 1` gets this impl.
> +unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 1> {}
> diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
> index e1233701d6cc..d5f728a464ca 100644
> --- a/rust/macros/for_lt.rs
> +++ b/rust/macros/for_lt.rs
> @@ -176,8 +176,10 @@ fn prove(&mut self, ty: &'a Type) {
> }
> }
>
> -pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> - let (ty, lifetime) = match input {
> +/// Resolve the higher-ranked type into a concrete `(ty, lifetime)` pair, expanding elided
> +/// lifetimes as needed. Shared by both `for_lt` and `covariant_for_lt`.
> +fn resolve_hrt(input: HigherRankedType) -> (Type, Lifetime) {
> + match input {
> HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
> HigherRankedType::Implicit { ty } => {
> // If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime
> @@ -188,7 +190,42 @@ pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> };
> (ty.expand_elided_lifetime(&lifetime), lifetime)
> }
> - };
> + }
> +}
> +
> +/// Produce the `'static`-substituted type for the WF check. Shared by both macros.
> +fn ty_static(ty: &Type, lifetime: &Lifetime) -> Type {
> + ty.replace_lifetime(
> + lifetime,
> + &Lifetime {
> + apostrophe: Span::mixed_site(),
> + ident: format_ident!("static"),
> + },
> + )
> +}
> +
> +pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
> + let (ty, lifetime) = resolve_hrt(input);
> +
> + // Make sure that the type is wellformed when substituting lifetime with `'static`.
> + //
> + // Currently the Rust compiler doesn't check this, see the `ProveWf` documentation in
> + // `covariant_for_lt` below.
> + //
> + // We prefer to use this way of proving WF-ness as it can work when generics are involved.
> + let ty_static = ty_static(&ty, &lifetime);
> +
> + quote!(
> + ::kernel::types::for_lt::UnsafeForLtImpl::<
> + dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
> + #ty_static,
> + 0,
> + >
This is still unsound, as I mentioned in the last version that the prover must
be kept.
`'static` helps with a specific case where it ensures that `ForLt!(for<'a> &'a T)`
requiress `T: 'static`, but when another lifetime (that is not 'static is
involved) the prover is still needed.
A very simple case
fn test_for_lt<'a>(x: &'a ()) {
let _: ForLt!(for<'b> &'a &'b ());
}
this needs to fail the build because 'a and 'b are unrelated (otherwise from the
type you would be able to obtain `'b: 'a` from nowhere).
Best,
Gary
> + )
> +}
> +
> +pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> + let (ty, lifetime) = resolve_hrt(input);
>
> let mut prover = Prover(&lifetime, Vec::new());
> prover.prove(&ty);
> @@ -226,13 +263,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
> // Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation.
> //
> // We prefer to use this way of proving WF-ness as it can work when generics are involved.
> - let ty_static = ty.replace_lifetime(
> - &lifetime,
> - &Lifetime {
> - apostrophe: Span::mixed_site(),
> - ident: format_ident!("static"),
> - },
> - );
> + let ty_static = ty_static(&ty, &lifetime);
>
> quote!(
> ::kernel::types::for_lt::UnsafeForLtImpl::<
> @@ -241,7 +272,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
> {
> #(#proof)*
>
> - 0
> + 1
> }
> >
> )
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 2167cb270928..e970769609f3 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -491,11 +491,28 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
> .into()
> }
>
> -/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
> +/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
> +///
> +/// Please refer to the documentation of the [`ForLt`] trait.
> +///
> +/// [`ForLt`]: trait.ForLt.html
> +#[proc_macro]
> +#[allow(non_snake_case)]
> +pub fn ForLt(input: TokenStream) -> TokenStream {
> + for_lt::for_lt(parse_macro_input!(input)).into()
> +}
> +
> +/// Obtain a type that implements [`CovariantForLt`] (and [`ForLt`]) for the given higher-ranked
> +/// type.
> +///
> +/// Unlike [`ForLt!`], this macro additionally proves that the type is covariant over the lifetime,
> +/// providing a safe [`CovariantForLt::cast_ref`] method.
> ///
> /// Please refer to the documentation of the [`CovariantForLt`] trait.
> ///
> /// [`CovariantForLt`]: trait.CovariantForLt.html
> +/// [`CovariantForLt::cast_ref`]: trait.CovariantForLt.html#method.cast_ref
> +/// [`ForLt`]: trait.ForLt.html
> #[proc_macro]
> #[allow(non_snake_case)]
> pub fn CovariantForLt(input: TokenStream) -> TokenStream {
^ permalink raw reply
* Re: [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt
From: Gary Guo @ 2026-06-24 15:13 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, acourbot, ecourtney,
m.wilczynski, david.m.ertman, ira.weiny, leon, daniel.almeida,
bhelgaas, kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
In-Reply-To: <20260618230834.812007-2-dakr@kernel.org>
On Fri Jun 19, 2026 at 12:08 AM BST, Danilo Krummrich wrote:
> Rename ForLt to CovariantForLt to prepare for the introduction of a new
> ForLt base trait that does not require covariance.
>
> The existing ForLt trait requires covariance, which enables the safe
> cast_ref() method. This rename preserves the same semantics under a more
> precise name, making room for a weaker ForLt trait in a subsequent
> commit.
>
> No functional change.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> drivers/gpu/nova-core/driver.rs | 4 +-
> rust/kernel/auxiliary.rs | 23 +++++------
> rust/kernel/types.rs | 2 +-
> rust/kernel/types/for_lt.rs | 57 ++++++++++++++-------------
> rust/macros/for_lt.rs | 2 +-
> rust/macros/lib.rs | 11 +++---
> samples/rust/rust_driver_auxiliary.rs | 8 ++--
> 7 files changed, 54 insertions(+), 53 deletions(-)
^ permalink raw reply
* Re: [PATCH v3 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres()
From: Alexandre Courbot @ 2026-06-24 15:09 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
aliceryhl, tmgross, ecourtney, m.wilczynski, david.m.ertman,
ira.weiny, leon, daniel.almeida, bhelgaas, kwilczynski,
driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
In-Reply-To: <20260618230834.812007-8-dakr@kernel.org>
On Fri Jun 19, 2026 at 8:08 AM JST, Danilo Krummrich wrote:
> Implement ForLt and CovariantForLt for IoMem<'static, SIZE> and
> ExclusiveIoMem<'static, SIZE> so that DevresLt can shorten the stored
> 'static lifetime back to the caller's borrow lifetime.
>
> CovariantForLt is sound because both types only hold &'a Device<Bound>,
> which is covariant over 'a.
>
> Since DevresLt::new() handles the lifetime transmutation internally,
> into_devres() no longer needs an explicit transmute to 'static.
>
> Add DevresIoMem<SIZE> and DevresExclusiveIoMem<SIZE> type aliases.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply
* [PATCH v3 3/3] rust: sync: Use safe synchronize_rcu() abstraction in poll
From: Philipp Stanner @ 2026-06-24 15:07 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Alexander Viro,
Christian Brauner, Jan Kara, Lyude Paul, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Christian Schrefl,
Philipp Stanner
Cc: rust-for-linux, linux-kernel, linux-fsdevel, rcu
In-Reply-To: <20260624150704.1504001-2-phasta@kernel.org>
We now have a safe wrapper for the foreign function synchronize_rcu().
Use it in poll.rs.
Signed-off-by: Philipp Stanner <phasta@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/sync/poll.rs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs
index 0ec985d560c8..30ebeea1702f 100644
--- a/rust/kernel/sync/poll.rs
+++ b/rust/kernel/sync/poll.rs
@@ -8,7 +8,11 @@
bindings,
fs::File,
prelude::*,
- sync::{CondVar, LockClassKey},
+ sync::{
+ CondVar,
+ LockClassKey,
+ rcu::synchronize_rcu, //
+ }, //
};
use core::{marker::PhantomData, ops::Deref};
@@ -99,8 +103,6 @@ fn drop(self: Pin<&mut Self>) {
unsafe { bindings::__wake_up_pollfree(self.inner.wait_queue_head.get()) };
// Wait for epoll items to be properly removed.
- //
- // SAFETY: Just an FFI call.
- unsafe { bindings::synchronize_rcu() };
+ synchronize_rcu();
}
}
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/3] rust: revocable: Use safe synchronize_rcu() abstraction
From: Philipp Stanner @ 2026-06-24 15:07 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Alexander Viro,
Christian Brauner, Jan Kara, Lyude Paul, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Christian Schrefl,
Philipp Stanner
Cc: rust-for-linux, linux-kernel, linux-fsdevel, rcu
In-Reply-To: <20260624150704.1504001-2-phasta@kernel.org>
We now have a safe wrapper for the foreign function synchronize_rcu().
Use it in revocable.rs.
Signed-off-by: Philipp Stanner <phasta@kernel.org>
Reviewed-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/revocable.rs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
index 0f4ae673256d..f539603349f1 100644
--- a/rust/kernel/revocable.rs
+++ b/rust/kernel/revocable.rs
@@ -7,7 +7,11 @@
use pin_init::Wrapper;
-use crate::{bindings, prelude::*, sync::rcu, types::Opaque};
+use crate::{
+ prelude::*,
+ sync::rcu,
+ types::Opaque, //
+};
use core::{
marker::PhantomData,
ops::Deref,
@@ -161,8 +165,7 @@ unsafe fn revoke_internal<const SYNC: bool>(&self) -> bool {
if revoke {
if SYNC {
- // SAFETY: Just an FFI call, there are no further requirements.
- unsafe { bindings::synchronize_rcu() };
+ rcu::synchronize_rcu();
}
// SAFETY: We know `self.data` is valid because only one CPU can succeed the
--
2.54.0
^ permalink raw reply related
* [PATCH v3 1/3] rust: sync: Add abstraction for synchronize_rcu()
From: Philipp Stanner @ 2026-06-24 15:07 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Alexander Viro,
Christian Brauner, Jan Kara, Lyude Paul, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Christian Schrefl,
Philipp Stanner
Cc: rust-for-linux, linux-kernel, linux-fsdevel, rcu
In-Reply-To: <20260624150704.1504001-2-phasta@kernel.org>
synchronize_rcu() is a frequently used C function which is always safe
to be called.
Add a safe abstraction for synchronize_rcu().
Signed-off-by: Philipp Stanner <phasta@kernel.org>
Reviewed-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/sync/rcu.rs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
index a32bef6e490b..2bae76d229f0 100644
--- a/rust/kernel/sync/rcu.rs
+++ b/rust/kernel/sync/rcu.rs
@@ -50,3 +50,19 @@ fn drop(&mut self) {
pub fn read_lock() -> Guard {
Guard::new()
}
+
+/// Wait for one RCU grace period.
+///
+/// Waits for all RCU read-side critical sections (such as those established by
+/// a [`rcu::Guard`]) at the moment of the function call to finish.
+///
+/// Does not prevent new read-side critical sections from starting, which may
+/// begin and run while this call is blocking.
+///
+/// Note that this is one of the RCU primitives which must not be called in
+/// atomic context.
+#[inline]
+pub fn synchronize_rcu() {
+ // SAFETY: `synchronize_rcu()` is always safe to be called from process context.
+ unsafe { bindings::synchronize_rcu() };
+}
--
2.54.0
^ permalink raw reply related
* [PATCH v3 0/3] Add and use abstraction for synchronize_rcu()
From: Philipp Stanner @ 2026-06-24 15:07 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Alexander Viro,
Christian Brauner, Jan Kara, Lyude Paul, Paul E. McKenney,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Uladzislau Rezki, Steven Rostedt,
Mathieu Desnoyers, Lai Jiangshan, Zqiang, Christian Schrefl,
Philipp Stanner
Cc: rust-for-linux, linux-kernel, linux-fsdevel, rcu
Changes since v2:
- Compromise docu for "RCU versus atomic context" a bit better
- Add respective R-b's
Changes since v1:
- Vertically format imports. (Onur)
- Make the new function #[inline]. (Alice)
- Add Alice's R-b.
Philipp Stanner (3):
rust: sync: Add abstraction for synchronize_rcu()
rust: revocable: Use safe synchronize_rcu() abstraction
rust: sync: Use safe synchronize_rcu() abstraction in poll
rust/kernel/revocable.rs | 9 ++++++---
rust/kernel/sync/poll.rs | 10 ++++++----
rust/kernel/sync/rcu.rs | 16 ++++++++++++++++
3 files changed, 28 insertions(+), 7 deletions(-)
base-commit: 43a393185e33e573a374c1d4f7ddf6481484ef8d
--
2.54.0
^ permalink raw reply
* Re: [PATCH v3 6/7] rust: pci: return DevresLt from Bar::into_devres()
From: Alexandre Courbot @ 2026-06-24 15:05 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
aliceryhl, tmgross, ecourtney, m.wilczynski, david.m.ertman,
ira.weiny, leon, daniel.almeida, bhelgaas, kwilczynski,
driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
In-Reply-To: <20260618230834.812007-7-dakr@kernel.org>
On Fri Jun 19, 2026 at 8:08 AM JST, Danilo Krummrich wrote:
> Implement ForLt and CovariantForLt for Bar<'static, SIZE> so that
> DevresLt can shorten the stored 'static lifetime back to the caller's
> borrow lifetime.
>
> CovariantForLt is sound because Bar<'a, SIZE> only holds &'a
> Device<Bound>, which is covariant over 'a.
>
> Since DevresLt::new() handles the lifetime transmutation internally,
> into_devres() no longer needs an explicit transmute to Bar<'static>.
>
> Add a DevresBar<SIZE> type alias for convenience.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply
* [PATCH v6 10/10] rust: module: update MAINTAINERS to cover module.rs
From: Alvin Sun @ 2026-06-24 15:00 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260624-fix-fops-owner-v6-0-5295e333cb3e@linux.dev>
Module types now live in `rust/kernel/module.rs` alongside
`rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern
from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both
files are covered.
Assisted-by: opencode:glm-5.2
Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c4..74733de3e41ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17984,7 +17984,7 @@ F: include/linux/module*.h
F: kernel/module/
F: lib/test_kmod.c
F: lib/tests/module/
-F: rust/kernel/module_param.rs
+F: rust/kernel/module*.rs
F: rust/macros/module.rs
F: scripts/module*
F: tools/testing/selftests/kmod/
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox