public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers
@ 2026-04-27 22:10 Danilo Krummrich
  2026-04-27 22:10 ` [PATCH 01/24] rust: driver core: drop drvdata before devres release Danilo Krummrich
                   ` (24 more replies)
  0 siblings, 25 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:10 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Currently, Rust device drivers access device resources such as PCI BAR mappings
and I/O memory regions through Devres<T>.

Devres::access() provides zero-overhead access by taking a &Device<Bound>
reference as proof that the device is still bound. Since a &Device<Bound> is
available in almost all contexts by design, Devres is mostly a type-system level
proof that the resource is valid, but it can also be used from scopes without
this guarantee through its try_access() accessor.

This works well in general, but has a few limitations:

  - Every access to a device resource goes through Devres::access(), which
    despite zero cost, adds boilerplate to every access site.

  - Destructors do not receive a &Device<Bound>, so they must use try_access(),
    which can fail. In practice the access succeeds if teardown ordering is
    correct, but the type system can't express this, forcing drivers to handle a
    failure path that should never be taken.

  - Sharing a resource across components (e.g. passing a BAR to a sub-component)
    requires Arc<Devres<T>>.

  - Device references must be stored as ARef<Device> rather than plain &Device
    borrows.

These limitations stem from the driver's bus device private data being 'static
-- the driver struct cannot borrow from the device reference it receives in
probe(), even though it structurally cannot outlive the device binding.

This series introduces Higher-Ranked Lifetime Types (HRT) for Rust device
drivers. An HRT is a type that is generic over a lifetime -- it does not have a
fixed lifetime, but can be instantiated with any lifetime chosen by the caller.

Rust does not directly support types that are generic over a lifetime as type
parameters; the ForLt trait (contributed by Gary Guo) encodes this internally.

The module_*_driver! macros handle the wrapping, so driver authors just write
struct MyDriver<'a> and impl Driver<'a>.

With HRT, driver structs carry a lifetime parameter tied to the device binding
scope -- the interval of a bus device being bound to a driver. Device resources
like pci::Bar<'a> and IoMem<'a> are handed out with this lifetime, so the
compiler enforces at build time that they do not escape the binding scope.

Before:

	struct MyDriver {
	    pdev: ARef<pci::Device>,
	    bar: Devres<pci::Bar<BAR_SIZE>>,
	}

	let io = self.bar.access(dev)?;
	io.read32(OFFSET);

After:

	struct MyDriver<'a> {
	    pdev: &'a pci::Device,
	    bar: pci::Bar<'a, BAR_SIZE>,
	}

	self.bar.read32(OFFSET);

Lifetime-parameterized device resources can be put into a Devres at any point
via Bar::into_devres() / IoMem::into_devres(), providing the exact same
semantics as before. This is useful for resources shared across subsystem
boundaries where revocation is needed.

This also synergizes with the upcoming self-referential initialization support
in pin-init, which allows one field of the driver struct to borrow another
during initialization without unsafe code.

The same pattern is applied to auxiliary device registration data as a first
example beyond bus device private data. Registration<F: ForLt> can hold
lifetime-parameterized data tied to the parent driver's binding scope. Since the
auxiliary bus guarantees that the parent remains bound while the auxiliary
device is registered, the registration data can safely borrow the parent's
device resources.

More generally, binding resource lifetimes to a registration scope applies to
every registration that is scoped to a driver binding -- auxiliary devices,
class devices, IRQ handlers, workqueues.

A follow-up series extends this to class device registrations, starting with
DRM, so that class device callbacks (IOCTLs, etc.) can safely access device
resources through the separate registration data bound to the registration's
lifetime without Devres indirection.

The series contains a few driver patches for reference, indicated by the REF
suffix.

Thanks to Gary for coming up with the ForLt implementation; thanks to Alice for
the early discussions around lifetime-parameterized private data that helped
shape the direction of this work.

This series depends on [1].

[1] https://lore.kernel.org/driver-core/20260427221002.2143861-1-dakr@kernel.org/

Danilo Krummrich (23):
  rust: driver core: drop drvdata before devres release
  rust: devres: add ForLt support to Devres
  rust: device: generalize drvdata methods over ForLt
  rust: driver: make Adapter trait lifetime-parameterized
  rust: pci: implement Sync for Device<Bound>
  rust: platform: implement Sync for Device<Bound>
  rust: auxiliary: implement Sync for Device<Bound>
  rust: usb: implement Sync for Device<Bound>
  rust: device: implement Sync for Device<Bound>
  rust: pci: make Driver trait lifetime-parameterized
  rust: platform: make Driver trait lifetime-parameterized
  rust: auxiliary: make Driver trait lifetime-parameterized
  rust: auxiliary: generalize Registration over ForLt
  samples: rust: rust_driver_auxiliary: showcase lifetime-bound
    registration data
  rust: usb: make Driver trait lifetime-parameterized
  rust: i2c: make Driver trait lifetime-parameterized
  rust: pci: make Bar lifetime-parameterized
  rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized
  samples: rust: rust_driver_pci: use HRT lifetime for Bar
  gpu: nova-core: use HRT lifetime for Bar
  gpu: nova-core: unregister sysmem flush page from Drop
  gpu: nova-core: replace ARef<Device> with &'a Device in SysmemFlush
  gpu: drm: tyr: use HRT lifetime for IoMem

Gary Guo (1):
  rust: types: add `ForLt` trait for higher-ranked lifetime support

 drivers/base/dd.c                     |   2 +-
 drivers/cpufreq/rcpufreq_dt.rs        |  10 +-
 drivers/gpu/drm/nova/driver.rs        |   9 +-
 drivers/gpu/drm/tyr/driver.rs         |  24 ++-
 drivers/gpu/drm/tyr/gpu.rs            |  62 ++++---
 drivers/gpu/drm/tyr/regs.rs           |  21 +--
 drivers/gpu/nova-core/driver.rs       |  48 ++---
 drivers/gpu/nova-core/fb.rs           |  31 ++--
 drivers/gpu/nova-core/gpu.rs          |  32 +---
 drivers/gpu/nova-core/nova_core.rs    |   4 +-
 drivers/pwm/pwm_th1520.rs             |  14 +-
 include/linux/device/driver.h         |   4 +-
 rust/Makefile                         |   1 +
 rust/kernel/auxiliary.rs              | 144 ++++++++++-----
 rust/kernel/cpufreq.rs                |   8 +-
 rust/kernel/device.rs                 |  84 ++++++---
 rust/kernel/devres.rs                 |  31 +++-
 rust/kernel/driver.rs                 |  44 +++--
 rust/kernel/i2c.rs                    | 121 ++++++++-----
 rust/kernel/io/mem.rs                 | 118 ++++++-------
 rust/kernel/pci.rs                    |  88 +++++++---
 rust/kernel/pci/io.rs                 |  50 +++---
 rust/kernel/platform.rs               | 101 +++++++----
 rust/kernel/types.rs                  |   4 +
 rust/kernel/types/for_lt.rs           | 117 +++++++++++++
 rust/kernel/usb.rs                    |  93 ++++++----
 rust/macros/for_lt.rs                 | 242 ++++++++++++++++++++++++++
 rust/macros/lib.rs                    |  12 ++
 samples/rust/rust_debugfs.rs          |  10 +-
 samples/rust/rust_dma.rs              |   9 +-
 samples/rust/rust_driver_auxiliary.rs |  53 ++++--
 samples/rust/rust_driver_i2c.rs       |  18 +-
 samples/rust/rust_driver_pci.rs       |  93 +++++-----
 samples/rust/rust_driver_platform.rs  |  12 +-
 samples/rust/rust_driver_usb.rs       |  14 +-
 samples/rust/rust_i2c_client.rs       |  12 +-
 samples/rust/rust_soc.rs              |  12 +-
 37 files changed, 1182 insertions(+), 570 deletions(-)
 create mode 100644 rust/kernel/types/for_lt.rs
 create mode 100644 rust/macros/for_lt.rs

-- 
2.54.0


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

* [PATCH 01/24] rust: driver core: drop drvdata before devres release
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
@ 2026-04-27 22:10 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support Danilo Krummrich
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:10 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Move the post_unbind_rust callback before devres_release_all() in
device_unbind_cleanup().

With drvdata() removed, the driver's bus device private data is only
accessible by the owning driver itself. It is hence safe to drop the
driver's bus device private data before devres actions are released.

This reordering is the key enabler for Higher-Ranked Lifetime Types
(HRT) in Rust device drivers -- it allows driver structs to hold direct
references to devres-managed resources, because the bus device private
data (and with it all such references) is guaranteed to be dropped while
the underlying devres resources are still alive.

Without this change, devres resources would be freed first, leaving the
driver's bus device private data with dangling references during its
destructor.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/base/dd.c             | 2 +-
 include/linux/device/driver.h | 4 ++--
 rust/kernel/driver.rs         | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 5799a60fd058..be59d2e13a15 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -593,9 +593,9 @@ static DEVICE_ATTR_RW(state_synced);
 
 static void device_unbind_cleanup(struct device *dev)
 {
-	devres_release_all(dev);
 	if (dev->driver->p_cb.post_unbind_rust)
 		dev->driver->p_cb.post_unbind_rust(dev);
+	devres_release_all(dev);
 	arch_teardown_dma_ops(dev);
 	kfree(dev->dma_range_map);
 	dev->dma_range_map = NULL;
diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h
index bbc67ec513ed..38e9a4679447 100644
--- a/include/linux/device/driver.h
+++ b/include/linux/device/driver.h
@@ -123,8 +123,8 @@ struct device_driver {
 	struct driver_private *p;
 	struct {
 		/*
-		 * Called after remove() and after all devres entries have been
-		 * processed. This is a Rust only callback.
+		 * Called after remove() but before devres entries are released.
+		 * This is a Rust only callback.
 		 */
 		void (*post_unbind_rust)(struct device *dev);
 	} p_cb;
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 36de8098754d..8f0e50729215 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -189,8 +189,8 @@ extern "C" fn post_unbind_callback(dev: *mut bindings::device) {
         // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`.
         let dev = unsafe { &*dev.cast::<device::Device<device::CoreInternal>>() };
 
-        // `remove()` and all devres callbacks have been completed at this point, hence drop the
-        // driver's device private data.
+        // `remove()` has been completed at this point; devres resources are still valid and will
+        // be released after the driver's bus device private data is dropped.
         //
         // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the
         // driver's device private data type.
-- 
2.54.0


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

* [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
  2026-04-27 22:10 ` [PATCH 01/24] rust: driver core: drop drvdata before devres release Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:16   ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 03/24] rust: devres: add ForLt support to Devres Danilo Krummrich
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux

From: Gary Guo <gary@garyguo.net>

There are a few cases, e.g. when dealing with data referencing each other,
one might want to write code that are generic over lifetimes. For example,
if you want take a function that takes `&'a Foo` and gives `Bar<'a>`, you
can write:

    f: impl for<'a> FnOnce(&'a Foo) -> Bar<'a>,

However, it becomes tricky when you want that function to not have a fixed
`Bar`, but have it be generic again. In this case, one needs something that
is generic over types that are themselves generic over lifetimes.

`ForLt` provides such support. It provides a trait `ForLt` which describes
a type generic over lifetime. One may use `ForLt::Of<'a>` to get an
instance of a type for a specific lifetime.

For the case of cross referencing, one would almost always want the
lifetime to be covariant. Therefore this is also made a requirement for the
`ForLt` trait, so functions with `ForLt` trait bound can assume covariance.

A macro `ForLt!()` is provided to be able to obtain a type that implements
`ForLt`. For example, `ForLt!(for<'a> Bar<'a>)` would yield a type that
`<TheType as ForLt>::Of<'a>` is `Bar<'a>`. This also works with lifetime
elision, e.g. `ForLt!(Bar<'_>)` or for types without lifetime at all, e.g.
`ForLt!(u32)`.

The API design draws inspiration from the higher-kinded-types [1] crate,
however different design decision has been taken (e.g. covariance
requirement) and the implementation is independent.

License headers use "Apache-2.0 OR MIT" because I anticipate this to be
used in pin-init crate too which is licensed as such.

Link: https://docs.rs/higher-kinded-types/ [1]

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/Makefile               |   1 +
 rust/kernel/types.rs        |   4 +
 rust/kernel/types/for_lt.rs | 117 +++++++++++++++++
 rust/macros/for_lt.rs       | 242 ++++++++++++++++++++++++++++++++++++
 rust/macros/lib.rs          |  12 ++
 5 files changed, 376 insertions(+)
 create mode 100644 rust/kernel/types/for_lt.rs
 create mode 100644 rust/macros/for_lt.rs

diff --git a/rust/Makefile b/rust/Makefile
index b361bfedfdf0..c5a9a3339416 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -110,6 +110,7 @@ syn-cfgs := \
     feature="parsing" \
     feature="printing" \
     feature="proc-macro" \
+    feature="visit" \
     feature="visit-mut"
 
 syn-flags := \
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index 4329d3c2c2e5..3119401dcb9f 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -11,6 +11,10 @@
 };
 use pin_init::{PinInit, Wrapper, Zeroable};
 
+#[doc(hidden)]
+pub mod for_lt;
+pub use for_lt::ForLt;
+
 /// 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
diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
new file mode 100644
index 000000000000..4983cc761f80
--- /dev/null
+++ b/rust/kernel/types/for_lt.rs
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! Provide implementation and test of the `ForLt` trait and macro.
+//!
+//! This module is hidden and user should just use `ForLt!` 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 shorterned.
+///
+/// The lifetime involved must be covariant.
+///
+/// # Macro
+///
+/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
+/// type that implements this trait.
+///
+/// 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 does not involved 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)`
+/// # >();
+/// ```
+///
+/// The macro will attempt to prove that the type is indeed covariant over the lifetime supplied.
+/// When it cannot be syntactically proven, it will emit checks to ask the Rust compiler to prove
+/// it.
+/// ```ignore,compile_fail
+/// # use kernel::types::ForLt;
+/// # fn expect_lt<F: ForLt>() {}
+/// # expect_lt::<
+/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.
+/// # >();
+/// ```
+///
+/// There is a limitation if the type refer to generic parameters; if the macro cannot prove the
+/// covariance syntactically, the emitted checks will fail the compilation as it needs to refer to
+/// the generic parameter but is in a separate item.
+/// ```
+/// # use kernel::types::ForLt;
+/// fn expect_lt<F: ForLt>() {}
+/// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")]
+/// fn generic_fn<T: 'static>() {
+///     // Syntactically proven by the macro
+///     expect_lt::<ForLt!(&T)>();
+///     // Syntactically proven by the macro
+///     expect_lt::<ForLt!(&KBox<T>)>();
+///     // Cannot be syntactically proven, need to check covariance of `KBox`
+///     // expect_lt::<ForLt!(&KBox<&T>)>();
+/// }
+/// ```
+///
+/// # Safety
+///
+/// `Self::Of<'a>` must be covariant over the lifetime `'a`.
+pub unsafe trait ForLt {
+    /// The type parameterized by the lifetime.
+    type Of<'a>;
+
+    /// 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> {
+        // SAFETY: This is sound as this trait guarantees covariance.
+        unsafe { core::mem::transmute(long) }
+    }
+}
+pub use macros::ForLt;
+
+/// This is intended to be an "unsafe-to-refer-to" type.
+///
+/// Must only be used by the `ForLt!` macro.
+///
+/// `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.
+#[doc(hidden)]
+pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
+
+// This is a helper trait for implementation `ForLt` to be able to use HRTB.
+#[doc(hidden)]
+pub trait WithLt<'a> {
+    type Of;
+}
+
+// SAFETY: In `ForLt!` 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> ForLt for UnsafeForLtImpl<T, WF, 0> {
+    type Of<'a> = <T as WithLt<'a>>::Of;
+}
diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
new file mode 100644
index 000000000000..df2027789713
--- /dev/null
+++ b/rust/macros/for_lt.rs
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::{
+    Span,
+    TokenStream, //
+};
+use quote::{
+    format_ident,
+    quote, //
+};
+use syn::{
+    parse::{
+        Parse,
+        ParseStream, //
+    },
+    visit::Visit,
+    visit_mut::VisitMut,
+    Lifetime,
+    Result,
+    Token,
+    Type, //
+};
+
+pub(crate) enum HigherRankedType {
+    Explicit {
+        _for_token: Token![for],
+        _lt_token: Token![<],
+        lifetime: Lifetime,
+        _gt_token: Token![>],
+        ty: Type,
+    },
+    Implicit {
+        ty: Type,
+    },
+}
+
+impl Parse for HigherRankedType {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        if input.peek(Token![for]) {
+            Ok(Self::Explicit {
+                _for_token: input.parse()?,
+                _lt_token: input.parse()?,
+                lifetime: input.parse()?,
+                _gt_token: input.parse()?,
+                ty: input.parse()?,
+            })
+        } else {
+            Ok(Self::Implicit { ty: input.parse()? })
+        }
+    }
+}
+
+trait TypeExt {
+    fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type;
+    fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type;
+    fn has_lifetime(&self, lt: &Lifetime) -> bool;
+}
+
+impl TypeExt for Type {
+    fn expand_elided_lifetime(&self, explicit_lt: &Lifetime) -> Type {
+        struct ElidedLifetimeExpander<'a>(&'a Lifetime);
+
+        impl VisitMut for ElidedLifetimeExpander<'_> {
+            fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) {
+                // Expand explicit `'_`
+                if lifetime.ident == "_" {
+                    *lifetime = self.0.clone();
+                }
+            }
+
+            fn visit_type_reference_mut(&mut self, reference: &mut syn::TypeReference) {
+                syn::visit_mut::visit_type_reference_mut(self, reference);
+
+                if reference.lifetime.is_none() {
+                    reference.lifetime = Some(self.0.clone());
+                }
+            }
+        }
+
+        let mut ret = self.clone();
+        ElidedLifetimeExpander(explicit_lt).visit_type_mut(&mut ret);
+        ret
+    }
+
+    fn replace_lifetime(&self, src: &Lifetime, dst: &Lifetime) -> Type {
+        struct LifetimeReplacer<'a>(&'a Lifetime, &'a Lifetime);
+
+        impl VisitMut for LifetimeReplacer<'_> {
+            fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) {
+                if lifetime.ident == self.0.ident {
+                    *lifetime = self.1.clone();
+                }
+            }
+        }
+
+        let mut ret = self.clone();
+        LifetimeReplacer(src, dst).visit_type_mut(&mut ret);
+        ret
+    }
+
+    fn has_lifetime(&self, lt: &Lifetime) -> bool {
+        struct HasLifetime<'a>(&'a Lifetime, bool);
+
+        impl Visit<'_> for HasLifetime<'_> {
+            fn visit_lifetime(&mut self, lifetime: &Lifetime) {
+                if lifetime.ident == self.0.ident {
+                    self.1 = true;
+                }
+            }
+        }
+
+        let mut visitor = HasLifetime(lt, false);
+        visitor.visit_type(self);
+        visitor.1
+    }
+}
+
+struct Prover<'a>(&'a Lifetime, Vec<&'a Type>);
+
+impl<'a> Prover<'a> {
+    /// Prove that `ty` is covariant over `'lt`.
+    ///
+    /// This also needs to prove that it'll be wellformed for any instance of `'lt`.
+    /// It can be assumed that `ty` will be wellformed if `'lt` is substituted to `'static`.
+    fn prove(&mut self, ty: &'a Type) {
+        match ty {
+            Type::Paren(ty) => self.prove(&ty.elem),
+            Type::Group(ty) => self.prove(&ty.elem),
+
+            // No lifetime involved
+            Type::Never(_) => {}
+
+            // `[T; N]` and `[T]` is covariant over `T`.
+            Type::Array(ty) => self.prove(&ty.elem),
+            Type::Slice(ty) => self.prove(&ty.elem),
+
+            Type::Tuple(ty) => {
+                for elem in &ty.elems {
+                    self.prove(elem);
+                }
+            }
+
+            // `*const T` is covariant over `T`
+            Type::Ptr(ty) if ty.const_token.is_some() => self.prove(&ty.elem),
+
+            // `&T` is covariant over `T` and lifetime.
+            //
+            // Note that if we encounter `&'other_lt T`, then we still need to make sure the type
+            // is wellformed if `T` involves `&'lt`, so we defer to the compiler.
+            //
+            // This is to block cases like `ForLt!(for<'a> &'static &'a u32)`, as the presence of
+            // the type implies `'a: 'static` but this is unsound.
+            Type::Reference(ty)
+                if ty.mutability.is_none() && ty.lifetime.as_ref() == Some(self.0) =>
+            {
+                self.prove(&ty.elem)
+            }
+
+            // `&[mut] T` is covariant over lifetime.
+            // In case we have `&[mut] NoLifetime`, we don't need to do additional checks.
+            Type::Reference(ty) if !ty.elem.has_lifetime(self.0) => (),
+
+            // No mention of lifetime at all, no need to perform compiler check.
+            ty if !ty.has_lifetime(self.0) => (),
+
+            // Otherwise, we need to emit checks so that compiler can determine if the types are
+            // actually covariant.
+            ty => self.1.push(ty),
+        }
+    }
+}
+
+pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
+    let (ty, 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
+            // and expand elided sites.
+            let lifetime = Lifetime {
+                apostrophe: Span::mixed_site(),
+                ident: format_ident!("__elided", span = Span::mixed_site()),
+            };
+            (ty.expand_elided_lifetime(&lifetime), lifetime)
+        }
+    };
+
+    let mut prover = Prover(&lifetime, Vec::new());
+    prover.prove(&ty);
+
+    let mut proof = Vec::new();
+
+    // Emit proofs for every type that requires additional compiler help in proving covariance.
+    for (idx, required_proof) in prover.1.into_iter().enumerate() {
+        // Insert a proof that the type is well-formed.
+        //
+        // This is intended to workaround a Rust compiler soundness bug related to HRTB.
+        // https://github.com/rust-lang/rust/issues/152489
+        //
+        // This needs to be a struct instead of fn to avoid the implied WF bounds.
+        let wf_proof_name = format_ident!("ProveWf{idx}");
+        proof.push(quote!(
+            struct #wf_proof_name<#lifetime>(
+                ::core::marker::PhantomData<&#lifetime ()>, #required_proof
+            );
+        ));
+
+        // Insert a proof that the type is covariant.
+        let cov_proof_name = format_ident!("prove_covariant_{idx}");
+        proof.push(quote!(
+            fn #cov_proof_name<'__short, '__long: '__short>(
+                long: #wf_proof_name<'__long>
+            ) -> #wf_proof_name<'__short> {
+                long
+            }
+        ));
+    }
+
+    // Make sure that the type is wellformed when substituting lifetime with `'static`.
+    //
+    // 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"),
+        },
+    );
+
+    quote!(
+        ::kernel::types::for_lt::UnsafeForLtImpl::<
+            dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
+            #ty_static,
+            {
+                #(#proof)*
+
+                0
+            }
+        >
+    )
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 2cfd59e0f9e7..e5f6f8318112 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -17,6 +17,7 @@
 mod concat_idents;
 mod export;
 mod fmt;
+mod for_lt;
 mod helpers;
 mod kunit;
 mod module;
@@ -489,3 +490,14 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
         .unwrap_or_else(|e| e.into_compile_error())
         .into()
 }
+
+/// Obtain a type that implements `ForLt` for the given higher-ranked type.
+///
+/// Please refer to the documentation of [`ForLt`] trait.
+///
+/// [`ForLt`]: trait.ForLt.html
+#[proc_macro]
+#[allow(non_snake_case)] // The macro shares the name with the trait.
+pub fn ForLt(input: TokenStream) -> TokenStream {
+    for_lt::for_lt(parse_macro_input!(input)).into()
+}
-- 
2.54.0


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

* [PATCH 03/24] rust: devres: add ForLt support to Devres
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
  2026-04-27 22:10 ` [PATCH 01/24] rust: driver core: drop drvdata before devres release Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 04/24] rust: device: generalize drvdata methods over ForLt Danilo Krummrich
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Use ForLt::cast_ref() in Devres access methods, enabling lifetime
shortening for types that are covariant over their lifetime parameter.

This is a no-op for 'static types, but prepares Devres for use with
lifetime-parameterized types such as pci::Bar<'_, SIZE>.

Add DevresGuard as a wrapper around RevocableGuard that applies
ForLt::cast_ref() on deref().

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/devres.rs | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 9e5f93aed20c..7baabcdb1ad3 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -24,6 +24,7 @@
         Arc, //
     },
     types::{
+        ForLt,
         ForeignOwnable,
         Opaque, //
     },
@@ -324,22 +325,26 @@ pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a T> {
         // SAFETY: `dev` being the same device as the device this `Devres` has been created for
         // proves that `self.data` hasn't been revoked and is guaranteed to not be revoked as long
         // as `dev` lives; `dev` lives at least as long as `self`.
-        Ok(unsafe { self.data().access() })
+        Ok(<ForLt!(T)>::cast_ref(unsafe { self.data().access() }))
     }
 
     /// [`Devres`] accessor for [`Revocable::try_access`].
-    pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
-        self.data().try_access()
+    #[allow(clippy::type_complexity)]
+    pub fn try_access(&self) -> Option<DevresGuard<'_, ForLt!(T)>> {
+        self.data().try_access().map(DevresGuard)
     }
 
     /// [`Devres`] accessor for [`Revocable::try_access_with`].
     pub fn try_access_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
-        self.data().try_access_with(f)
+        self.data()
+            .try_access_with(|data| f(<ForLt!(T)>::cast_ref(data)))
     }
 
     /// [`Devres`] accessor for [`Revocable::try_access_with_guard`].
     pub fn try_access_with_guard<'a>(&'a self, guard: &'a rcu::Guard) -> Option<&'a T> {
-        self.data().try_access_with_guard(guard)
+        self.data()
+            .try_access_with_guard(guard)
+            .map(<ForLt!(T)>::cast_ref)
     }
 }
 
@@ -365,6 +370,20 @@ fn drop(&mut self) {
     }
 }
 
+/// Guard returned by [`Devres::try_access`].
+///
+/// Dereferences to `F::Of<'a>`, applying [`ForLt::cast_ref`] to shorten the lifetime of the
+/// stored data to the guard's borrow lifetime.
+pub struct DevresGuard<'a, F: ForLt>(RevocableGuard<'a, F::Of<'static>>);
+
+impl<'a, F: ForLt> core::ops::Deref for DevresGuard<'a, F> {
+    type Target = F::Of<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        F::cast_ref(&*self.0)
+    }
+}
+
 /// Consume `data` and [`Drop::drop`] `data` once `dev` is unbound.
 fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
 where
-- 
2.54.0


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

* [PATCH 04/24] rust: device: generalize drvdata methods over ForLt
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (2 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 03/24] rust: devres: add ForLt support to Devres Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 05/24] rust: driver: make Adapter trait lifetime-parameterized Danilo Krummrich
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Generalize set_drvdata(), drvdata_obtain() and drvdata_borrow() to take
F: ForLt, enabling Higher-Ranked Lifetime Types (HRT) for device private
data.

The data is initialized as F::Of<'a> and stored as F::Of<'static>; ForLt
guarantees covariance, making it sound to shorten the stored 'static
lifetime to the borrow lifetime of &self.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/auxiliary.rs |  7 ++--
 rust/kernel/device.rs    | 80 +++++++++++++++++++++++++++++-----------
 rust/kernel/driver.rs    | 15 +++++---
 rust/kernel/i2c.rs       | 13 ++++---
 rust/kernel/pci.rs       | 11 ++++--
 rust/kernel/platform.rs  | 11 ++++--
 rust/kernel/usb.rs       | 11 ++++--
 7 files changed, 101 insertions(+), 47 deletions(-)

diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 467befea8e44..5cd10b254baf 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -20,6 +20,7 @@
     },
     prelude::*,
     types::{
+        ForLt,
         ForeignOwnable,
         Opaque, //
     },
@@ -46,7 +47,7 @@
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
 unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
     type DriverType = bindings::auxiliary_driver;
-    type DriverData = T;
+    type DriverData = ForLt!(T);
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
@@ -97,7 +98,7 @@ extern "C" fn probe_callback(
         from_result(|| {
             let data = T::probe(adev, info);
 
-            adev.as_ref().set_drvdata(data)?;
+            adev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
             Ok(0)
         })
     }
@@ -112,7 +113,7 @@ extern "C" fn remove_callback(adev: *mut bindings::auxiliary_device) {
         // SAFETY: `remove_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { adev.as_ref().drvdata_borrow::<T>() };
+        let data = unsafe { adev.as_ref().drvdata_borrow::<ForLt!(T)>() };
 
         T::unbind(adev, data);
     }
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index fd50399aadea..09cbe8a438a9 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -10,6 +10,7 @@
     prelude::*,
     sync::aref::ARef,
     types::{
+        ForLt,
         ForeignOwnable,
         Opaque, //
     }, //
@@ -202,23 +203,41 @@ pub unsafe fn as_bound(&self) -> &Device<Bound> {
 }
 
 impl Device<CoreInternal> {
-    /// Store a pointer to the bound driver's private data.
-    pub fn set_drvdata<T: 'static>(&self, data: impl PinInit<T, Error>) -> Result {
+    /// Store the bound driver's private data.
+    ///
+    /// `F` is the [`ForLt`] encoding of the data type. For types without a lifetime parameter,
+    /// use [`ForLt!(T)`](macro@ForLt). For lifetime-parameterized types, the data is
+    /// initialized as `F::Of<'a>` and stored as `F::Of<'static>`; lifetimes are erased and do not
+    /// affect layout, while [`ForLt`] guarantees covariance for safe lifetime shortening.
+    ///
+    /// [`ForLt`]: trait@ForLt
+    pub fn set_drvdata<'a, F: ForLt>(&self, data: impl PinInit<F::Of<'a>, Error>) -> Result
+    where
+        F::Of<'static>: 'static,
+    {
         let data = KBox::pin_init(data, GFP_KERNEL)?;
 
+        // SAFETY: Lifetimes are erased and do not affect layout, so Of<'a> and Of<'static> have
+        // identical representation. The raw pointer is type-erased through c_void anyway.
+        let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(data) });
+
         // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
-        unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) };
+        unsafe { bindings::dev_set_drvdata(self.as_raw(), ptr.cast()) };
 
         Ok(())
     }
 
     /// Take ownership of the private data stored in this [`Device`].
     ///
+    /// `F` is the [`ForLt`] encoding of the data type. The returned [`KBox`] has its lifetime
+    /// tied to `&self`, ensuring it is dropped before the device goes away.
+    ///
     /// # Safety
     ///
-    /// - The type `T` must match the type of the `ForeignOwnable` previously stored by
-    ///   [`Device::set_drvdata`].
-    pub(crate) unsafe fn drvdata_obtain<T: 'static>(&self) -> Option<Pin<KBox<T>>> {
+    /// - `F` must match the [`ForLt`] type previously stored by [`Device::set_drvdata`].
+    ///
+    /// [`ForLt`]: trait@ForLt
+    pub(crate) unsafe fn drvdata_obtain<F: ForLt>(&self) -> Option<Pin<KBox<F::Of<'_>>>> {
         // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
         let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
 
@@ -230,24 +249,34 @@ pub(crate) unsafe fn drvdata_obtain<T: 'static>(&self) -> Option<Pin<KBox<T>>> {
         }
 
         // SAFETY:
-        // - If `ptr` is not NULL, it comes from a previous call to `into_foreign()`.
-        // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
-        //   in `into_foreign()`.
-        Some(unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) })
+        // - If `ptr` is not NULL, it was stored by a previous call to `set_drvdata()`, which
+        //   stores a pointer via `KBox::into_raw()`.
+        // - Lifetimes are erased and do not affect layout, so reconstructing as `F::Of<'_>`
+        //   (tied to `&self`) is sound.
+        // - `dev_get_drvdata()` guarantees to return the same pointer given to
+        //   `dev_set_drvdata()`.
+        Some(unsafe { Pin::new_unchecked(KBox::from_raw(ptr.cast())) })
     }
 
     /// Borrow the driver's private data bound to this [`Device`].
     ///
+    /// `F` is the [`ForLt`] encoding of the data type. The returned reference has its lifetime
+    /// shortened from `'static` to `&self`'s borrow lifetime via [`ForLt::cast_ref`].
+    ///
     /// # Safety
     ///
     /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before the
     ///   device is fully unbound.
-    /// - The type `T` must match the type of the `ForeignOwnable` previously stored by
-    ///   [`Device::set_drvdata`].
-    pub unsafe fn drvdata_borrow<T: 'static>(&self) -> Pin<&T> {
+    /// - `F` must match the [`ForLt`] type previously stored by [`Device::set_drvdata`].
+    ///
+    /// [`ForLt`]: trait@ForLt
+    pub unsafe fn drvdata_borrow<F: ForLt>(&self) -> Pin<&F::Of<'_>>
+    where
+        F::Of<'static>: 'static,
+    {
         // SAFETY: `drvdata_unchecked()` has the exact same safety requirements as the ones
         // required by this method.
-        unsafe { self.drvdata_unchecked() }
+        unsafe { self.drvdata_unchecked::<F>() }
     }
 }
 
@@ -258,18 +287,25 @@ impl Device<Bound> {
     ///
     /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before
     ///   the device is fully unbound.
-    /// - The type `T` must match the type of the `ForeignOwnable` previously stored by
-    ///   [`Device::set_drvdata`].
-    unsafe fn drvdata_unchecked<T: 'static>(&self) -> Pin<&T> {
+    /// - `F` must match the [`ForLt`] type previously stored by [`Device::set_drvdata`].
+    unsafe fn drvdata_unchecked<F: ForLt>(&self) -> Pin<&F::Of<'_>>
+    where
+        F::Of<'static>: 'static,
+    {
         // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
         let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
 
         // SAFETY:
-        // - By the safety requirements of this function, `ptr` comes from a previous call to
-        //   `into_foreign()`.
-        // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
-        //   in `into_foreign()`.
-        unsafe { Pin::<KBox<T>>::borrow(ptr.cast()) }
+        // - By the safety requirements of this function, `ptr` was stored by a previous call to
+        //   `set_drvdata()` via `KBox::into_raw()`.
+        // - `dev_get_drvdata()` guarantees to return the same pointer given to
+        //   `dev_set_drvdata()`.
+        let pinned: Pin<&F::Of<'static>> =
+            unsafe { Pin::<KBox<F::Of<'static>>>::borrow(ptr.cast()) };
+
+        // SAFETY: `ForLt` guarantees covariance, making it sound to shorten 'static to &self's
+        // lifetime via cast_ref().
+        unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) }
     }
 }
 
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 8f0e50729215..29a67b12c872 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -99,7 +99,10 @@
     device,
     of,
     prelude::*,
-    types::Opaque,
+    types::{
+        ForLt,
+        Opaque, //
+    },
     ThisModule, //
 };
 
@@ -112,14 +115,16 @@
 ///
 /// Implementors must guarantee that:
 /// - `DriverType` is `repr(C)`,
-/// - `DriverData` is the type of the driver's device private data.
+/// - `DriverData` is the [`ForLt`] encoding of the driver's device private data type.
 /// - `DriverType` embeds a valid `struct device_driver` at byte offset `DEVICE_DRIVER_OFFSET`.
+///
+/// [`ForLt`]: trait@ForLt
 pub unsafe trait DriverLayout {
     /// The specific driver type embedding a `struct device_driver`.
     type DriverType: Default;
 
-    /// The type of the driver's device private data.
-    type DriverData;
+    /// The [`ForLt`](trait@ForLt) encoding of the driver's device private data type.
+    type DriverData: ForLt;
 
     /// Byte offset of the embedded `struct device_driver` within `DriverType`.
     ///
@@ -193,7 +198,7 @@ extern "C" fn post_unbind_callback(dev: *mut bindings::device) {
         // be released after the driver's bus device private data is dropped.
         //
         // SAFETY: By the safety requirements of the `Driver` trait, `T::DriverData` is the
-        // driver's device private data type.
+        // ForLt encoding of the driver's device private data type.
         drop(unsafe { dev.drvdata_obtain::<T::DriverData>() });
     }
 
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 7b908f0c5a58..cde3dd7a6cc7 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -20,7 +20,10 @@
         ARef,
         AlwaysRefCounted, //
     },
-    types::Opaque, //
+    types::{
+        ForLt,
+        Opaque, //
+    }, //
 };
 
 use core::{
@@ -98,7 +101,7 @@ macro_rules! i2c_device_table {
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
 unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
     type DriverType = bindings::i2c_driver;
-    type DriverData = T;
+    type DriverData = ForLt!(T);
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
@@ -165,7 +168,7 @@ extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_
         from_result(|| {
             let data = T::probe(idev, info);
 
-            idev.as_ref().set_drvdata(data)?;
+            idev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
             Ok(0)
         })
     }
@@ -177,7 +180,7 @@ extern "C" fn remove_callback(idev: *mut bindings::i2c_client) {
         // SAFETY: `remove_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { idev.as_ref().drvdata_borrow::<T>() };
+        let data = unsafe { idev.as_ref().drvdata_borrow::<ForLt!(T)>() };
 
         T::unbind(idev, data);
     }
@@ -189,7 +192,7 @@ extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
         // SAFETY: `shutdown_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { idev.as_ref().drvdata_borrow::<T>() };
+        let data = unsafe { idev.as_ref().drvdata_borrow::<ForLt!(T)>() };
 
         T::shutdown(idev, data);
     }
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114..fe5148f41d8b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -19,7 +19,10 @@
     },
     prelude::*,
     str::CStr,
-    types::Opaque,
+    types::{
+        ForLt,
+        Opaque, //
+    },
     ThisModule, //
 };
 use core::{
@@ -64,7 +67,7 @@
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
 unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
     type DriverType = bindings::pci_driver;
-    type DriverData = T;
+    type DriverData = ForLt!(T);
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
@@ -115,7 +118,7 @@ extern "C" fn probe_callback(
         from_result(|| {
             let data = T::probe(pdev, info);
 
-            pdev.as_ref().set_drvdata(data)?;
+            pdev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
             Ok(0)
         })
     }
@@ -130,7 +133,7 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
         // SAFETY: `remove_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { pdev.as_ref().drvdata_borrow::<T>() };
+        let data = unsafe { pdev.as_ref().drvdata_borrow::<ForLt!(T)>() };
 
         T::unbind(pdev, data);
     }
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 8917d4ee499f..7ff69e3eea90 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -27,7 +27,10 @@
     },
     of,
     prelude::*,
-    types::Opaque,
+    types::{
+        ForLt,
+        Opaque, //
+    },
     ThisModule, //
 };
 
@@ -50,7 +53,7 @@
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
 unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
     type DriverType = bindings::platform_driver;
-    type DriverData = T;
+    type DriverData = ForLt!(T);
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
@@ -103,7 +106,7 @@ extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ff
         from_result(|| {
             let data = T::probe(pdev, info);
 
-            pdev.as_ref().set_drvdata(data)?;
+            pdev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
             Ok(0)
         })
     }
@@ -118,7 +121,7 @@ extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
         // SAFETY: `remove_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { pdev.as_ref().drvdata_borrow::<T>() };
+        let data = unsafe { pdev.as_ref().drvdata_borrow::<ForLt!(T)>() };
 
         T::unbind(pdev, data);
     }
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 9c17a672cd27..9b9d3ae41087 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -19,7 +19,10 @@
     },
     prelude::*,
     sync::aref::AlwaysRefCounted,
-    types::Opaque,
+    types::{
+        ForLt,
+        Opaque, //
+    },
     ThisModule, //
 };
 use core::{
@@ -41,7 +44,7 @@
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
 unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
     type DriverType = bindings::usb_driver;
-    type DriverData = T;
+    type DriverData = ForLt!(T);
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
@@ -93,7 +96,7 @@ extern "C" fn probe_callback(
             let data = T::probe(intf, id, info);
 
             let dev: &device::Device<device::CoreInternal> = intf.as_ref();
-            dev.set_drvdata(data)?;
+            dev.set_drvdata::<ForLt!(T)>(data)?;
             Ok(0)
         })
     }
@@ -110,7 +113,7 @@ extern "C" fn disconnect_callback(intf: *mut bindings::usb_interface) {
         // SAFETY: `disconnect_callback` is only ever called after a successful call to
         // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
         // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { dev.drvdata_borrow::<T>() };
+        let data = unsafe { dev.drvdata_borrow::<ForLt!(T)>() };
 
         T::disconnect(intf, data);
     }
-- 
2.54.0


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

* [PATCH 05/24] rust: driver: make Adapter trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (3 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 04/24] rust: device: generalize drvdata methods over ForLt Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 06/24] rust: pci: implement Sync for Device<Bound> Danilo Krummrich
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Parameterize the Adapter trait with a lifetime, changing the id_info(),
of_id_info() and acpi_id_info() methods to take &'a Device<Bound> and
return &'a Self::IdInfo instead of &'static.

This is needed for the ForLt conversion of bus driver traits. Once
Driver becomes lifetime-parameterized, its IdInfo associated type may
depend on the lifetime parameter. With Adapter<'a>, the impl can set
IdInfo = <F::Of<'a> as Driver<'a>>::IdInfo and the lifetime flows
through naturally, avoiding the need for transmute.

For the current non-lifetime-parameterized Driver trait, this is a no-op
type relaxation; IdInfo is 'static and &'static coerces to &'a.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/driver.rs   | 16 ++++++++++------
 rust/kernel/i2c.rs      | 10 +++++-----
 rust/kernel/platform.rs |  4 ++--
 3 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 29a67b12c872..91490040d77e 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -96,7 +96,11 @@
 
 use crate::{
     acpi,
-    device,
+    device::{
+        self,
+        Bound,
+        Device, //
+    },
     of,
     prelude::*,
     types::{
@@ -192,7 +196,7 @@ extern "C" fn post_unbind_callback(dev: *mut bindings::device) {
         // a `struct device`.
         //
         // INVARIANT: `dev` is valid for the duration of the `post_unbind_callback()`.
-        let dev = unsafe { &*dev.cast::<device::Device<device::CoreInternal>>() };
+        let dev = unsafe { &*dev.cast::<Device<device::CoreInternal>>() };
 
         // `remove()` has been completed at this point; devres resources are still valid and will
         // be released after the driver's bus device private data is dropped.
@@ -289,7 +293,7 @@ fn init(
 /// of a device and a driver.
 ///
 /// It provides bus independent functions for device / driver interactions.
-pub trait Adapter {
+pub trait Adapter<'a> {
     /// The type holding driver private data about each device id supported by the driver.
     type IdInfo: 'static;
 
@@ -299,7 +303,7 @@ pub trait Adapter {
     /// Returns the driver's private data from the matching entry in the [`acpi::IdTable`], if any.
     ///
     /// If this returns `None`, it means there is no match with an entry in the [`acpi::IdTable`].
-    fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
+    fn acpi_id_info(dev: &'a Device<Bound>) -> Option<&'a Self::IdInfo> {
         #[cfg(not(CONFIG_ACPI))]
         {
             let _ = dev;
@@ -333,7 +337,7 @@ fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
     /// Returns the driver's private data from the matching entry in the [`of::IdTable`], if any.
     ///
     /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`].
-    fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
+    fn of_id_info(dev: &'a Device<Bound>) -> Option<&'a Self::IdInfo> {
         #[cfg(not(CONFIG_OF))]
         {
             let _ = dev;
@@ -369,7 +373,7 @@ fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
     ///
     /// If this returns `None`, it means that there is no match in any of the ID tables directly
     /// associated with a [`device::Device`].
-    fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
+    fn id_info(dev: &'a Device<Bound>) -> Option<&'a Self::IdInfo> {
         let id = Self::acpi_id_info(dev);
         if id.is_some() {
             return id;
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index cde3dd7a6cc7..08d310aa9d6b 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -162,8 +162,8 @@ extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_
         // INVARIANT: `idev` is valid for the duration of `probe_callback()`.
         let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
 
-        let info =
-            Self::i2c_id_info(idev).or_else(|| <Self as driver::Adapter>::id_info(idev.as_ref()));
+        let info = Self::i2c_id_info(idev)
+            .or_else(|| <Self as driver::Adapter<'_>>::id_info(idev.as_ref()));
 
         from_result(|| {
             let data = T::probe(idev, info);
@@ -198,14 +198,14 @@ extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
     }
 
     /// The [`i2c::IdTable`] of the corresponding driver.
-    fn i2c_id_table() -> Option<IdTable<<Self as driver::Adapter>::IdInfo>> {
+    fn i2c_id_table() -> Option<IdTable<<Self as driver::Adapter<'static>>::IdInfo>> {
         T::I2C_ID_TABLE
     }
 
     /// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any.
     ///
     /// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`].
-    fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter>::IdInfo> {
+    fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter<'static>>::IdInfo> {
         let table = Self::i2c_id_table()?;
 
         // SAFETY:
@@ -225,7 +225,7 @@ fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter>::Id
     }
 }
 
-impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
+impl<'a, T: Driver + 'static> driver::Adapter<'a> for Adapter<T> {
     type IdInfo = T::IdInfo;
 
     fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 7ff69e3eea90..1585b6a1ac45 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -101,7 +101,7 @@ extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ff
         //
         // INVARIANT: `pdev` is valid for the duration of `probe_callback()`.
         let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
-        let info = <Self as driver::Adapter>::id_info(pdev.as_ref());
+        let info = <Self as driver::Adapter<'_>>::id_info(pdev.as_ref());
 
         from_result(|| {
             let data = T::probe(pdev, info);
@@ -127,7 +127,7 @@ extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
     }
 }
 
-impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
+impl<'a, T: Driver + 'static> driver::Adapter<'a> for Adapter<T> {
     type IdInfo = T::IdInfo;
 
     fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
-- 
2.54.0


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

* [PATCH 06/24] rust: pci: implement Sync for Device<Bound>
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (4 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 05/24] rust: driver: make Adapter trait lifetime-parameterized Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 23:52   ` Gary Guo
  2026-04-27 22:11 ` [PATCH 07/24] rust: platform: " Danilo Krummrich
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Implement Sync for Device<Bound> in addition to Device<Normal>. The
underlying struct pci_dev is the same; Bound is a zero-sized type-state
marker that does not affect thread safety.

This is needed for pci::Bar to hold &'a Device<Bound> (required for
Bar::into_devres()) while remaining Send.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/pci.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index fe5148f41d8b..6f82f2e6c74f 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -526,3 +526,7 @@ unsafe impl Send for Device {}
 // SAFETY: `Device` can be shared among threads because all methods of `Device`
 // (i.e. `Device<Normal>) are thread safe.
 unsafe impl Sync for Device {}
+
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct pci_dev` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}
-- 
2.54.0


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

* [PATCH 07/24] rust: platform: implement Sync for Device<Bound>
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (5 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 06/24] rust: pci: implement Sync for Device<Bound> Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 08/24] rust: auxiliary: " Danilo Krummrich
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Device<Bound> uses the same underlying struct platform_device as
Device<Normal>; Bound is a zero-sized type-state marker that does not
affect thread safety.

This is needed for drivers to store &'a platform::Device<Bound> in their
HRT private data while remaining Send.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/platform.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 1585b6a1ac45..ae648304eb5b 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -564,3 +564,7 @@ unsafe impl Send for Device {}
 // SAFETY: `Device` can be shared among threads because all methods of `Device`
 // (i.e. `Device<Normal>) are thread safe.
 unsafe impl Sync for Device {}
+
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct platform_device` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}
-- 
2.54.0


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

* [PATCH 08/24] rust: auxiliary: implement Sync for Device<Bound>
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (6 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 07/24] rust: platform: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 09/24] rust: usb: " Danilo Krummrich
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Device<Bound> uses the same underlying struct auxiliary_device as
Device<Normal>; Bound is a zero-sized type-state marker that does not
affect thread safety.

This is needed for drivers to store &'a auxiliary::Device<Bound> in
their HRT private data while remaining Send.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/auxiliary.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 5cd10b254baf..8a278ddb5b95 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -366,6 +366,10 @@ unsafe impl Send for Device {}
 // (i.e. `Device<Normal>) are thread safe.
 unsafe impl Sync for Device {}
 
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct auxiliary_device` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}
+
 /// Wrapper that stores a [`TypeId`] alongside the registration data for runtime type checking.
 #[repr(C)]
 #[pin_data]
-- 
2.54.0


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

* [PATCH 09/24] rust: usb: implement Sync for Device<Bound>
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (7 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 08/24] rust: auxiliary: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 10/24] rust: device: " Danilo Krummrich
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Device<Bound> uses the same underlying struct usb_device as
Device<Normal>; Bound is a zero-sized type-state marker that does not
affect thread safety.

This is needed for drivers to store &'a usb::Device<Bound> in their HRT
private data while remaining Send.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/usb.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 9b9d3ae41087..442e456fd2d3 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -467,6 +467,10 @@ unsafe impl Send for Device {}
 // allow any mutation through a shared reference.
 unsafe impl Sync for Device {}
 
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct usb_device` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}
+
 /// Declares a kernel module that exposes a single USB driver.
 ///
 /// # Examples
-- 
2.54.0


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

* [PATCH 10/24] rust: device: implement Sync for Device<Bound>
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (8 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 09/24] rust: usb: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 11/24] rust: pci: make Driver trait lifetime-parameterized Danilo Krummrich
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

The underlying `struct device` is the same for all device contexts;
`Bound` is a zero-sized type-state marker that does not affect thread
safety. Implement `Sync` for `Device<Bound>` with the same safety
argument as the existing `Device<Normal>` implementation.

This is needed for types that hold `&'a Device<Bound>`, such as
`io::mem::IoMem`, to be `Send`.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/device.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 09cbe8a438a9..b330b87178d3 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -503,6 +503,10 @@ unsafe impl Send for Device {}
 // synchronization in `struct device`.
 unsafe impl Sync for Device {}
 
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct device` is the same; `Bound` is a
+// zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<Bound> {}
+
 /// Marker trait for the context or scope of a bus specific device.
 ///
 /// [`DeviceContext`] is a marker trait for types representing the context of a bus specific
-- 
2.54.0


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

* [PATCH 11/24] rust: pci: make Driver trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (9 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 10/24] rust: device: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 12/24] rust: platform: " Danilo Krummrich
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make pci::Driver take a lifetime parameter 'a that ties device resources
to the binding scope.

Internally, Adapter<T: Driver> becomes Adapter<F: ForLt> with a HRTB
bound for<'a> F::Of<'a>: Driver<'a>; module_pci_driver! wraps the driver
type in ForLt!() so drivers don't have to.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/driver.rs       |  9 ++-
 drivers/gpu/nova-core/nova_core.rs    |  4 +-
 rust/kernel/pci.rs                    | 79 +++++++++++++++++++--------
 samples/rust/rust_dma.rs              |  9 ++-
 samples/rust/rust_driver_auxiliary.rs | 13 +++--
 samples/rust/rust_driver_pci.rs       | 11 ++--
 6 files changed, 86 insertions(+), 39 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 8fe484d357f6..815489dd92d0 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -50,7 +50,7 @@ pub(crate) struct NovaCore {
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <NovaCore as pci::Driver>::IdInfo,
+    <NovaCore as pci::Driver<'_>>::IdInfo,
     [
         // Modern NVIDIA GPUs will show up as either VGA or 3D controllers.
         (
@@ -72,11 +72,14 @@ pub(crate) struct NovaCore {
     ]
 );
 
-impl pci::Driver for NovaCore {
+impl<'a> pci::Driver<'a> for NovaCore {
     type IdInfo = ();
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 
-    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        pdev: &'a pci::Device<Core>,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         pin_init::pin_init_scope(move || {
             dev_dbg!(pdev, "Probe Nova Core GPU driver.\n");
 
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 04a1fa6b25f8..49c093a0cb42 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -7,6 +7,7 @@
     driver::Registration,
     pci,
     prelude::*,
+    types::ForLt,
     InPlaceModule, //
 };
 
@@ -46,8 +47,9 @@ fn drop(&mut self) {
 struct NovaCoreModule {
     // Fields are dropped in declaration order, so `_driver` is dropped first,
     // then `_debugfs_guard` clears `DEBUGFS_ROOT`.
+    #[allow(clippy::type_complexity)]
     #[pin]
-    _driver: Registration<pci::Adapter<driver::NovaCore>>,
+    _driver: Registration<pci::Adapter<ForLt!(driver::NovaCore)>>,
     _debugfs_guard: DebugfsRootGuard,
 }
 
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 6f82f2e6c74f..73d9fcff99c4 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -58,22 +58,34 @@
 };
 
 /// An adapter for the registration of PCI drivers.
-pub struct Adapter<T: Driver>(T);
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_pci_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
 
 // SAFETY:
 // - `bindings::pci_driver` is a C type declared as `repr(C)`.
-// - `T` is the type of the driver's device private data.
+// - `F::Of<'static>` is the stored type of the driver's device private data.
 // - `struct pci_driver` embeds a `struct device_driver`.
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
-unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+unsafe impl<F> driver::DriverLayout for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     type DriverType = bindings::pci_driver;
-    type DriverData = ForLt!(T);
+    type DriverData = F;
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
 // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
 // a preceding call to `register` has been successful.
-unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+unsafe impl<F> driver::RegistrationOps for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     unsafe fn register(
         pdrv: &Opaque<Self::DriverType>,
         name: &'static CStr,
@@ -84,7 +96,7 @@ unsafe fn register(
             (*pdrv.get()).name = name.as_char_ptr();
             (*pdrv.get()).probe = Some(Self::probe_callback);
             (*pdrv.get()).remove = Some(Self::remove_callback);
-            (*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
+            (*pdrv.get()).id_table = <F::Of<'static> as Driver<'static>>::ID_TABLE.as_ptr();
         }
 
         // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
@@ -99,7 +111,11 @@ unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
     }
 }
 
-impl<T: Driver + 'static> Adapter<T> {
+impl<F> Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     extern "C" fn probe_callback(
         pdev: *mut bindings::pci_dev,
         id: *const bindings::pci_device_id,
@@ -113,12 +129,12 @@ extern "C" fn probe_callback(
         // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct pci_device_id` and
         // does not add additional invariants, so it's safe to transmute.
         let id = unsafe { &*id.cast::<DeviceId>() };
-        let info = T::ID_TABLE.info(id.index());
 
         from_result(|| {
-            let data = T::probe(pdev, info);
+            let info = <F::Of<'_> as Driver<'_>>::ID_TABLE.info(id.index());
+            let data = <F::Of<'_> as Driver<'_>>::probe(pdev, info);
 
-            pdev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
+            pdev.as_ref().set_drvdata::<F>(data)?;
             Ok(0)
         })
     }
@@ -131,16 +147,18 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
         let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
 
         // SAFETY: `remove_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { pdev.as_ref().drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { pdev.as_ref().drvdata_borrow::<F>() };
 
-        T::unbind(pdev, data);
+        <F::Of<'_> as Driver<'_>>::unbind(pdev, data);
     }
 }
 
 /// Declares a kernel module that exposes a single PCI driver.
 ///
+/// The `type` field accepts a driver type, optionally with a lifetime placeholder `'_` for
+/// lifetime-parameterized drivers. The macro wraps it in [`ForLt!`] automatically.
+///
 /// # Examples
 ///
 ///```ignore
@@ -152,10 +170,16 @@ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
 ///     license: "GPL v2",
 /// }
 ///```
+///
+/// [`ForLt!`]: macro@ForLt
+/// [`ForLt`]: trait@ForLt
 #[macro_export]
 macro_rules! module_pci_driver {
-($($f:tt)*) => {
-    $crate::module_driver!(<T>, $crate::pci::Adapter<T>, { $($f)* });
+(type: $type:ty, $($rest:tt)*) => {
+    $crate::module_driver!(<T>, $crate::pci::Adapter<T>, {
+        type: $crate::types::ForLt!($type),
+        $($rest)*
+    });
 };
 }
 
@@ -261,6 +285,9 @@ macro_rules! pci_device_table {
 
 /// The PCI driver trait.
 ///
+/// Drivers implement this trait with a lifetime parameter `'a` that ties device resources to the
+/// device scope.
+///
 /// # Examples
 ///
 ///```
@@ -271,7 +298,7 @@ macro_rules! pci_device_table {
 /// kernel::pci_device_table!(
 ///     PCI_TABLE,
 ///     MODULE_PCI_TABLE,
-///     <MyDriver as pci::Driver>::IdInfo,
+///     <MyDriver as pci::Driver<'_>>::IdInfo,
 ///     [
 ///         (
 ///             pci::DeviceId::from_id(pci::Vendor::REDHAT, bindings::PCI_ANY_ID as u32),
@@ -280,21 +307,22 @@ macro_rules! pci_device_table {
 ///     ]
 /// );
 ///
-/// impl pci::Driver for MyDriver {
+/// impl<'a> pci::Driver<'a> for MyDriver {
 ///     type IdInfo = ();
 ///     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 ///
 ///     fn probe(
-///         _pdev: &pci::Device<Core>,
-///         _id_info: &Self::IdInfo,
-///     ) -> impl PinInit<Self, Error> {
+///         _pdev: &'a pci::Device<Core>,
+///         _id_info: &'a Self::IdInfo,
+///     ) -> impl PinInit<Self, Error> + 'a {
 ///         Err(ENODEV)
 ///     }
 /// }
 ///```
+///
 /// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
 /// `Adapter` documentation for an example.
-pub trait Driver: Send {
+pub trait Driver<'a>: Send {
     /// The type holding information about each device id supported by the driver.
     // TODO: Use `associated_type_defaults` once stabilized:
     //
@@ -310,7 +338,10 @@ pub trait Driver: Send {
     ///
     /// Called when a new pci device is added or discovered. Implementers should
     /// attempt to initialize the device here.
-    fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
+    fn probe(
+        dev: &'a Device<device::Core>,
+        id_info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a;
 
     /// PCI driver unbind.
     ///
@@ -322,7 +353,7 @@ pub trait Driver: Send {
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
-    fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
+    fn unbind(dev: &'a Device<device::Core>, this: Pin<&'a Self>) {
         let _ = (dev, this);
     }
 }
diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs
index 129bb4b39c04..8b6f8c923049 100644
--- a/samples/rust/rust_dma.rs
+++ b/samples/rust/rust_dma.rs
@@ -52,15 +52,18 @@ unsafe impl kernel::transmute::FromBytes for MyStruct {}
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <DmaSampleDriver as pci::Driver>::IdInfo,
+    <DmaSampleDriver as pci::Driver<'_>>::IdInfo,
     [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
 );
 
-impl pci::Driver for DmaSampleDriver {
+impl<'a> pci::Driver<'a> for DmaSampleDriver {
     type IdInfo = ();
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 
-    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        pdev: &'a pci::Device<Core>,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         pin_init::pin_init_scope(move || {
             dev_info!(pdev, "Probe DMA test driver.\n");
 
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index 319ef734c02b..f57b2b03adb6 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -14,6 +14,7 @@
     driver,
     pci,
     prelude::*,
+    types::ForLt,
     InPlaceModule, //
 };
 
@@ -59,16 +60,19 @@ struct ParentDriver {
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <ParentDriver as pci::Driver>::IdInfo,
+    <ParentDriver as pci::Driver<'_>>::IdInfo,
     [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
 );
 
-impl pci::Driver for ParentDriver {
+impl<'a> pci::Driver<'a> for ParentDriver {
     type IdInfo = ();
 
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 
-    fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        pdev: &'a pci::Device<Core>,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         Ok(Self {
             _reg0: auxiliary::Registration::new(
                 pdev.as_ref(),
@@ -116,7 +120,8 @@ fn connect(adev: &auxiliary::Device<Bound>) -> Result {
 #[pin_data]
 struct SampleModule {
     #[pin]
-    _pci_driver: driver::Registration<pci::Adapter<ParentDriver>>,
+    #[allow(clippy::type_complexity)]
+    _pci_driver: driver::Registration<pci::Adapter<ForLt!(ParentDriver)>>,
     #[pin]
     _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
 }
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 47d3e84fab63..2747beecb5fd 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -77,7 +77,7 @@ struct SampleDriver {
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <SampleDriver as pci::Driver>::IdInfo,
+    <SampleDriver as pci::Driver<'_>>::IdInfo,
     [(
         pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
         TestIndex::NO_EVENTFD
@@ -138,12 +138,15 @@ fn config_space(pdev: &pci::Device<Bound>) {
     }
 }
 
-impl pci::Driver for SampleDriver {
+impl<'a> pci::Driver<'a> for SampleDriver {
     type IdInfo = TestIndex;
 
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 
-    fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        pdev: &'a pci::Device<Core>,
+        info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         pin_init::pin_init_scope(move || {
             let vendor = pdev.vendor_id();
             dev_dbg!(
@@ -174,7 +177,7 @@ fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Er
         })
     }
 
-    fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
+    fn unbind(pdev: &'a pci::Device<Core>, this: Pin<&'a Self>) {
         if let Ok(bar) = this.bar.access(pdev.as_ref()) {
             // Reset pci-testdev by writing a new test index.
             bar.write_reg(regs::TEST::zeroed().with_index(this.index));
-- 
2.54.0


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

* [PATCH 12/24] rust: platform: make Driver trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (10 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 11/24] rust: pci: make Driver trait lifetime-parameterized Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 13/24] rust: auxiliary: " Danilo Krummrich
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make platform::Driver take a lifetime parameter 'a that ties device
resources to the binding scope.

Internally, Adapter<T: Driver> becomes Adapter<F: ForLt> with a bound
for<'a> F::Of<'a>: Driver<'a>; module_platform_driver! wraps the driver
type in ForLt!() so drivers don't have to.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/cpufreq/rcpufreq_dt.rs       | 10 +--
 drivers/gpu/drm/tyr/driver.rs        | 10 +--
 drivers/pwm/pwm_th1520.rs            | 10 +--
 rust/kernel/cpufreq.rs               |  8 +--
 rust/kernel/driver.rs                |  9 ++-
 rust/kernel/io/mem.rs                | 16 ++---
 rust/kernel/platform.rs              | 92 ++++++++++++++++++----------
 samples/rust/rust_debugfs.rs         | 10 +--
 samples/rust/rust_driver_platform.rs | 12 ++--
 samples/rust/rust_i2c_client.rs      | 12 ++--
 samples/rust/rust_soc.rs             | 12 ++--
 11 files changed, 116 insertions(+), 85 deletions(-)

diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs
index f17bf64c22e2..06c05f946829 100644
--- a/drivers/cpufreq/rcpufreq_dt.rs
+++ b/drivers/cpufreq/rcpufreq_dt.rs
@@ -195,18 +195,18 @@ fn register_em(policy: &mut cpufreq::Policy) {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <CPUFreqDTDriver as platform::Driver>::IdInfo,
+    <CPUFreqDTDriver as platform::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"operating-points-v2"), ())]
 );
 
-impl platform::Driver for CPUFreqDTDriver {
+impl<'a> platform::Driver<'a> for CPUFreqDTDriver {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        _id_info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        _id_info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
         Ok(Self {})
     }
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 279710b36a10..7cc47ec76863 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -82,21 +82,21 @@ fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <TyrPlatformDriverData as platform::Driver>::IdInfo,
+    <TyrPlatformDriverData as platform::Driver<'_>>::IdInfo,
     [
         (of::DeviceId::new(c"rockchip,rk3588-mali"), ()),
         (of::DeviceId::new(c"arm,mali-valhall-csf"), ())
     ]
 );
 
-impl platform::Driver for TyrPlatformDriverData {
+impl<'a> platform::Driver<'a> for TyrPlatformDriverData {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        _info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        _info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?;
         let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?;
         let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?;
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
index ddd44a5ce497..7139f3f4373d 100644
--- a/drivers/pwm/pwm_th1520.rs
+++ b/drivers/pwm/pwm_th1520.rs
@@ -310,18 +310,18 @@ fn drop(self: Pin<&mut Self>) {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <Th1520PwmPlatformDriver as platform::Driver>::IdInfo,
+    <Th1520PwmPlatformDriver as platform::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"thead,th1520-pwm"), ())]
 );
 
-impl platform::Driver for Th1520PwmPlatformDriver {
+impl<'a> platform::Driver<'a> for Th1520PwmPlatformDriver {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        _id_info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        _id_info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         let dev = pdev.as_ref();
         let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
 
diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index d8d26870bea2..ac59cdfd633c 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -886,14 +886,14 @@ fn register_em(_policy: &mut Policy) {
 ///     }
 /// }
 ///
-/// impl platform::Driver for SampleDriver {
+/// impl<'a> platform::Driver<'a> for SampleDriver {
 ///     type IdInfo = ();
 ///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
 ///
 ///     fn probe(
-///         pdev: &platform::Device<Core>,
-///         _id_info: Option<&Self::IdInfo>,
-///     ) -> impl PinInit<Self, Error> {
+///         pdev: &'a platform::Device<Core>,
+///         _id_info: Option<&'a Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> + 'a {
 ///         cpufreq::Registration::<SampleDriver>::new_foreign_owned(pdev.as_ref())?;
 ///         Ok(Self {})
 ///     }
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 91490040d77e..03974690406b 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -13,7 +13,7 @@
 //! The main driver interface is defined by a bus specific driver trait. For instance:
 //!
 //! ```ignore
-//! pub trait Driver: Send {
+//! pub trait Driver<'a>: Send {
 //!     /// The type holding information about each device ID supported by the driver.
 //!     type IdInfo: 'static;
 //!
@@ -24,10 +24,13 @@
 //!     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
 //!
 //!     /// Driver probe.
-//!     fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
+//!     fn probe(
+//!         dev: &'a Device<device::Core>,
+//!         id_info: &'a Self::IdInfo,
+//!     ) -> impl PinInit<Self, Error> + 'a;
 //!
 //!     /// Driver unbind (optional).
-//!     fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
+//!     fn unbind(dev: &'a Device<device::Core>, this: Pin<&'a Self>) {
 //!         let _ = (dev, this);
 //!     }
 //! }
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index 7dc78d547f7a..a483e59054e8 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -60,13 +60,13 @@ pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> S
     /// };
     /// struct SampleDriver;
     ///
-    /// impl platform::Driver for SampleDriver {
+    /// impl<'a> platform::Driver<'a> for SampleDriver {
     ///    # type IdInfo = ();
     ///
     ///    fn probe(
-    ///       pdev: &platform::Device<Core>,
-    ///       info: Option<&Self::IdInfo>,
-    ///    ) -> impl PinInit<Self, Error> {
+    ///       pdev: &'a platform::Device<Core>,
+    ///       info: Option<&'a Self::IdInfo>,
+    ///    ) -> impl PinInit<Self, Error> + 'a {
     ///       let offset = 0; // Some offset.
     ///
     ///       // If the size is known at compile time, use [`Self::iomap_sized`].
@@ -124,13 +124,13 @@ pub fn iomap_exclusive_sized<const SIZE: usize>(
     /// };
     /// struct SampleDriver;
     ///
-    /// impl platform::Driver for SampleDriver {
+    /// impl<'a> platform::Driver<'a> for SampleDriver {
     ///    # type IdInfo = ();
     ///
     ///    fn probe(
-    ///       pdev: &platform::Device<Core>,
-    ///       info: Option<&Self::IdInfo>,
-    ///    ) -> impl PinInit<Self, Error> {
+    ///       pdev: &'a platform::Device<Core>,
+    ///       info: Option<&'a Self::IdInfo>,
+    ///    ) -> impl PinInit<Self, Error> + 'a {
     ///       let offset = 0; // Some offset.
     ///
     ///       // Unlike [`Self::iomap_sized`], here the size of the memory region
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index ae648304eb5b..ec41886b0ba3 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -44,33 +44,45 @@
 };
 
 /// An adapter for the registration of platform drivers.
-pub struct Adapter<T: Driver>(T);
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_platform_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
 
 // SAFETY:
 // - `bindings::platform_driver` is a C type declared as `repr(C)`.
-// - `T` is the type of the driver's device private data.
+// - `F::Of<'static>` is the stored type of the driver's device private data.
 // - `struct platform_driver` embeds a `struct device_driver`.
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
-unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+unsafe impl<F> driver::DriverLayout for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     type DriverType = bindings::platform_driver;
-    type DriverData = ForLt!(T);
+    type DriverData = F;
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
 // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
 // a preceding call to `register` has been successful.
-unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+unsafe impl<F> driver::RegistrationOps for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     unsafe fn register(
         pdrv: &Opaque<Self::DriverType>,
         name: &'static CStr,
         module: &'static ThisModule,
     ) -> Result {
-        let of_table = match T::OF_ID_TABLE {
+        let of_table = match <F::Of<'static> as Driver<'static>>::OF_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
         };
 
-        let acpi_table = match T::ACPI_ID_TABLE {
+        let acpi_table = match <F::Of<'static> as Driver<'static>>::ACPI_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
         };
@@ -94,19 +106,23 @@ unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
     }
 }
 
-impl<T: Driver + 'static> Adapter<T> {
+impl<F> Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int {
         // SAFETY: The platform bus only ever calls the probe callback with a valid pointer to a
         // `struct platform_device`.
         //
         // INVARIANT: `pdev` is valid for the duration of `probe_callback()`.
         let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
-        let info = <Self as driver::Adapter<'_>>::id_info(pdev.as_ref());
 
         from_result(|| {
-            let data = T::probe(pdev, info);
+            let info = <Self as driver::Adapter<'_>>::id_info(pdev.as_ref());
+            let data = <F::Of<'_> as Driver<'_>>::probe(pdev, info);
 
-            pdev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
+            pdev.as_ref().set_drvdata::<F>(data)?;
             Ok(0)
         })
     }
@@ -119,28 +135,34 @@ extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
         let pdev = unsafe { &*pdev.cast::<Device<device::CoreInternal>>() };
 
         // SAFETY: `remove_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { pdev.as_ref().drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { pdev.as_ref().drvdata_borrow::<F>() };
 
-        T::unbind(pdev, data);
+        <F::Of<'_> as Driver<'_>>::unbind(pdev, data);
     }
 }
 
-impl<'a, T: Driver + 'static> driver::Adapter<'a> for Adapter<T> {
-    type IdInfo = T::IdInfo;
+impl<'a, F> driver::Adapter<'a> for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'b> F::Of<'b>: Driver<'b>,
+{
+    type IdInfo = <F::Of<'a> as Driver<'a>>::IdInfo;
 
     fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
-        T::OF_ID_TABLE
+        <F::Of<'a> as Driver<'a>>::OF_ID_TABLE
     }
 
     fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
-        T::ACPI_ID_TABLE
+        <F::Of<'a> as Driver<'a>>::ACPI_ID_TABLE
     }
 }
 
 /// Declares a kernel module that exposes a single platform driver.
 ///
+/// The `type` field accepts a driver type, optionally with a lifetime placeholder `'_` for
+/// lifetime-parameterized drivers. The macro wraps it in [`ForLt!`] automatically.
+///
 /// # Examples
 ///
 /// ```ignore
@@ -152,10 +174,16 @@ fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
 ///     license: "GPL v2",
 /// }
 /// ```
+///
+/// [`ForLt!`]: macro@ForLt
+/// [`ForLt`]: trait@ForLt
 #[macro_export]
 macro_rules! module_platform_driver {
-    ($($f:tt)*) => {
-        $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
+    (type: $type:ty, $($rest:tt)*) => {
+        $crate::module_driver!(<T>, $crate::platform::Adapter<T>, {
+            type: $crate::types::ForLt!($type),
+            $($rest)*
+        });
     };
 }
 
@@ -178,7 +206,7 @@ macro_rules! module_platform_driver {
 /// kernel::of_device_table!(
 ///     OF_TABLE,
 ///     MODULE_OF_TABLE,
-///     <MyDriver as platform::Driver>::IdInfo,
+///     <MyDriver as platform::Driver<'_>>::IdInfo,
 ///     [
 ///         (of::DeviceId::new(c"test,device"), ())
 ///     ]
@@ -187,26 +215,26 @@ macro_rules! module_platform_driver {
 /// kernel::acpi_device_table!(
 ///     ACPI_TABLE,
 ///     MODULE_ACPI_TABLE,
-///     <MyDriver as platform::Driver>::IdInfo,
+///     <MyDriver as platform::Driver<'_>>::IdInfo,
 ///     [
 ///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
 ///     ]
 /// );
 ///
-/// impl platform::Driver for MyDriver {
+/// impl<'a> platform::Driver<'a> for MyDriver {
 ///     type IdInfo = ();
 ///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 ///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 ///
 ///     fn probe(
-///         _pdev: &platform::Device<Core>,
-///         _id_info: Option<&Self::IdInfo>,
-///     ) -> impl PinInit<Self, Error> {
+///         _pdev: &'a platform::Device<Core>,
+///         _id_info: Option<&'a Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> + 'a {
 ///         Err(ENODEV)
 ///     }
 /// }
 ///```
-pub trait Driver: Send {
+pub trait Driver<'a>: Send {
     /// The type holding driver private data about each device id supported by the driver.
     // TODO: Use associated_type_defaults once stabilized:
     //
@@ -226,9 +254,9 @@ pub trait Driver: Send {
     /// Called when a new platform device is added or discovered.
     /// Implementers should attempt to initialize the device here.
     fn probe(
-        dev: &Device<device::Core>,
-        id_info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error>;
+        dev: &'a Device<device::Core>,
+        id_info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a;
 
     /// Platform driver unbind.
     ///
@@ -240,7 +268,7 @@ fn probe(
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
-    fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
+    fn unbind(dev: &'a Device<device::Core>, this: Pin<&'a Self>) {
         let _ = (dev, this);
     }
 }
diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs
index 0963efe19f93..0d88a9aaa957 100644
--- a/samples/rust/rust_debugfs.rs
+++ b/samples/rust/rust_debugfs.rs
@@ -111,19 +111,19 @@ fn from_str(s: &str) -> Result<Self> {
 kernel::acpi_device_table!(
     ACPI_TABLE,
     MODULE_ACPI_TABLE,
-    <RustDebugFs as platform::Driver>::IdInfo,
+    <RustDebugFs as platform::Driver<'_>>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
 );
 
-impl platform::Driver for RustDebugFs {
+impl<'a> platform::Driver<'a> for RustDebugFs {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        _info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        _info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         RustDebugFs::new(pdev).pin_chain(|this| {
             this.counter.store(91, Relaxed);
             {
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
index f2229d176fb9..cf9177bf3202 100644
--- a/samples/rust/rust_driver_platform.rs
+++ b/samples/rust/rust_driver_platform.rs
@@ -88,26 +88,26 @@ struct SampleDriver {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <SampleDriver as platform::Driver>::IdInfo,
+    <SampleDriver as platform::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"test,rust-device"), Info(42))]
 );
 
 kernel::acpi_device_table!(
     ACPI_TABLE,
     MODULE_ACPI_TABLE,
-    <SampleDriver as platform::Driver>::IdInfo,
+    <SampleDriver as platform::Driver<'_>>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), Info(0))]
 );
 
-impl platform::Driver for SampleDriver {
+impl<'a> platform::Driver<'a> for SampleDriver {
     type IdInfo = Info;
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         let dev = pdev.as_ref();
 
         dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs
index 8d2c12e535b0..342755df8b95 100644
--- a/samples/rust/rust_i2c_client.rs
+++ b/samples/rust/rust_i2c_client.rs
@@ -88,14 +88,14 @@ struct SampleDriver {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <SampleDriver as platform::Driver>::IdInfo,
+    <SampleDriver as platform::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"test,rust-device"), ())]
 );
 
 kernel::acpi_device_table!(
     ACPI_TABLE,
     MODULE_ACPI_TABLE,
-    <SampleDriver as platform::Driver>::IdInfo,
+    <SampleDriver as platform::Driver<'_>>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
 );
 
@@ -104,15 +104,15 @@ struct SampleDriver {
 const BOARD_INFO: i2c::I2cBoardInfo =
     i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR);
 
-impl platform::Driver for SampleDriver {
+impl<'a> platform::Driver<'a> for SampleDriver {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 
     fn probe(
-        pdev: &platform::Device<device::Core>,
-        _info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<device::Core>,
+        _info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         dev_info!(
             pdev.as_ref(),
             "Probe Rust I2C Client registration sample.\n"
diff --git a/samples/rust/rust_soc.rs b/samples/rust/rust_soc.rs
index 8079c1c48416..16f1e4bcfa36 100644
--- a/samples/rust/rust_soc.rs
+++ b/samples/rust/rust_soc.rs
@@ -24,26 +24,26 @@ struct SampleSocDriver {
 kernel::of_device_table!(
     OF_TABLE,
     MODULE_OF_TABLE,
-    <SampleSocDriver as platform::Driver>::IdInfo,
+    <SampleSocDriver as platform::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"test,rust-device"), ())]
 );
 
 kernel::acpi_device_table!(
     ACPI_TABLE,
     MODULE_ACPI_TABLE,
-    <SampleSocDriver as platform::Driver>::IdInfo,
+    <SampleSocDriver as platform::Driver<'_>>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
 );
 
-impl platform::Driver for SampleSocDriver {
+impl<'a> platform::Driver<'a> for SampleSocDriver {
     type IdInfo = ();
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 
     fn probe(
-        pdev: &platform::Device<Core>,
-        _info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        pdev: &'a platform::Device<Core>,
+        _info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         dev_dbg!(pdev, "Probe Rust SoC driver sample.\n");
 
         let pdev = pdev.into();
-- 
2.54.0


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

* [PATCH 13/24] rust: auxiliary: make Driver trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (11 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 12/24] rust: platform: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 14/24] rust: auxiliary: generalize Registration over ForLt Danilo Krummrich
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make auxiliary::Driver take a lifetime parameter 'a that ties device
resources to the binding scope.

Internally, Adapter<T: Driver> becomes Adapter<F: ForLt> with a bound
for<'a> F::Of<'a>: Driver<'a>; module_auxiliary_driver! wraps the driver
type in ForLt!() so drivers don't have to.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/drm/nova/driver.rs        |  9 ++--
 rust/kernel/auxiliary.rs              | 59 ++++++++++++++++++---------
 samples/rust/rust_driver_auxiliary.rs | 12 ++++--
 3 files changed, 54 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs
index b1af0a099551..183d0e679a0b 100644
--- a/drivers/gpu/drm/nova/driver.rs
+++ b/drivers/gpu/drm/nova/driver.rs
@@ -42,18 +42,21 @@ pub(crate) struct NovaData {
 kernel::auxiliary_device_table!(
     AUX_TABLE,
     MODULE_AUX_TABLE,
-    <NovaDriver as auxiliary::Driver>::IdInfo,
+    <NovaDriver as auxiliary::Driver<'_>>::IdInfo,
     [(
         auxiliary::DeviceId::new(NOVA_CORE_MODULE_NAME, AUXILIARY_NAME),
         ()
     )]
 );
 
-impl auxiliary::Driver for NovaDriver {
+impl<'a> auxiliary::Driver<'a> for NovaDriver {
     type IdInfo = ();
     const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
 
-    fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        adev: &'a auxiliary::Device<Core>,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         let data = try_pin_init!(NovaData { adev: adev.into() });
 
         let drm = drm::Device::<Self>::new(adev.as_ref(), data)?;
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 8a278ddb5b95..f593a21a16be 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -38,22 +38,34 @@
 };
 
 /// An adapter for the registration of auxiliary drivers.
-pub struct Adapter<T: Driver>(T);
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_auxiliary_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
 
 // SAFETY:
 // - `bindings::auxiliary_driver` is a C type declared as `repr(C)`.
-// - `T` is the type of the driver's device private data.
+// - `F::Of<'static>` is the stored type of the driver's device private data.
 // - `struct auxiliary_driver` embeds a `struct device_driver`.
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
-unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+unsafe impl<F> driver::DriverLayout for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     type DriverType = bindings::auxiliary_driver;
-    type DriverData = ForLt!(T);
+    type DriverData = F;
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
 // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
 // a preceding call to `register` has been successful.
-unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+unsafe impl<F> driver::RegistrationOps for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     unsafe fn register(
         adrv: &Opaque<Self::DriverType>,
         name: &'static CStr,
@@ -64,7 +76,7 @@ unsafe fn register(
             (*adrv.get()).name = name.as_char_ptr();
             (*adrv.get()).probe = Some(Self::probe_callback);
             (*adrv.get()).remove = Some(Self::remove_callback);
-            (*adrv.get()).id_table = T::ID_TABLE.as_ptr();
+            (*adrv.get()).id_table = <F::Of<'static> as Driver<'static>>::ID_TABLE.as_ptr();
         }
 
         // SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
@@ -79,7 +91,11 @@ unsafe fn unregister(adrv: &Opaque<Self::DriverType>) {
     }
 }
 
-impl<T: Driver + 'static> Adapter<T> {
+impl<F> Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     extern "C" fn probe_callback(
         adev: *mut bindings::auxiliary_device,
         id: *const bindings::auxiliary_device_id,
@@ -93,12 +109,12 @@ extern "C" fn probe_callback(
         // SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id`
         // and does not add additional invariants, so it's safe to transmute.
         let id = unsafe { &*id.cast::<DeviceId>() };
-        let info = T::ID_TABLE.info(id.index());
 
         from_result(|| {
-            let data = T::probe(adev, info);
+            let info = <F::Of<'_> as Driver<'_>>::ID_TABLE.info(id.index());
+            let data = <F::Of<'_> as Driver<'_>>::probe(adev, info);
 
-            adev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
+            adev.as_ref().set_drvdata::<F>(data)?;
             Ok(0)
         })
     }
@@ -111,19 +127,21 @@ extern "C" fn remove_callback(adev: *mut bindings::auxiliary_device) {
         let adev = unsafe { &*adev.cast::<Device<device::CoreInternal>>() };
 
         // SAFETY: `remove_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { adev.as_ref().drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { adev.as_ref().drvdata_borrow::<F>() };
 
-        T::unbind(adev, data);
+        <F::Of<'_> as Driver<'_>>::unbind(adev, data);
     }
 }
 
 /// Declares a kernel module that exposes a single auxiliary driver.
 #[macro_export]
 macro_rules! module_auxiliary_driver {
-    ($($f:tt)*) => {
-        $crate::module_driver!(<T>, $crate::auxiliary::Adapter<T>, { $($f)* });
+    (type: $type:ty, $($rest:tt)*) => {
+        $crate::module_driver!(<T>, $crate::auxiliary::Adapter<T>, {
+            type: $crate::types::ForLt!($type),
+            $($rest)*
+        });
     };
 }
 
@@ -195,7 +213,7 @@ macro_rules! auxiliary_device_table {
 /// The auxiliary driver trait.
 ///
 /// Drivers must implement this trait in order to get an auxiliary driver registered.
-pub trait Driver {
+pub trait Driver<'a>: Send {
     /// The type holding information about each device id supported by the driver.
     ///
     /// TODO: Use associated_type_defaults once stabilized:
@@ -209,7 +227,10 @@ pub trait Driver {
     /// Auxiliary driver probe.
     ///
     /// Called when an auxiliary device is matches a corresponding driver.
-    fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> impl PinInit<Self, Error>;
+    fn probe(
+        dev: &'a Device<device::Core>,
+        id_info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a;
 
     /// Auxiliary driver unbind.
     ///
@@ -221,7 +242,7 @@ pub trait Driver {
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
-    fn unbind(dev: &Device<device::Core>, this: Pin<&Self>) {
+    fn unbind(dev: &'a Device<device::Core>, this: Pin<&'a Self>) {
         let _ = (dev, this);
     }
 }
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index f57b2b03adb6..d35963ac7fa4 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -26,16 +26,19 @@
 kernel::auxiliary_device_table!(
     AUX_TABLE,
     MODULE_AUX_TABLE,
-    <AuxiliaryDriver as auxiliary::Driver>::IdInfo,
+    <AuxiliaryDriver as auxiliary::Driver<'_>>::IdInfo,
     [(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())]
 );
 
-impl auxiliary::Driver for AuxiliaryDriver {
+impl<'a> auxiliary::Driver<'a> for AuxiliaryDriver {
     type IdInfo = ();
 
     const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
 
-    fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
+    fn probe(
+        adev: &'a auxiliary::Device<Core>,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         dev_info!(
             adev,
             "Probing auxiliary driver for auxiliary device with id={}\n",
@@ -123,7 +126,8 @@ struct SampleModule {
     #[allow(clippy::type_complexity)]
     _pci_driver: driver::Registration<pci::Adapter<ForLt!(ParentDriver)>>,
     #[pin]
-    _aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
+    #[allow(clippy::type_complexity)]
+    _aux_driver: driver::Registration<auxiliary::Adapter<ForLt!(AuxiliaryDriver)>>,
 }
 
 impl InPlaceModule for SampleModule {
-- 
2.54.0


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

* [PATCH 14/24] rust: auxiliary: generalize Registration over ForLt
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (12 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 13/24] rust: auxiliary: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 15/24] samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data Danilo Krummrich
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Generalize Registration<T> to Registration<F: ForLt> and
Device::registration_data<F: ForLt>() to return Pin<&F::Of<'_>>.

The stored 'static lifetime is shortened to the borrow lifetime of &self
via ForLt::cast_ref; ForLt's covariance guarantee makes this sound.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/driver.rs       |  4 +-
 rust/kernel/auxiliary.rs              | 80 ++++++++++++++++++---------
 samples/rust/rust_driver_auxiliary.rs |  7 ++-
 3 files changed, 61 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 815489dd92d0..2a17fc99d9b6 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -21,6 +21,7 @@
         },
         Arc,
     },
+    types::ForLt,
 };
 
 use crate::gpu::Gpu;
@@ -32,7 +33,8 @@
 pub(crate) struct NovaCore {
     #[pin]
     pub(crate) gpu: Gpu,
-    _reg: Devres<auxiliary::Registration<()>>,
+    #[allow(clippy::type_complexity)]
+    _reg: Devres<auxiliary::Registration<ForLt!(())>>,
 }
 
 const BAR0_SIZE: usize = SZ_16M;
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index f593a21a16be..e27de4be4d87 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -287,12 +287,19 @@ pub fn parent(&self) -> &device::Device<device::Bound> {
 
     /// Returns a pinned reference to the registration data set by the registering (parent) driver.
     ///
-    /// Returns [`EINVAL`] if `T` does not match the type used by the parent driver when calling
+    /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The returned
+    /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via
+    /// [`ForLt::cast_ref`].
+    ///
+    /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
     /// [`Registration::new()`].
     ///
     /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
     /// registered by a C driver.
-    pub fn registration_data<T: 'static>(&self) -> Result<Pin<&T>> {
+    pub fn registration_data<F: ForLt>(&self) -> Result<Pin<&F::Of<'_>>>
+    where
+        F::Of<'static>: 'static,
+    {
         // SAFETY: By the type invariant, `self.as_raw()` is a valid `struct auxiliary_device`.
         let ptr = unsafe { (*self.as_raw()).registration_data_rust };
         if ptr.is_null() {
@@ -305,18 +312,23 @@ pub fn registration_data<T: 'static>(&self) -> Result<Pin<&T>> {
 
         // SAFETY: `ptr` is non-null and was set via `into_foreign()` in `Registration::new()`;
         // `RegistrationData` is `#[repr(C)]` with `type_id` at offset 0, so reading a `TypeId`
-        // at the start of the allocation is valid regardless of `T`.
+        // at the start of the allocation is valid regardless of `F`.
         let type_id = unsafe { ptr.cast::<TypeId>().read() };
-        if type_id != TypeId::of::<T>() {
+        if type_id != TypeId::of::<F::Of<'static>>() {
             return Err(EINVAL);
         }
 
-        // SAFETY: The `TypeId` check above confirms that the stored type is `T`; `ptr` remains
-        // valid until `Registration::drop()` calls `from_foreign()`.
-        let wrapper = unsafe { Pin::<KBox<RegistrationData<T>>>::borrow(ptr) };
+        // SAFETY: The `TypeId` check above confirms that the stored type matches
+        // `F::Of<'static>`; `ptr` remains valid until `Registration::drop()` calls
+        // `from_foreign()`.
+        let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) };
 
         // SAFETY: `data` is a structurally pinned field of `RegistrationData`.
-        Ok(unsafe { wrapper.map_unchecked(|w| &w.data) })
+        let pinned: Pin<&F::Of<'static>> = unsafe { wrapper.map_unchecked(|w| &w.data) };
+
+        // SAFETY: ForLt guarantees covariance, making it sound to shorten 'static to &self's
+        // lifetime via cast_ref.
+        Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) })
     }
 }
 
@@ -405,43 +417,54 @@ struct RegistrationData<T> {
 /// This type represents the registration of a [`struct auxiliary_device`]. When its parent device
 /// is unbound, the corresponding auxiliary device will be unregistered from the system.
 ///
-/// The type parameter `T` is the type of the registration data owned by the registering (parent)
-/// driver. It can be accessed by the auxiliary driver through
-/// [`Device::registration_data()`].
+/// The type parameter `F` is a [`ForLt`](trait@ForLt) encoding of the registration
+/// data type. For non-lifetime-parameterized types, use [`ForLt!(T)`](macro@ForLt).
+/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`].
 ///
 /// # Invariants
 ///
 /// `self.adev` always holds a valid pointer to an initialized and registered
 /// [`struct auxiliary_device`], and `registration_data` points to a valid
-/// `Pin<KBox<RegistrationData<T>>>`.
-pub struct Registration<T: 'static> {
+/// `Pin<KBox<RegistrationData<F::Of<'static>>>>`.
+pub struct Registration<F: ForLt>
+where
+    F::Of<'static>: 'static,
+{
     adev: NonNull<bindings::auxiliary_device>,
-    _data: PhantomData<T>,
+    _data: PhantomData<F>,
 }
 
-impl<T: Send + 'static> Registration<T> {
+impl<F: ForLt> Registration<F>
+where
+    F::Of<'static>: Send + 'static,
+{
     /// Create and register a new auxiliary device with the given registration data.
     ///
     /// The `data` is owned by the registration and can be accessed through the auxiliary device
     /// via [`Device::registration_data()`].
-    pub fn new<E>(
-        parent: &device::Device<device::Bound>,
+    pub fn new<'a, E>(
+        parent: &'a device::Device<device::Bound>,
         name: &CStr,
         id: u32,
         modname: &CStr,
-        data: impl PinInit<T, E>,
+        data: impl PinInit<F::Of<'a>, E>,
     ) -> Result<Devres<Self>>
     where
         Error: From<E>,
     {
         let data = KBox::pin_init::<Error>(
             try_pin_init!(RegistrationData {
-                type_id: TypeId::of::<T>(),
+                type_id: TypeId::of::<F::Of<'static>>(),
                 data <- data,
             }),
             GFP_KERNEL,
         )?;
 
+        // SAFETY: Lifetimes are erased and do not affect layout, so RegistrationData<F::Of<'a>>
+        // and RegistrationData<F::Of<'static>> have identical representation.
+        let data: Pin<KBox<RegistrationData<F::Of<'static>>>> =
+            unsafe { core::mem::transmute(data) };
+
         let boxed = KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)?;
         let adev = boxed.get();
 
@@ -470,9 +493,11 @@ pub fn new<E>(
         let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) };
         if ret != 0 {
             // SAFETY: `registration_data` was set above via `into_foreign()`.
-            let _ = unsafe {
-                Pin::<KBox<RegistrationData<T>>>::from_foreign((*adev).registration_data_rust)
-            };
+            drop(unsafe {
+                Pin::<KBox<RegistrationData<F::Of<'static>>>>::from_foreign(
+                    (*adev).registration_data_rust,
+                )
+            });
 
             // SAFETY: `adev` is guaranteed to be a valid pointer to a
             // `struct auxiliary_device`, which has been initialized.
@@ -494,7 +519,10 @@ pub fn new<E>(
     }
 }
 
-impl<T: 'static> Drop for Registration<T> {
+impl<F: ForLt> Drop for Registration<F>
+where
+    F::Of<'static>: 'static,
+{
     fn drop(&mut self) {
         // SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered
         // `struct auxiliary_device`.
@@ -502,7 +530,7 @@ fn drop(&mut self) {
 
         // SAFETY: `registration_data` was set in `new()` via `into_foreign()`.
         drop(unsafe {
-            Pin::<KBox<RegistrationData<T>>>::from_foreign(
+            Pin::<KBox<RegistrationData<F::Of<'static>>>>::from_foreign(
                 (*self.adev.as_ptr()).registration_data_rust,
             )
         });
@@ -516,7 +544,7 @@ fn drop(&mut self) {
 }
 
 // SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
-unsafe impl<T: Send> Send for Registration<T> {}
+unsafe impl<F: ForLt> Send for Registration<F> where F::Of<'static>: Send {}
 
 // SAFETY: `Registration` does not expose any methods or fields that need synchronization.
-unsafe impl<T: Send> Sync for Registration<T> {}
+unsafe impl<F: ForLt> Sync for Registration<F> where F::Of<'static>: Send {}
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index d35963ac7fa4..4ad619c5731e 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -55,9 +55,10 @@ struct Data {
     index: u32,
 }
 
+#[allow(clippy::type_complexity)]
 struct ParentDriver {
-    _reg0: Devres<auxiliary::Registration<Data>>,
-    _reg1: Devres<auxiliary::Registration<Data>>,
+    _reg0: Devres<auxiliary::Registration<ForLt!(Data)>>,
+    _reg1: Devres<auxiliary::Registration<ForLt!(Data)>>,
 }
 
 kernel::pci_device_table!(
@@ -100,7 +101,7 @@ fn connect(adev: &auxiliary::Device<Bound>) -> Result {
         let dev = adev.parent();
         let pdev: &pci::Device<Bound> = dev.try_into()?;
 
-        let data = adev.registration_data::<Data>()?;
+        let data = adev.registration_data::<ForLt!(Data)>()?;
 
         dev_info!(
             dev,
-- 
2.54.0


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

* [PATCH 15/24] samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (13 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 14/24] rust: auxiliary: generalize Registration over ForLt Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 16/24] rust: usb: make Driver trait lifetime-parameterized Danilo Krummrich
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make the Data struct lifetime-parameterized, storing a reference to the
parent pci::Device<Bound>. This demonstrates that registration data can
hold device resources tied to the parent driver's lifetime.

In connect(), retrieve the parent PCI device from the registration data
rather than casting through adev.parent().

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 samples/rust/rust_driver_auxiliary.rs | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index 4ad619c5731e..010ec2201a69 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -51,14 +51,15 @@ fn probe(
     }
 }
 
-struct Data {
+struct Data<'a> {
     index: u32,
+    parent: &'a pci::Device<Bound>,
 }
 
 #[allow(clippy::type_complexity)]
 struct ParentDriver {
-    _reg0: Devres<auxiliary::Registration<ForLt!(Data)>>,
-    _reg1: Devres<auxiliary::Registration<ForLt!(Data)>>,
+    _reg0: Devres<auxiliary::Registration<ForLt!(Data<'_>)>>,
+    _reg1: Devres<auxiliary::Registration<ForLt!(Data<'_>)>>,
 }
 
 kernel::pci_device_table!(
@@ -83,14 +84,20 @@ fn probe(
                 AUXILIARY_NAME,
                 0,
                 MODULE_NAME,
-                Data { index: 0 },
+                Data {
+                    index: 0,
+                    parent: pdev,
+                },
             )?,
             _reg1: auxiliary::Registration::new(
                 pdev.as_ref(),
                 AUXILIARY_NAME,
                 1,
                 MODULE_NAME,
-                Data { index: 1 },
+                Data {
+                    index: 1,
+                    parent: pdev,
+                },
             )?,
         })
     }
@@ -98,13 +105,11 @@ fn probe(
 
 impl ParentDriver {
     fn connect(adev: &auxiliary::Device<Bound>) -> Result {
-        let dev = adev.parent();
-        let pdev: &pci::Device<Bound> = dev.try_into()?;
-
-        let data = adev.registration_data::<ForLt!(Data)>()?;
+        let data = adev.registration_data::<ForLt!(Data<'_>)>()?;
+        let pdev = data.parent;
 
         dev_info!(
-            dev,
+            pdev,
             "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
             adev.id(),
             pdev.vendor_id(),
@@ -112,7 +117,7 @@ fn connect(adev: &auxiliary::Device<Bound>) -> Result {
         );
 
         dev_info!(
-            dev,
+            pdev,
             "Connected to auxiliary device with index {}.\n",
             data.index
         );
-- 
2.54.0


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

* [PATCH 16/24] rust: usb: make Driver trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (14 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 15/24] samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 17/24] rust: i2c: " Danilo Krummrich
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make usb::Driver take a lifetime parameter 'a that ties device resources
to the binding scope.

Internally, Adapter<T: Driver> becomes Adapter<F: ForLt> with a bound
for<'a> F::Of<'a>: Driver<'a>; module_usb_driver! wraps the driver type
in ForLt!() so drivers don't have to.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/usb.rs              | 84 ++++++++++++++++++++-------------
 samples/rust/rust_driver_usb.rs | 14 +++---
 2 files changed, 58 insertions(+), 40 deletions(-)

diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 442e456fd2d3..f519444cf8d0 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -35,22 +35,34 @@
 };
 
 /// An adapter for the registration of USB drivers.
-pub struct Adapter<T: Driver>(T);
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_usb_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
 
 // SAFETY:
 // - `bindings::usb_driver` is a C type declared as `repr(C)`.
-// - `T` is the type of the driver's device private data.
+// - `F::Of<'static>` is the stored type of the driver's device private data.
 // - `struct usb_driver` embeds a `struct device_driver`.
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
-unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+unsafe impl<F> driver::DriverLayout for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     type DriverType = bindings::usb_driver;
-    type DriverData = ForLt!(T);
+    type DriverData = F;
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
 // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
 // a preceding call to `register` has been successful.
-unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+unsafe impl<F> driver::RegistrationOps for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     unsafe fn register(
         udrv: &Opaque<Self::DriverType>,
         name: &'static CStr,
@@ -61,7 +73,7 @@ unsafe fn register(
             (*udrv.get()).name = name.as_char_ptr();
             (*udrv.get()).probe = Some(Self::probe_callback);
             (*udrv.get()).disconnect = Some(Self::disconnect_callback);
-            (*udrv.get()).id_table = T::ID_TABLE.as_ptr();
+            (*udrv.get()).id_table = <F::Of<'static> as Driver<'static>>::ID_TABLE.as_ptr();
         }
 
         // SAFETY: `udrv` is guaranteed to be a valid `DriverType`.
@@ -76,7 +88,11 @@ unsafe fn unregister(udrv: &Opaque<Self::DriverType>) {
     }
 }
 
-impl<T: Driver + 'static> Adapter<T> {
+impl<F> Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     extern "C" fn probe_callback(
         intf: *mut bindings::usb_interface,
         id: *const bindings::usb_device_id,
@@ -87,16 +103,16 @@ extern "C" fn probe_callback(
         // INVARIANT: `intf` is valid for the duration of `probe_callback()`.
         let intf = unsafe { &*intf.cast::<Interface<device::CoreInternal>>() };
 
-        from_result(|| {
-            // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct usb_device_id` and
-            // does not add additional invariants, so it's safe to transmute.
-            let id = unsafe { &*id.cast::<DeviceId>() };
+        // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct usb_device_id` and
+        // does not add additional invariants, so it's safe to transmute.
+        let id = unsafe { &*id.cast::<DeviceId>() };
 
-            let info = T::ID_TABLE.info(id.index());
-            let data = T::probe(intf, id, info);
+        from_result(|| {
+            let info = <F::Of<'_> as Driver<'_>>::ID_TABLE.info(id.index());
+            let data = <F::Of<'_> as Driver<'_>>::probe(intf, id, info);
 
             let dev: &device::Device<device::CoreInternal> = intf.as_ref();
-            dev.set_drvdata::<ForLt!(T)>(data)?;
+            dev.set_drvdata::<F>(data)?;
             Ok(0)
         })
     }
@@ -111,11 +127,10 @@ extern "C" fn disconnect_callback(intf: *mut bindings::usb_interface) {
         let dev: &device::Device<device::CoreInternal> = intf.as_ref();
 
         // SAFETY: `disconnect_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { dev.drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { dev.drvdata_borrow::<F>() };
 
-        T::disconnect(intf, data);
+        <F::Of<'_> as Driver<'_>>::disconnect(intf, data);
     }
 }
 
@@ -281,29 +296,29 @@ macro_rules! usb_device_table {
 /// kernel::usb_device_table!(
 ///     USB_TABLE,
 ///     MODULE_USB_TABLE,
-///     <MyDriver as usb::Driver>::IdInfo,
+///     <MyDriver as usb::Driver<'_>>::IdInfo,
 ///     [
 ///         (usb::DeviceId::from_id(0x1234, 0x5678), ()),
 ///         (usb::DeviceId::from_id(0xabcd, 0xef01), ()),
 ///     ]
 /// );
 ///
-/// impl usb::Driver for MyDriver {
+/// impl<'a> usb::Driver<'a> for MyDriver {
 ///     type IdInfo = ();
 ///     const ID_TABLE: usb::IdTable<Self::IdInfo> = &USB_TABLE;
 ///
 ///     fn probe(
-///         _interface: &usb::Interface<Core>,
-///         _id: &usb::DeviceId,
-///         _info: &Self::IdInfo,
-///     ) -> impl PinInit<Self, Error> {
+///         _interface: &'a usb::Interface<Core>,
+///         _id: &'a usb::DeviceId,
+///         _info: &'a Self::IdInfo,
+///     ) -> impl PinInit<Self, Error> + 'a {
 ///         Err(ENODEV)
 ///     }
 ///
-///     fn disconnect(_interface: &usb::Interface<Core>, _data: Pin<&Self>) {}
+///     fn disconnect(_interface: &'a usb::Interface<Core>, _data: Pin<&'a Self>) {}
 /// }
 ///```
-pub trait Driver {
+pub trait Driver<'a> {
     /// The type holding information about each one of the device ids supported by the driver.
     type IdInfo: 'static;
 
@@ -315,15 +330,15 @@ pub trait Driver {
     /// Called when a new USB interface is bound to this driver.
     /// Implementers should attempt to initialize the interface here.
     fn probe(
-        interface: &Interface<device::Core>,
-        id: &DeviceId,
-        id_info: &Self::IdInfo,
-    ) -> impl PinInit<Self, Error>;
+        interface: &'a Interface<device::Core>,
+        id: &'a DeviceId,
+        id_info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a;
 
     /// USB driver disconnect.
     ///
     /// Called when the USB interface is about to be unbound from this driver.
-    fn disconnect(interface: &Interface<device::Core>, data: Pin<&Self>);
+    fn disconnect(interface: &'a Interface<device::Core>, data: Pin<&'a Self>);
 }
 
 /// A USB interface.
@@ -486,7 +501,10 @@ unsafe impl Sync for Device<device::Bound> {}
 /// ```
 #[macro_export]
 macro_rules! module_usb_driver {
-    ($($f:tt)*) => {
-        $crate::module_driver!(<T>, $crate::usb::Adapter<T>, { $($f)* });
+    (type: $type:ty, $($rest:tt)*) => {
+        $crate::module_driver!(<T>, $crate::usb::Adapter<T>, {
+            type: $crate::types::ForLt!($type),
+            $($rest)*
+        });
     }
 }
diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs
index ab72e99e1274..6f3e5db9f35d 100644
--- a/samples/rust/rust_driver_usb.rs
+++ b/samples/rust/rust_driver_usb.rs
@@ -20,26 +20,26 @@ struct SampleDriver {
 kernel::usb_device_table!(
     USB_TABLE,
     MODULE_USB_TABLE,
-    <SampleDriver as usb::Driver>::IdInfo,
+    <SampleDriver as usb::Driver<'_>>::IdInfo,
     [(usb::DeviceId::from_id(0x1234, 0x5678), ()),]
 );
 
-impl usb::Driver for SampleDriver {
+impl<'a> usb::Driver<'a> for SampleDriver {
     type IdInfo = ();
     const ID_TABLE: usb::IdTable<Self::IdInfo> = &USB_TABLE;
 
     fn probe(
-        intf: &usb::Interface<Core>,
-        _id: &usb::DeviceId,
-        _info: &Self::IdInfo,
-    ) -> impl PinInit<Self, Error> {
+        intf: &'a usb::Interface<Core>,
+        _id: &'a usb::DeviceId,
+        _info: &'a Self::IdInfo,
+    ) -> impl PinInit<Self, Error> + 'a {
         let dev: &device::Device<Core> = intf.as_ref();
         dev_info!(dev, "Rust USB driver sample probed\n");
 
         Ok(Self { _intf: intf.into() })
     }
 
-    fn disconnect(intf: &usb::Interface<Core>, _data: Pin<&Self>) {
+    fn disconnect(intf: &'a usb::Interface<Core>, _data: Pin<&'a Self>) {
         let dev: &device::Device<Core> = intf.as_ref();
         dev_info!(dev, "Rust USB driver sample disconnected\n");
     }
-- 
2.54.0


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

* [PATCH 17/24] rust: i2c: make Driver trait lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (15 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 16/24] rust: usb: make Driver trait lifetime-parameterized Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 18/24] rust: pci: make Bar lifetime-parameterized Danilo Krummrich
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Make i2c::Driver take a lifetime parameter 'a that ties device resources
to the binding scope.

Internally, Adapter<T: Driver> becomes Adapter<F: ForLt> with a bound
for<'a> F::Of<'a>: Driver<'a>; module_i2c_driver! wraps the driver type
in ForLt!() so drivers don't have to.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/i2c.rs              | 116 +++++++++++++++++++-------------
 samples/rust/rust_driver_i2c.rs |  18 ++---
 2 files changed, 78 insertions(+), 56 deletions(-)

diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 08d310aa9d6b..4464146d6c4d 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -92,43 +92,57 @@ macro_rules! i2c_device_table {
 }
 
 /// An adapter for the registration of I2C drivers.
-pub struct Adapter<T: Driver>(T);
+///
+/// `F` is a [`ForLt`](trait@ForLt) type that maps lifetimes to the driver's device
+/// private data type, i.e. `F::Of<'a>` is the driver struct parameterized by `'a`. The macro
+/// `module_i2c_driver!` generates this automatically via `ForLt!()`.
+pub struct Adapter<F>(PhantomData<F>);
 
 // SAFETY:
 // - `bindings::i2c_driver` is a C type declared as `repr(C)`.
-// - `T` is the type of the driver's device private data.
+// - `F::Of<'static>` is the stored type of the driver's device private data.
 // - `struct i2c_driver` embeds a `struct device_driver`.
 // - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
-unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+unsafe impl<F> driver::DriverLayout for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     type DriverType = bindings::i2c_driver;
-    type DriverData = ForLt!(T);
+    type DriverData = F;
     const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
 }
 
 // SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
 // a preceding call to `register` has been successful.
-unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+unsafe impl<F> driver::RegistrationOps for Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     unsafe fn register(
         idrv: &Opaque<Self::DriverType>,
         name: &'static CStr,
         module: &'static ThisModule,
     ) -> Result {
         build_assert!(
-            T::ACPI_ID_TABLE.is_some() || T::OF_ID_TABLE.is_some() || T::I2C_ID_TABLE.is_some(),
+            <F::Of<'static> as Driver<'static>>::ACPI_ID_TABLE.is_some()
+                || <F::Of<'static> as Driver<'static>>::OF_ID_TABLE.is_some()
+                || <F::Of<'static> as Driver<'static>>::I2C_ID_TABLE.is_some(),
             "At least one of ACPI/OF/Legacy tables must be present when registering an i2c driver"
         );
 
-        let i2c_table = match T::I2C_ID_TABLE {
+        let i2c_table = match <F::Of<'static> as Driver<'static>>::I2C_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
         };
 
-        let of_table = match T::OF_ID_TABLE {
+        let of_table = match <F::Of<'static> as Driver<'static>>::OF_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
         };
 
-        let acpi_table = match T::ACPI_ID_TABLE {
+        let acpi_table = match <F::Of<'static> as Driver<'static>>::ACPI_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
         };
@@ -154,7 +168,11 @@ unsafe fn unregister(idrv: &Opaque<Self::DriverType>) {
     }
 }
 
-impl<T: Driver + 'static> Adapter<T> {
+impl<F> Adapter<F>
+where
+    F: ForLt + 'static,
+    for<'a> F::Of<'a>: Driver<'a>,
+{
     extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_int {
         // SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a
         // `struct i2c_client`.
@@ -162,13 +180,12 @@ extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_
         // INVARIANT: `idev` is valid for the duration of `probe_callback()`.
         let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
 
-        let info = Self::i2c_id_info(idev)
-            .or_else(|| <Self as driver::Adapter<'_>>::id_info(idev.as_ref()));
-
         from_result(|| {
-            let data = T::probe(idev, info);
+            let info = Self::i2c_id_info(idev)
+                .or_else(|| <Self as driver::Adapter<'_>>::id_info(idev.as_ref()));
+            let data = <F::Of<'_> as Driver<'_>>::probe(idev, info);
 
-            idev.as_ref().set_drvdata::<ForLt!(T)>(data)?;
+            idev.as_ref().set_drvdata::<F>(data)?;
             Ok(0)
         })
     }
@@ -178,11 +195,10 @@ extern "C" fn remove_callback(idev: *mut bindings::i2c_client) {
         let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
 
         // SAFETY: `remove_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { idev.as_ref().drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { idev.as_ref().drvdata_borrow::<F>() };
 
-        T::unbind(idev, data);
+        <F::Of<'_> as Driver<'_>>::unbind(idev, data);
     }
 
     extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
@@ -190,23 +206,22 @@ extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
         let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
 
         // SAFETY: `shutdown_callback` is only ever called after a successful call to
-        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
-        // and stored a `Pin<KBox<T>>`.
-        let data = unsafe { idev.as_ref().drvdata_borrow::<ForLt!(T)>() };
+        // `probe_callback`, hence it's guaranteed that drvdata has been set.
+        let data = unsafe { idev.as_ref().drvdata_borrow::<F>() };
 
-        T::shutdown(idev, data);
+        <F::Of<'_> as Driver<'_>>::shutdown(idev, data);
     }
 
     /// The [`i2c::IdTable`] of the corresponding driver.
-    fn i2c_id_table() -> Option<IdTable<<Self as driver::Adapter<'static>>::IdInfo>> {
-        T::I2C_ID_TABLE
+    fn i2c_id_table<'a>() -> Option<IdTable<<F::Of<'a> as Driver<'a>>::IdInfo>> {
+        <F::Of<'a> as Driver<'a>>::I2C_ID_TABLE
     }
 
     /// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any.
     ///
     /// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`].
-    fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter<'static>>::IdInfo> {
-        let table = Self::i2c_id_table()?;
+    fn i2c_id_info<'a>(dev: &I2cClient) -> Option<&'a <F::Of<'a> as Driver<'a>>::IdInfo> {
+        let table = Self::i2c_id_table::<'a>()?;
 
         // SAFETY:
         // - `table` has static lifetime, hence it's valid for reads
@@ -225,15 +240,19 @@ fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter<'sta
     }
 }
 
-impl<'a, T: Driver + 'static> driver::Adapter<'a> for Adapter<T> {
-    type IdInfo = T::IdInfo;
+impl<'a, F> driver::Adapter<'a> for Adapter<F>
+where
+    F: ForLt + 'static,
+    F::Of<'a>: Driver<'a>,
+{
+    type IdInfo = <F::Of<'a> as Driver<'a>>::IdInfo;
 
     fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
-        T::OF_ID_TABLE
+        <F::Of<'a> as Driver<'a>>::OF_ID_TABLE
     }
 
     fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
-        T::ACPI_ID_TABLE
+        <F::Of<'a> as Driver<'a>>::ACPI_ID_TABLE
     }
 }
 
@@ -252,8 +271,11 @@ fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
 /// ```
 #[macro_export]
 macro_rules! module_i2c_driver {
-    ($($f:tt)*) => {
-        $crate::module_driver!(<T>, $crate::i2c::Adapter<T>, { $($f)* });
+    (type: $type:ty, $($rest:tt)*) => {
+        $crate::module_driver!(<T>, $crate::i2c::Adapter<T>, {
+            type: $crate::types::ForLt!($type),
+            $($rest)*
+        });
     };
 }
 
@@ -271,7 +293,7 @@ macro_rules! module_i2c_driver {
 /// kernel::acpi_device_table!(
 ///     ACPI_TABLE,
 ///     MODULE_ACPI_TABLE,
-///     <MyDriver as i2c::Driver>::IdInfo,
+///     <MyDriver as i2c::Driver<'_>>::IdInfo,
 ///     [
 ///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
 ///     ]
@@ -280,7 +302,7 @@ macro_rules! module_i2c_driver {
 /// kernel::i2c_device_table!(
 ///     I2C_TABLE,
 ///     MODULE_I2C_TABLE,
-///     <MyDriver as i2c::Driver>::IdInfo,
+///     <MyDriver as i2c::Driver<'_>>::IdInfo,
 ///     [
 ///          (i2c::DeviceId::new(c"rust_driver_i2c"), ())
 ///     ]
@@ -289,30 +311,30 @@ macro_rules! module_i2c_driver {
 /// kernel::of_device_table!(
 ///     OF_TABLE,
 ///     MODULE_OF_TABLE,
-///     <MyDriver as i2c::Driver>::IdInfo,
+///     <MyDriver as i2c::Driver<'_>>::IdInfo,
 ///     [
 ///         (of::DeviceId::new(c"test,device"), ())
 ///     ]
 /// );
 ///
-/// impl i2c::Driver for MyDriver {
+/// impl<'a> i2c::Driver<'a> for MyDriver {
 ///     type IdInfo = ();
 ///     const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
 ///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 ///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 ///
 ///     fn probe(
-///         _idev: &i2c::I2cClient<Core>,
-///         _id_info: Option<&Self::IdInfo>,
-///     ) -> impl PinInit<Self, Error> {
+///         _idev: &'a i2c::I2cClient<Core>,
+///         _id_info: Option<&'a Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> + 'a {
 ///         Err(ENODEV)
 ///     }
 ///
-///     fn shutdown(_idev: &i2c::I2cClient<Core>, this: Pin<&Self>) {
+///     fn shutdown(_idev: &'a i2c::I2cClient<Core>, _this: Pin<&'a Self>) {
 ///     }
 /// }
 ///```
-pub trait Driver: Send {
+pub trait Driver<'a>: Send {
     /// The type holding information about each device id supported by the driver.
     // TODO: Use `associated_type_defaults` once stabilized:
     //
@@ -335,9 +357,9 @@ pub trait Driver: Send {
     /// Called when a new i2c client is added or discovered.
     /// Implementers should attempt to initialize the client here.
     fn probe(
-        dev: &I2cClient<device::Core>,
-        id_info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error>;
+        dev: &'a I2cClient<device::Core>,
+        id_info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a;
 
     /// I2C driver shutdown.
     ///
@@ -350,7 +372,7 @@ fn probe(
     /// This callback is distinct from final resource cleanup, as the driver instance remains valid
     /// after it returns. Any deallocation or teardown of driver-owned resources should instead be
     /// handled in `Self::drop`.
-    fn shutdown(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
+    fn shutdown(dev: &'a I2cClient<device::Core>, this: Pin<&'a Self>) {
         let _ = (dev, this);
     }
 
@@ -364,7 +386,7 @@ fn shutdown(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
     /// operations to gracefully tear down the device.
     ///
     /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
-    fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
+    fn unbind(dev: &'a I2cClient<device::Core>, this: Pin<&'a Self>) {
         let _ = (dev, this);
     }
 }
diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs
index 6be79f9e9fb5..f86c1cf7c786 100644
--- a/samples/rust/rust_driver_i2c.rs
+++ b/samples/rust/rust_driver_i2c.rs
@@ -15,25 +15,25 @@
 kernel::acpi_device_table! {
     ACPI_TABLE,
     MODULE_ACPI_TABLE,
-    <SampleDriver as i2c::Driver>::IdInfo,
+    <SampleDriver as i2c::Driver<'_>>::IdInfo,
     [(acpi::DeviceId::new(c"LNUXBEEF"), 0)]
 }
 
 kernel::i2c_device_table! {
     I2C_TABLE,
     MODULE_I2C_TABLE,
-    <SampleDriver as i2c::Driver>::IdInfo,
+    <SampleDriver as i2c::Driver<'_>>::IdInfo,
     [(i2c::DeviceId::new(c"rust_driver_i2c"), 0)]
 }
 
 kernel::of_device_table! {
     OF_TABLE,
     MODULE_OF_TABLE,
-    <SampleDriver as i2c::Driver>::IdInfo,
+    <SampleDriver as i2c::Driver<'_>>::IdInfo,
     [(of::DeviceId::new(c"test,rust_driver_i2c"), 0)]
 }
 
-impl i2c::Driver for SampleDriver {
+impl<'a> i2c::Driver<'a> for SampleDriver {
     type IdInfo = u32;
 
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
@@ -41,9 +41,9 @@ impl i2c::Driver for SampleDriver {
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 
     fn probe(
-        idev: &i2c::I2cClient<Core>,
-        info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+        idev: &'a i2c::I2cClient<Core>,
+        info: Option<&'a Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'a {
         let dev = idev.as_ref();
 
         dev_info!(dev, "Probe Rust I2C driver sample.\n");
@@ -55,11 +55,11 @@ fn probe(
         Ok(Self)
     }
 
-    fn shutdown(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
+    fn shutdown(idev: &'a i2c::I2cClient<Core>, _this: Pin<&'a Self>) {
         dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n");
     }
 
-    fn unbind(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
+    fn unbind(idev: &'a i2c::I2cClient<Core>, _this: Pin<&'a Self>) {
         dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n");
     }
 }
-- 
2.54.0


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

* [PATCH 18/24] rust: pci: make Bar lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (16 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 17/24] rust: i2c: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 19/24] rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized Danilo Krummrich
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Convert pci::Bar<SIZE> to pci::Bar<'a, SIZE>, storing &'a Device<Bound>
to tie the BAR mapping lifetime to the device.

iomap_region_sized() now returns Result<Bar<'a, SIZE>> directly instead
of impl PinInit<Devres<Bar<SIZE>>, Error>.

Add Bar::into_devres() to consume the bar and register it as a
device-managed resource, returning Devres<Bar<'static, SIZE>>. The
lifetime is erased to 'static because Devres guarantees the bar does not
actually outlive the device -- access is revoked on unbind.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/driver.rs |  7 +++--
 rust/kernel/devres.rs           |  2 +-
 rust/kernel/pci/io.rs           | 50 ++++++++++++++++++---------------
 samples/rust/rust_driver_pci.rs |  5 ++--
 4 files changed, 35 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 2a17fc99d9b6..149a20748e86 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -47,7 +47,7 @@ pub(crate) struct NovaCore {
 // DMA addresses. These systems should be quite rare.
 const GPU_DMA_BITS: u32 = 47;
 
-pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
+pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>;
 
 kernel::pci_device_table!(
     PCI_TABLE,
@@ -93,8 +93,9 @@ fn probe(
             // other threads of execution.
             unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
 
-            let bar = Arc::pin_init(
-                pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0"),
+            let bar = Arc::new(
+                pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0")?
+                    .into_devres()?,
                 GFP_KERNEL,
             )?;
 
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 7baabcdb1ad3..6f3c58355d10 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -305,7 +305,7 @@ pub fn device(&self) -> &Device {
     ///     pci, //
     /// };
     ///
-    /// fn from_core(dev: &pci::Device<Core>, devres: Devres<pci::Bar<0x4>>) -> Result {
+    /// fn from_core(dev: &pci::Device<Core>, devres: Devres<pci::Bar<'_, 0x4>>) -> Result {
     ///     let bar = devres.access(dev.as_ref())?;
     ///
     ///     let _ = bar.read32(0x0);
diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
index ae78676c927f..6116c55412bc 100644
--- a/rust/kernel/pci/io.rs
+++ b/rust/kernel/pci/io.rs
@@ -14,8 +14,7 @@
         Mmio,
         MmioRaw, //
     },
-    prelude::*,
-    sync::aref::ARef, //
+    prelude::*, //
 };
 use core::{
     marker::PhantomData,
@@ -146,14 +145,14 @@ impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
 ///
 /// `Bar` always holds an `IoRaw` instance that holds a valid pointer to the start of the I/O
 /// memory mapped PCI BAR and its size.
-pub struct Bar<const SIZE: usize = 0> {
-    pdev: ARef<Device>,
+pub struct Bar<'a, const SIZE: usize = 0> {
+    pdev: &'a Device<device::Bound>,
     io: MmioRaw<SIZE>,
     num: i32,
 }
 
-impl<const SIZE: usize> Bar<SIZE> {
-    pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
+impl<'a, const SIZE: usize> Bar<'a, SIZE> {
+    pub(super) fn new(pdev: &'a Device<device::Bound>, num: u32, name: &CStr) -> Result<Self> {
         let len = pdev.resource_len(num)?;
         if len == 0 {
             return Err(ENOMEM);
@@ -196,11 +195,7 @@ pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
             }
         };
 
-        Ok(Bar {
-            pdev: pdev.into(),
-            io,
-            num,
-        })
+        Ok(Bar { pdev, io, num })
     }
 
     /// # Safety
@@ -219,11 +214,24 @@ unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
 
     fn release(&self) {
         // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
-        unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) };
+        unsafe { Self::do_release(self.pdev, self.io.addr(), self.num) };
+    }
+
+    /// Consume the `Bar` and register it as a device-managed resource.
+    ///
+    /// The returned `Devres<Bar<'static, SIZE>>` can outlive the original lifetime `'a`. Access
+    /// to the BAR is revoked when the device is unbound.
+    pub fn into_devres(self) -> Result<Devres<Bar<'static, SIZE>>> {
+        // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `Bar` does not
+        // actually outlive the device -- access is revoked and the resource is released when the
+        // device is unbound.
+        let bar: Bar<'static, SIZE> = unsafe { core::mem::transmute(self) };
+        let pdev = bar.pdev;
+        Devres::new(pdev.as_ref(), bar)
     }
 }
 
-impl Bar {
+impl Bar<'_> {
     #[inline]
     pub(super) fn index_is_valid(index: u32) -> bool {
         // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
@@ -231,13 +239,13 @@ pub(super) fn index_is_valid(index: u32) -> bool {
     }
 }
 
-impl<const SIZE: usize> Drop for Bar<SIZE> {
+impl<const SIZE: usize> Drop for Bar<'_, SIZE> {
     fn drop(&mut self) {
         self.release();
     }
 }
 
-impl<const SIZE: usize> Deref for Bar<SIZE> {
+impl<const SIZE: usize> Deref for Bar<'_, SIZE> {
     type Target = Mmio<SIZE>;
 
     fn deref(&self) -> &Self::Target {
@@ -252,17 +260,13 @@ impl Device<device::Bound> {
     pub fn iomap_region_sized<'a, const SIZE: usize>(
         &'a self,
         bar: u32,
-        name: &'a CStr,
-    ) -> impl PinInit<Devres<Bar<SIZE>>, Error> + 'a {
-        Devres::new(self.as_ref(), Bar::<SIZE>::new(self, bar, name))
+        name: &CStr,
+    ) -> Result<Bar<'a, SIZE>> {
+        Bar::new(self, bar, name)
     }
 
     /// Maps an entire PCI BAR after performing a region-request on it.
-    pub fn iomap_region<'a>(
-        &'a self,
-        bar: u32,
-        name: &'a CStr,
-    ) -> impl PinInit<Devres<Bar>, Error> + 'a {
+    pub fn iomap_region<'a>(&'a self, bar: u32, name: &CStr) -> Result<Bar<'a>> {
         self.iomap_region_sized::<0>(bar, name)
     }
 
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 2747beecb5fd..38d639731229 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -45,7 +45,7 @@ mod regs {
     pub(super) const END: usize = 0x10;
 }
 
-type Bar0 = pci::Bar<{ regs::END }>;
+type Bar0 = pci::Bar<'static, { regs::END }>;
 
 #[derive(Copy, Clone, Debug)]
 struct TestIndex(u8);
@@ -160,7 +160,8 @@ fn probe(
             pdev.set_master();
 
             Ok(try_pin_init!(Self {
-                bar <- pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci"),
+                bar: pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")?
+                    .into_devres()?,
                 index: *info,
                 _: {
                     let bar = bar.access(pdev.as_ref())?;
-- 
2.54.0


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

* [PATCH 19/24] rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (17 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 18/24] rust: pci: make Bar lifetime-parameterized Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH 20/24] samples: rust: rust_driver_pci: use HRT lifetime for Bar Danilo Krummrich
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Add a lifetime parameter to IoMem<'a, SIZE> and ExclusiveIoMem<'a,
SIZE>, storing a &'a Device<Bound> reference to tie the mapping to the
device's lifetime.

This mirrors the pci::Bar<'a, SIZE> design and enables drivers to hold
I/O memory mappings directly in their HRT private data, tied to the
device lifetime.

IoRequest::iomap_* methods now return the mapping directly instead of
wrapping it in Devres. Callers that need device-managed revocation can
call the new into_devres() method.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/drm/tyr/driver.rs |   4 +-
 drivers/pwm/pwm_th1520.rs     |   4 +-
 rust/kernel/io/mem.rs         | 102 +++++++++++++++++-----------------
 3 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 7cc47ec76863..eaa84efdfdf7 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -37,7 +37,7 @@
     regs, //
 };
 
-pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>;
+pub(crate) type IoMem = kernel::io::mem::IoMem<'static, SZ_2M>;
 
 pub(crate) struct TyrDrmDriver;
 
@@ -109,7 +109,7 @@ fn probe(
         let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
 
         let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
-        let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?;
+        let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?.into_devres()?, GFP_KERNEL)?;
 
         issue_soft_reset(pdev.as_ref(), &iomem)?;
         gpu::l2_power_on(pdev.as_ref(), &iomem)?;
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
index 7139f3f4373d..a45fe359c371 100644
--- a/drivers/pwm/pwm_th1520.rs
+++ b/drivers/pwm/pwm_th1520.rs
@@ -92,7 +92,7 @@ struct Th1520WfHw {
 #[pin_data(PinnedDrop)]
 struct Th1520PwmDriverData {
     #[pin]
-    iomem: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>,
+    iomem: devres::Devres<IoMem<'static, TH1520_PWM_REG_SIZE>>,
     clk: Clk,
 }
 
@@ -351,7 +351,7 @@ fn probe(
             dev,
             TH1520_MAX_PWM_NUM,
             try_pin_init!(Th1520PwmDriverData {
-                iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>(),
+                iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>()?.into_devres(),
                 clk <- clk,
             }),
         )?;
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index a483e59054e8..12b773bc994d 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -73,22 +73,19 @@ pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> S
     ///       //
     ///       // No runtime checks will apply when reading and writing.
     ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
-    ///       let iomem = request.iomap_sized::<42>();
-    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
-    ///
-    ///       let io = iomem.access(pdev.as_ref())?;
+    ///       let iomem = request.iomap_sized::<42>()?;
     ///
     ///       // Read and write a 32-bit value at `offset`.
-    ///       let data = io.read32(offset);
+    ///       let data = iomem.read32(offset);
     ///
-    ///       io.write32(data, offset);
+    ///       iomem.write32(data, offset);
     ///
     ///       # Ok(SampleDriver)
     ///     }
     /// }
     /// ```
-    pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
-        IoMem::new(self)
+    pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> {
+        IoMem::ioremap(self.device, self.resource)
     }
 
     /// Same as [`Self::iomap_sized`] but with exclusive access to the
@@ -97,10 +94,8 @@ pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>,
     /// This uses the [`ioremap()`] C API.
     ///
     /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
-    pub fn iomap_exclusive_sized<const SIZE: usize>(
-        self,
-    ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
-        ExclusiveIoMem::new(self)
+    pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> {
+        ExclusiveIoMem::ioremap(self.device, self.resource)
     }
 
     /// Maps an [`IoRequest`] where the size is not known at compile time,
@@ -138,27 +133,24 @@ pub fn iomap_exclusive_sized<const SIZE: usize>(
     ///       // family of functions should be used, leading to runtime checks on every
     ///       // access.
     ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
-    ///       let iomem = request.iomap();
-    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
-    ///
-    ///       let io = iomem.access(pdev.as_ref())?;
+    ///       let iomem = request.iomap()?;
     ///
-    ///       let data = io.try_read32(offset)?;
+    ///       let data = iomem.try_read32(offset)?;
     ///
-    ///       io.try_write32(data, offset)?;
+    ///       iomem.try_write32(data, offset)?;
     ///
     ///       # Ok(SampleDriver)
     ///     }
     /// }
     /// ```
-    pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
-        Self::iomap_sized::<0>(self)
+    pub fn iomap(self) -> Result<IoMem<'a>> {
+        self.iomap_sized::<0>()
     }
 
     /// Same as [`Self::iomap`] but with exclusive access to the underlying
     /// region.
-    pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
-        Self::iomap_exclusive_sized::<0>(self)
+    pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> {
+        self.iomap_exclusive_sized::<0>()
     }
 }
 
@@ -167,9 +159,9 @@ pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> +
 /// # Invariants
 ///
 /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
-pub struct ExclusiveIoMem<const SIZE: usize> {
+pub struct ExclusiveIoMem<'a, const SIZE: usize> {
     /// The underlying `IoMem` instance.
-    iomem: IoMem<SIZE>,
+    iomem: IoMem<'a, SIZE>,
 
     /// The region abstraction. This represents exclusive access to the
     /// range represented by the underlying `iomem`.
@@ -178,9 +170,9 @@ pub struct ExclusiveIoMem<const SIZE: usize> {
     _region: Region,
 }
 
-impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
+impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
     /// Creates a new `ExclusiveIoMem` instance.
-    fn ioremap(resource: &Resource) -> Result<Self> {
+    fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
         let start = resource.start();
         let size = resource.size();
         let name = resource.name().unwrap_or_default();
@@ -194,26 +186,29 @@ fn ioremap(resource: &Resource) -> Result<Self> {
             )
             .ok_or(EBUSY)?;
 
-        let iomem = IoMem::ioremap(resource)?;
+        let iomem = IoMem::ioremap(dev, resource)?;
 
-        let iomem = ExclusiveIoMem {
+        Ok(ExclusiveIoMem {
             iomem,
             _region: region,
-        };
-
-        Ok(iomem)
+        })
     }
 
-    /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
-    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
-        let dev = io_request.device;
-        let res = io_request.resource;
-
-        Devres::new(dev, Self::ioremap(res))
+    /// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
+    ///
+    /// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime
+    /// `'a`. Access to the I/O memory is revoked when the device is unbound.
+    pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
+        // SAFETY: Casting to `'static` is sound because `Devres` guarantees the
+        // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the
+        // resource is released when the device is unbound.
+        let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
+        let dev = iomem.iomem.dev;
+        Devres::new(dev, iomem)
     }
 }
 
-impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
+impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
     type Target = Mmio<SIZE>;
 
     fn deref(&self) -> &Self::Target {
@@ -230,12 +225,13 @@ fn deref(&self) -> &Self::Target {
 ///
 /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
 /// start of the I/O memory mapped region.
-pub struct IoMem<const SIZE: usize = 0> {
+pub struct IoMem<'a, const SIZE: usize = 0> {
+    dev: &'a Device<Bound>,
     io: MmioRaw<SIZE>,
 }
 
-impl<const SIZE: usize> IoMem<SIZE> {
-    fn ioremap(resource: &Resource) -> Result<Self> {
+impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
+    fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
         // Note: Some ioremap() implementations use types that depend on the CPU
         // word width rather than the bus address width.
         //
@@ -267,28 +263,32 @@ fn ioremap(resource: &Resource) -> Result<Self> {
         }
 
         let io = MmioRaw::new(addr as usize, size)?;
-        let io = IoMem { io };
 
-        Ok(io)
+        Ok(IoMem { dev, io })
     }
 
-    /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
-    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
-        let dev = io_request.device;
-        let res = io_request.resource;
-
-        Devres::new(dev, Self::ioremap(res))
+    /// Consume the `IoMem` and register it as a device-managed resource.
+    ///
+    /// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original lifetime `'a`. Access
+    /// to the I/O memory is revoked when the device is unbound.
+    pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> {
+        // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not
+        // actually outlive the device -- access is revoked and the resource is released when the
+        // device is unbound.
+        let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
+        let dev = iomem.dev;
+        Devres::new(dev, iomem)
     }
 }
 
-impl<const SIZE: usize> Drop for IoMem<SIZE> {
+impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
     fn drop(&mut self) {
         // SAFETY: Safe as by the invariant of `Io`.
         unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
     }
 }
 
-impl<const SIZE: usize> Deref for IoMem<SIZE> {
+impl<const SIZE: usize> Deref for IoMem<'_, SIZE> {
     type Target = Mmio<SIZE>;
 
     fn deref(&self) -> &Self::Target {
-- 
2.54.0


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

* [PATCH 20/24] samples: rust: rust_driver_pci: use HRT lifetime for Bar
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (18 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 19/24] rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH REF 21/24] gpu: nova-core: " Danilo Krummrich
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Convert the sample driver to SampleDriver<'a>, taking advantage of the
lifetime-parameterized Driver trait.

The driver struct holds &'a pci::Device directly instead of
ARef<pci::Device>, and pci::Bar<'a> directly instead of
Devres<pci::Bar>. This removes PinnedDrop, pin_init_scope, and runtime
revocation checks on BAR access.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 samples/rust/rust_driver_pci.rs | 89 +++++++++++++++------------------
 1 file changed, 39 insertions(+), 50 deletions(-)

diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 38d639731229..7f763f43a38d 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -9,7 +9,6 @@
         Bound,
         Core, //
     },
-    devres::Devres,
     io::{
         register,
         register::Array,
@@ -17,8 +16,7 @@
     },
     num::Bounded,
     pci,
-    prelude::*,
-    sync::aref::ARef, //
+    prelude::*, //
 };
 
 mod regs {
@@ -45,7 +43,7 @@ mod regs {
     pub(super) const END: usize = 0x10;
 }
 
-type Bar0 = pci::Bar<'static, { regs::END }>;
+type Bar0<'a> = pci::Bar<'a, { regs::END }>;
 
 #[derive(Copy, Clone, Debug)]
 struct TestIndex(u8);
@@ -66,26 +64,24 @@ impl TestIndex {
     const NO_EVENTFD: Self = Self(0);
 }
 
-#[pin_data(PinnedDrop)]
-struct SampleDriver {
-    pdev: ARef<pci::Device>,
-    #[pin]
-    bar: Devres<Bar0>,
+struct SampleDriver<'a> {
+    pdev: &'a pci::Device,
+    bar: Bar0<'a>,
     index: TestIndex,
 }
 
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <SampleDriver as pci::Driver<'_>>::IdInfo,
+    <SampleDriver<'_> as pci::Driver<'_>>::IdInfo,
     [(
         pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
         TestIndex::NO_EVENTFD
     )]
 );
 
-impl SampleDriver {
-    fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
+impl SampleDriver<'_> {
+    fn testdev(index: &TestIndex, bar: &Bar0<'_>) -> Result<u32> {
         // Select the test.
         bar.write_reg(regs::TEST::zeroed().with_index(*index));
 
@@ -138,7 +134,7 @@ fn config_space(pdev: &pci::Device<Bound>) {
     }
 }
 
-impl<'a> pci::Driver<'a> for SampleDriver {
+impl<'a> pci::Driver<'a> for SampleDriver<'a> {
     type IdInfo = TestIndex;
 
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
@@ -147,54 +143,47 @@ fn probe(
         pdev: &'a pci::Device<Core>,
         info: &'a Self::IdInfo,
     ) -> impl PinInit<Self, Error> + 'a {
-        pin_init::pin_init_scope(move || {
-            let vendor = pdev.vendor_id();
-            dev_dbg!(
-                pdev,
-                "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
-                vendor,
-                pdev.device_id()
-            );
-
-            pdev.enable_device_mem()?;
-            pdev.set_master();
-
-            Ok(try_pin_init!(Self {
-                bar: pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")?
-                    .into_devres()?,
-                index: *info,
-                _: {
-                    let bar = bar.access(pdev.as_ref())?;
-
-                    dev_info!(
-                        pdev,
-                        "pci-testdev data-match count: {}\n",
-                        Self::testdev(info, bar)?
-                    );
-                    Self::config_space(pdev);
-                },
-                pdev: pdev.into(),
-            }))
+        let vendor = pdev.vendor_id();
+        dev_dbg!(
+            pdev,
+            "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
+            vendor,
+            pdev.device_id()
+        );
+
+        pdev.enable_device_mem()?;
+        pdev.set_master();
+
+        let bar = pdev.iomap_region_sized::<{ regs::END }>(0, c"rust_driver_pci")?;
+
+        dev_info!(
+            pdev,
+            "pci-testdev data-match count: {}\n",
+            Self::testdev(info, &bar)?
+        );
+        Self::config_space(pdev);
+
+        Ok(Self {
+            pdev,
+            bar,
+            index: *info,
         })
     }
 
-    fn unbind(pdev: &'a pci::Device<Core>, this: Pin<&'a Self>) {
-        if let Ok(bar) = this.bar.access(pdev.as_ref()) {
-            // Reset pci-testdev by writing a new test index.
-            bar.write_reg(regs::TEST::zeroed().with_index(this.index));
-        }
+    fn unbind(_pdev: &'a pci::Device<Core>, this: Pin<&'a Self>) {
+        this.bar
+            .write_reg(regs::TEST::zeroed().with_index(this.index));
     }
 }
 
-#[pinned_drop]
-impl PinnedDrop for SampleDriver {
-    fn drop(self: Pin<&mut Self>) {
+impl Drop for SampleDriver<'_> {
+    fn drop(&mut self) {
         dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n");
     }
 }
 
 kernel::module_pci_driver! {
-    type: SampleDriver,
+    type: SampleDriver<'_>,
     name: "rust_driver_pci",
     authors: ["Danilo Krummrich"],
     description: "Rust PCI driver",
-- 
2.54.0


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

* [PATCH REF 21/24] gpu: nova-core: use HRT lifetime for Bar
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (19 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH 20/24] samples: rust: rust_driver_pci: use HRT lifetime for Bar Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH REF 22/24] gpu: nova-core: unregister sysmem flush page from Drop Danilo Krummrich
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Take advantage of the lifetime-parameterized pci::Bar<'a> to hold the
BAR mapping directly in NovaCore<'a>, and pass a borrowed reference to
Gpu<'a>.

This eliminates the Arc<Devres<Bar0>> indirection, removes runtime
revocation checks for BAR access, and simplifies Gpu::unbind().

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/driver.rs    | 40 ++++++++++++++----------------
 drivers/gpu/nova-core/gpu.rs       | 27 ++++++++------------
 drivers/gpu/nova-core/nova_core.rs |  2 +-
 3 files changed, 30 insertions(+), 39 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 149a20748e86..ec9cecb30f63 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -14,12 +14,9 @@
     },
     prelude::*,
     sizes::SZ_16M,
-    sync::{
-        atomic::{
-            Atomic,
-            Relaxed, //
-        },
-        Arc,
+    sync::atomic::{
+        Atomic,
+        Relaxed, //
     },
     types::ForLt,
 };
@@ -30,9 +27,10 @@
 static AUXILIARY_ID_COUNTER: Atomic<u32> = Atomic::new(0);
 
 #[pin_data]
-pub(crate) struct NovaCore {
+pub(crate) struct NovaCore<'a> {
     #[pin]
-    pub(crate) gpu: Gpu,
+    pub(crate) gpu: Gpu<'a>,
+    bar: pci::Bar<'a, BAR0_SIZE>,
     #[allow(clippy::type_complexity)]
     _reg: Devres<auxiliary::Registration<ForLt!(())>>,
 }
@@ -47,12 +45,12 @@ pub(crate) struct NovaCore {
 // DMA addresses. These systems should be quite rare.
 const GPU_DMA_BITS: u32 = 47;
 
-pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>;
+pub(crate) type Bar0 = kernel::io::Mmio<BAR0_SIZE>;
 
 kernel::pci_device_table!(
     PCI_TABLE,
     MODULE_PCI_TABLE,
-    <NovaCore as pci::Driver<'_>>::IdInfo,
+    <NovaCore<'_> as pci::Driver<'_>>::IdInfo,
     [
         // Modern NVIDIA GPUs will show up as either VGA or 3D controllers.
         (
@@ -74,7 +72,7 @@ pub(crate) struct NovaCore {
     ]
 );
 
-impl<'a> pci::Driver<'a> for NovaCore {
+impl<'a> pci::Driver<'a> for NovaCore<'a> {
     type IdInfo = ();
     const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
 
@@ -93,14 +91,14 @@ fn probe(
             // other threads of execution.
             unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
 
-            let bar = Arc::new(
-                pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0")?
-                    .into_devres()?,
-                GFP_KERNEL,
-            )?;
-
-            Ok(try_pin_init!(Self {
-                gpu <- Gpu::new(pdev, bar.clone(), bar.access(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.
+                //
+                // SAFETY: `bar` is initialized before this expression is evaluated
+                // (`try_pin_init!()` initializes fields in declaration order), lives at a pinned
+                // stable address, and is dropped after `gpu` (struct field drop order).
+                gpu <- Gpu::new(pdev, unsafe { &*core::ptr::from_ref(bar) }),
                 _reg: auxiliary::Registration::new(
                     pdev.as_ref(),
                     c"nova-drm",
@@ -114,7 +112,7 @@ fn probe(
         })
     }
 
-    fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
-        this.gpu.unbind(pdev.as_ref());
+    fn unbind(_pdev: &'a pci::Device<Core>, this: Pin<&'a Self>) {
+        this.gpu.unbind();
     }
 }
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 0f6fe9a1b955..922197f2aeef 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -2,13 +2,11 @@
 
 use kernel::{
     device,
-    devres::Devres,
     fmt,
     io::Io,
     num::Bounded,
     pci,
-    prelude::*,
-    sync::Arc, //
+    prelude::*, //
 };
 
 use crate::{
@@ -224,10 +222,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 /// Structure holding the resources required to operate the GPU.
 #[pin_data]
-pub(crate) struct Gpu {
+pub(crate) struct Gpu<'a> {
     spec: Spec,
-    /// MMIO mapping of PCI BAR 0
-    bar: Arc<Devres<Bar0>>,
+    /// MMIO mapping of PCI BAR 0.
+    bar: &'a Bar0,
     /// System memory page required for flushing all pending GPU-side memory writes done through
     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
     sysmem_flush: SysmemFlush,
@@ -240,10 +238,9 @@ pub(crate) struct Gpu {
     gsp: Gsp,
 }
 
-impl Gpu {
-    pub(crate) fn new<'a>(
+impl<'a> Gpu<'a> {
+    pub(crate) fn new(
         pdev: &'a pci::Device<device::Bound>,
-        devres_bar: Arc<Devres<Bar0>>,
         bar: &'a Bar0,
     ) -> impl PinInit<Self, Error> + 'a {
         try_pin_init!(Self {
@@ -257,6 +254,8 @@ pub(crate) fn new<'a>(
                     .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
             },
 
+            bar,
+
             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
 
             gsp_falcon: Falcon::new(
@@ -270,19 +269,13 @@ pub(crate) fn new<'a>(
             gsp <- Gsp::new(pdev),
 
             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
-
-            bar: devres_bar,
         })
     }
 
     /// Called when the corresponding [`Device`](device::Device) is unbound.
     ///
     /// Note: This method must only be called from `Driver::unbind`.
-    pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
-        kernel::warn_on!(self
-            .bar
-            .access(dev)
-            .inspect(|bar| self.sysmem_flush.unregister(bar))
-            .is_err());
+    pub(crate) fn unbind(&self) {
+        self.sysmem_flush.unregister(self.bar);
     }
 }
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 49c093a0cb42..ed5eb39c8201 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -49,7 +49,7 @@ struct NovaCoreModule {
     // then `_debugfs_guard` clears `DEBUGFS_ROOT`.
     #[allow(clippy::type_complexity)]
     #[pin]
-    _driver: Registration<pci::Adapter<ForLt!(driver::NovaCore)>>,
+    _driver: Registration<pci::Adapter<ForLt!(driver::NovaCore<'_>)>>,
     _debugfs_guard: DebugfsRootGuard,
 }
 
-- 
2.54.0


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

* [PATCH REF 22/24] gpu: nova-core: unregister sysmem flush page from Drop
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (20 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH REF 21/24] gpu: nova-core: " Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH REF 23/24] gpu: nova-core: replace ARef<Device> with &'a Device in SysmemFlush Danilo Krummrich
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich,
	Eliot Courtney

Now that SysmemFlush can borrow the Bar via HRT lifetime, store a &'a
Bar0 reference and implement Drop to automatically unregister the
sysmem flush page. This removes the need for manual unregister() calls
and the Gpu::unbind() method.

Reported-by: Eliot Courtney <ecourtney@nvidia.com>
Closes: https://lore.kernel.org/all/20260409-fix-systemflush-v1-1-a1d6c968f17c@nvidia.com/
Fixes: 6554ad65b589 ("gpu: nova-core: register sysmem flush page")
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/driver.rs |  4 ----
 drivers/gpu/nova-core/fb.rs     | 22 ++++++++++------------
 drivers/gpu/nova-core/gpu.rs    |  9 +--------
 3 files changed, 11 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index ec9cecb30f63..401ae213991f 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -111,8 +111,4 @@ fn probe(
             }))
         })
     }
-
-    fn unbind(_pdev: &'a pci::Device<Core>, this: Pin<&'a Self>) {
-        this.gpu.unbind();
-    }
 }
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index bdd5eed760e1..cbc42e98afca 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -46,21 +46,20 @@
 /// Because of this, the sysmem flush memory page must be registered as early as possible during
 /// driver initialization, and before any falcon is reset.
 ///
-/// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
-/// otherwise the GPU might still use it even after it has been freed.
-pub(crate) struct SysmemFlush {
+pub(crate) struct SysmemFlush<'a> {
     /// Chipset we are operating on.
     chipset: Chipset,
     device: ARef<device::Device>,
+    bar: &'a Bar0,
     /// Keep the page alive as long as we need it.
     page: CoherentHandle,
 }
 
-impl SysmemFlush {
+impl<'a> SysmemFlush<'a> {
     /// Allocate a memory page and register it as the sysmem flush page.
     pub(crate) fn register(
         dev: &device::Device<device::Bound>,
-        bar: &Bar0,
+        bar: &'a Bar0,
         chipset: Chipset,
     ) -> Result<Self> {
         let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?;
@@ -70,19 +69,18 @@ pub(crate) fn register(
         Ok(Self {
             chipset,
             device: dev.into(),
+            bar,
             page,
         })
     }
+}
 
-    /// Unregister the managed sysmem flush page.
-    ///
-    /// In order to gracefully tear down the GPU, users must make sure to call this method before
-    /// dropping the object.
-    pub(crate) fn unregister(&self, bar: &Bar0) {
+impl Drop for SysmemFlush<'_> {
+    fn drop(&mut self) {
         let hal = hal::fb_hal(self.chipset);
 
-        if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
-            let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
+        if hal.read_sysmem_flush_page(self.bar) == self.page.dma_handle() {
+            let _ = hal.write_sysmem_flush_page(self.bar, 0).inspect_err(|e| {
                 dev_warn!(
                     &self.device,
                     "failed to unregister sysmem flush page: {:?}\n",
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 922197f2aeef..ff9a90a46609 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -228,7 +228,7 @@ pub(crate) struct Gpu<'a> {
     bar: &'a Bar0,
     /// System memory page required for flushing all pending GPU-side memory writes done through
     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
-    sysmem_flush: SysmemFlush,
+    sysmem_flush: SysmemFlush<'a>,
     /// GSP falcon instance, used for GSP boot up and cleanup.
     gsp_falcon: Falcon<GspFalcon>,
     /// SEC2 falcon instance, used for GSP boot up and cleanup.
@@ -271,11 +271,4 @@ pub(crate) fn new(
             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
         })
     }
-
-    /// Called when the corresponding [`Device`](device::Device) is unbound.
-    ///
-    /// Note: This method must only be called from `Driver::unbind`.
-    pub(crate) fn unbind(&self) {
-        self.sysmem_flush.unregister(self.bar);
-    }
 }
-- 
2.54.0


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

* [PATCH REF 23/24] gpu: nova-core: replace ARef<Device> with &'a Device in SysmemFlush
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (21 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH REF 22/24] gpu: nova-core: unregister sysmem flush page from Drop Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-27 22:11 ` [PATCH REF 24/24] gpu: drm: tyr: use HRT lifetime for IoMem Danilo Krummrich
  2026-04-28  9:37 ` [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Uwe Kleine-König
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Now that SysmemFlush is lifetime-parameterized, the ARef<Device> is
unnecessary -- a plain &'a Device reference suffices.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 drivers/gpu/nova-core/fb.rs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index cbc42e98afca..e5a2c9d42f27 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -15,8 +15,7 @@
         Alignable,
         Alignment, //
     },
-    sizes::*,
-    sync::aref::ARef, //
+    sizes::*, //
 };
 
 use crate::{
@@ -49,7 +48,7 @@
 pub(crate) struct SysmemFlush<'a> {
     /// Chipset we are operating on.
     chipset: Chipset,
-    device: ARef<device::Device>,
+    device: &'a device::Device,
     bar: &'a Bar0,
     /// Keep the page alive as long as we need it.
     page: CoherentHandle,
@@ -58,7 +57,7 @@ pub(crate) struct SysmemFlush<'a> {
 impl<'a> SysmemFlush<'a> {
     /// Allocate a memory page and register it as the sysmem flush page.
     pub(crate) fn register(
-        dev: &device::Device<device::Bound>,
+        dev: &'a device::Device<device::Bound>,
         bar: &'a Bar0,
         chipset: Chipset,
     ) -> Result<Self> {
@@ -68,7 +67,7 @@ pub(crate) fn register(
 
         Ok(Self {
             chipset,
-            device: dev.into(),
+            device: dev,
             bar,
             page,
         })
-- 
2.54.0


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

* [PATCH REF 24/24] gpu: drm: tyr: use HRT lifetime for IoMem
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (22 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH REF 23/24] gpu: nova-core: replace ARef<Device> with &'a Device in SysmemFlush Danilo Krummrich
@ 2026-04-27 22:11 ` Danilo Krummrich
  2026-04-28  9:37 ` [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Uwe Kleine-König
  24 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:11 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux, Danilo Krummrich

Take advantage of the lifetime-parameterized IoMem<'a> to use the
memory mapping directly during probe, eliminating the Arc<Devres<IoMem>>
indirection.

Since the IoMem is only used during probe, this also simplifies
Register::read/write to be infallible -- the Devres access check is no
longer needed, so reads return u32 directly and writes return ().

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
Not yet updated to Tyr using the register!() macro, but probably good enough for
reference.
---
 drivers/gpu/drm/tyr/driver.rs | 14 ++++----
 drivers/gpu/drm/tyr/gpu.rs    | 62 +++++++++++++++++------------------
 drivers/gpu/drm/tyr/regs.rs   | 21 +++---------
 3 files changed, 41 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index eaa84efdfdf7..d305ad433e03 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -10,7 +10,6 @@
         Core,
         Device, //
     },
-    devres::Devres,
     drm,
     drm::ioctl,
     io::poll,
@@ -23,7 +22,6 @@
     sizes::SZ_2M,
     sync::{
         aref::ARef,
-        Arc,
         Mutex, //
     },
     time, //
@@ -37,7 +35,7 @@
     regs, //
 };
 
-pub(crate) type IoMem = kernel::io::mem::IoMem<'static, SZ_2M>;
+pub(crate) type IoMem = kernel::io::Mmio<SZ_2M>;
 
 pub(crate) struct TyrDrmDriver;
 
@@ -65,11 +63,11 @@ pub(crate) struct TyrDrmDeviceData {
     pub(crate) gpu_info: GpuInfo,
 }
 
-fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result {
-    regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?;
+fn issue_soft_reset(dev: &Device<Bound>, iomem: &IoMem) -> Result {
+    regs::GPU_CMD.write(iomem, regs::GPU_CMD_SOFT_RESET);
 
     poll::read_poll_timeout(
-        || regs::GPU_IRQ_RAWSTAT.read(dev, iomem),
+        || Ok(regs::GPU_IRQ_RAWSTAT.read(iomem)),
         |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0,
         time::Delta::from_millis(1),
         time::Delta::from_millis(100),
@@ -109,12 +107,12 @@ fn probe(
         let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
 
         let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
-        let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?.into_devres()?, GFP_KERNEL)?;
+        let iomem = request.iomap_sized::<SZ_2M>()?;
 
         issue_soft_reset(pdev.as_ref(), &iomem)?;
         gpu::l2_power_on(pdev.as_ref(), &iomem)?;
 
-        let gpu_info = GpuInfo::new(pdev.as_ref(), &iomem)?;
+        let gpu_info = GpuInfo::new(&iomem);
         gpu_info.log(pdev);
 
         let platform: ARef<platform::Device> = pdev.into();
diff --git a/drivers/gpu/drm/tyr/gpu.rs b/drivers/gpu/drm/tyr/gpu.rs
index a88775160f98..bb0473c85bf7 100644
--- a/drivers/gpu/drm/tyr/gpu.rs
+++ b/drivers/gpu/drm/tyr/gpu.rs
@@ -10,7 +10,6 @@
         Bound,
         Device, //
     },
-    devres::Devres,
     io::poll,
     platform,
     prelude::*,
@@ -35,37 +34,36 @@
 pub(crate) struct GpuInfo(pub(crate) uapi::drm_panthor_gpu_info);
 
 impl GpuInfo {
-    pub(crate) fn new(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result<Self> {
-        let gpu_id = regs::GPU_ID.read(dev, iomem)?;
-        let csf_id = regs::GPU_CSF_ID.read(dev, iomem)?;
-        let gpu_rev = regs::GPU_REVID.read(dev, iomem)?;
-        let core_features = regs::GPU_CORE_FEATURES.read(dev, iomem)?;
-        let l2_features = regs::GPU_L2_FEATURES.read(dev, iomem)?;
-        let tiler_features = regs::GPU_TILER_FEATURES.read(dev, iomem)?;
-        let mem_features = regs::GPU_MEM_FEATURES.read(dev, iomem)?;
-        let mmu_features = regs::GPU_MMU_FEATURES.read(dev, iomem)?;
-        let thread_features = regs::GPU_THREAD_FEATURES.read(dev, iomem)?;
-        let max_threads = regs::GPU_THREAD_MAX_THREADS.read(dev, iomem)?;
-        let thread_max_workgroup_size = regs::GPU_THREAD_MAX_WORKGROUP_SIZE.read(dev, iomem)?;
-        let thread_max_barrier_size = regs::GPU_THREAD_MAX_BARRIER_SIZE.read(dev, iomem)?;
-        let coherency_features = regs::GPU_COHERENCY_FEATURES.read(dev, iomem)?;
-
-        let texture_features = regs::GPU_TEXTURE_FEATURES0.read(dev, iomem)?;
-
-        let as_present = regs::GPU_AS_PRESENT.read(dev, iomem)?;
-
-        let shader_present = u64::from(regs::GPU_SHADER_PRESENT_LO.read(dev, iomem)?);
+    pub(crate) fn new(iomem: &IoMem) -> Self {
+        let gpu_id = regs::GPU_ID.read(iomem);
+        let csf_id = regs::GPU_CSF_ID.read(iomem);
+        let gpu_rev = regs::GPU_REVID.read(iomem);
+        let core_features = regs::GPU_CORE_FEATURES.read(iomem);
+        let l2_features = regs::GPU_L2_FEATURES.read(iomem);
+        let tiler_features = regs::GPU_TILER_FEATURES.read(iomem);
+        let mem_features = regs::GPU_MEM_FEATURES.read(iomem);
+        let mmu_features = regs::GPU_MMU_FEATURES.read(iomem);
+        let thread_features = regs::GPU_THREAD_FEATURES.read(iomem);
+        let max_threads = regs::GPU_THREAD_MAX_THREADS.read(iomem);
+        let thread_max_workgroup_size = regs::GPU_THREAD_MAX_WORKGROUP_SIZE.read(iomem);
+        let thread_max_barrier_size = regs::GPU_THREAD_MAX_BARRIER_SIZE.read(iomem);
+        let coherency_features = regs::GPU_COHERENCY_FEATURES.read(iomem);
+
+        let texture_features = regs::GPU_TEXTURE_FEATURES0.read(iomem);
+
+        let as_present = regs::GPU_AS_PRESENT.read(iomem);
+
+        let shader_present = u64::from(regs::GPU_SHADER_PRESENT_LO.read(iomem));
         let shader_present =
-            shader_present | u64::from(regs::GPU_SHADER_PRESENT_HI.read(dev, iomem)?) << 32;
+            shader_present | u64::from(regs::GPU_SHADER_PRESENT_HI.read(iomem)) << 32;
 
-        let tiler_present = u64::from(regs::GPU_TILER_PRESENT_LO.read(dev, iomem)?);
-        let tiler_present =
-            tiler_present | u64::from(regs::GPU_TILER_PRESENT_HI.read(dev, iomem)?) << 32;
+        let tiler_present = u64::from(regs::GPU_TILER_PRESENT_LO.read(iomem));
+        let tiler_present = tiler_present | u64::from(regs::GPU_TILER_PRESENT_HI.read(iomem)) << 32;
 
-        let l2_present = u64::from(regs::GPU_L2_PRESENT_LO.read(dev, iomem)?);
-        let l2_present = l2_present | u64::from(regs::GPU_L2_PRESENT_HI.read(dev, iomem)?) << 32;
+        let l2_present = u64::from(regs::GPU_L2_PRESENT_LO.read(iomem));
+        let l2_present = l2_present | u64::from(regs::GPU_L2_PRESENT_HI.read(iomem)) << 32;
 
-        Ok(Self(uapi::drm_panthor_gpu_info {
+        Self(uapi::drm_panthor_gpu_info {
             gpu_id,
             gpu_rev,
             csf_id,
@@ -88,7 +86,7 @@ pub(crate) fn new(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result<Self> {
             core_features,
             pad: 0,
             gpu_features: 0,
-        }))
+        })
     }
 
     pub(crate) fn log(&self, pdev: &platform::Device) {
@@ -208,11 +206,11 @@ fn from(value: u32) -> Self {
 }
 
 /// Powers on the l2 block.
-pub(crate) fn l2_power_on(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result {
-    regs::L2_PWRON_LO.write(dev, iomem, 1)?;
+pub(crate) fn l2_power_on(dev: &Device<Bound>, iomem: &IoMem) -> Result {
+    regs::L2_PWRON_LO.write(iomem, 1);
 
     poll::read_poll_timeout(
-        || regs::L2_READY_LO.read(dev, iomem),
+        || Ok(regs::L2_READY_LO.read(iomem)),
         |status| *status == 1,
         Delta::from_millis(1),
         Delta::from_millis(100),
diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs
index 611870c2e6af..0881b3812afd 100644
--- a/drivers/gpu/drm/tyr/regs.rs
+++ b/drivers/gpu/drm/tyr/regs.rs
@@ -7,16 +7,7 @@
 // does.
 #![allow(dead_code)]
 
-use kernel::{
-    bits::bit_u32,
-    device::{
-        Bound,
-        Device, //
-    },
-    devres::Devres,
-    io::Io,
-    prelude::*, //
-};
+use kernel::{bits::bit_u32, io::Io};
 
 use crate::driver::IoMem;
 
@@ -29,15 +20,13 @@
 
 impl<const OFFSET: usize> Register<OFFSET> {
     #[inline]
-    pub(crate) fn read(&self, dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result<u32> {
-        let value = (*iomem).access(dev)?.read32(OFFSET);
-        Ok(value)
+    pub(crate) fn read(&self, iomem: &IoMem) -> u32 {
+        iomem.read32(OFFSET)
     }
 
     #[inline]
-    pub(crate) fn write(&self, dev: &Device<Bound>, iomem: &Devres<IoMem>, value: u32) -> Result {
-        (*iomem).access(dev)?.write32(value, OFFSET);
-        Ok(())
+    pub(crate) fn write(&self, iomem: &IoMem, value: u32) {
+        iomem.write32(value, OFFSET);
     }
 }
 
-- 
2.54.0


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

* Re: [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support
  2026-04-27 22:11 ` [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support Danilo Krummrich
@ 2026-04-27 22:16   ` Danilo Krummrich
  0 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-27 22:16 UTC (permalink / raw)
  To: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux

On Tue Apr 28, 2026 at 12:11 AM CEST, Danilo Krummrich wrote:
> From: Gary Guo <gary@garyguo.net>
>
> There are a few cases, e.g. when dealing with data referencing each other,
> one might want to write code that are generic over lifetimes. For example,
> if you want take a function that takes `&'a Foo` and gives `Bar<'a>`, you
> can write:
>
>     f: impl for<'a> FnOnce(&'a Foo) -> Bar<'a>,
>
> However, it becomes tricky when you want that function to not have a fixed
> `Bar`, but have it be generic again. In this case, one needs something that
> is generic over types that are themselves generic over lifetimes.
>
> `ForLt` provides such support. It provides a trait `ForLt` which describes
> a type generic over lifetime. One may use `ForLt::Of<'a>` to get an
> instance of a type for a specific lifetime.
>
> For the case of cross referencing, one would almost always want the
> lifetime to be covariant. Therefore this is also made a requirement for the
> `ForLt` trait, so functions with `ForLt` trait bound can assume covariance.
>
> A macro `ForLt!()` is provided to be able to obtain a type that implements
> `ForLt`. For example, `ForLt!(for<'a> Bar<'a>)` would yield a type that
> `<TheType as ForLt>::Of<'a>` is `Bar<'a>`. This also works with lifetime
> elision, e.g. `ForLt!(Bar<'_>)` or for types without lifetime at all, e.g.
> `ForLt!(u32)`.
>
> The API design draws inspiration from the higher-kinded-types [1] crate,
> however different design decision has been taken (e.g. covariance
> requirement) and the implementation is independent.
>
> License headers use "Apache-2.0 OR MIT" because I anticipate this to be
> used in pin-init crate too which is licensed as such.
>
> Link: https://docs.rs/higher-kinded-types/ [1]
>
> Signed-off-by: Gary Guo <gary@garyguo.net>

Signed-off-by: Danilo Krummrich <dakr@kernel.org>

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

* Re: [PATCH 06/24] rust: pci: implement Sync for Device<Bound>
  2026-04-27 22:11 ` [PATCH 06/24] rust: pci: implement Sync for Device<Bound> Danilo Krummrich
@ 2026-04-27 23:52   ` Gary Guo
  2026-04-28 10:11     ` Danilo Krummrich
  0 siblings, 1 reply; 30+ messages in thread
From: Gary Guo @ 2026-04-27 23:52 UTC (permalink / raw)
  To: Danilo Krummrich, gregkh, rafael, acourbot, aliceryhl,
	david.m.ertman, ira.weiny, leon, viresh.kumar, m.wilczynski,
	ukleinek, bhelgaas, kwilczynski, abdiel.janulgue, robin.murphy,
	markus.probst, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	tmgross
  Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pm,
	linux-pwm, linux-pci, rust-for-linux

On Mon Apr 27, 2026 at 11:11 PM BST, Danilo Krummrich wrote:
> Implement Sync for Device<Bound> in addition to Device<Normal>. The
> underlying struct pci_dev is the same; Bound is a zero-sized type-state
> marker that does not affect thread safety.
>
> This is needed for pci::Bar to hold &'a Device<Bound> (required for
> Bar::into_devres()) while remaining Send.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/pci.rs | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> index fe5148f41d8b..6f82f2e6c74f 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -526,3 +526,7 @@ unsafe impl Send for Device {}
>  // SAFETY: `Device` can be shared among threads because all methods of `Device`
>  // (i.e. `Device<Normal>) are thread safe.
>  unsafe impl Sync for Device {}
> +
> +// SAFETY: Same as `Device<Normal>` -- the underlying `struct pci_dev` is the same;
> +// `Bound` is a zero-sized type-state marker that does not affect thread safety.
> +unsafe impl Sync for Device<device::Bound> {}

Given that you're now implementing for two ctx marker types, it might worth changing
the existing one to spell out the `Normal` explicitly (also saves you from
having to spell it out in the comment).

It might also make sense to write a comment to say that why it's not the case
for `Device<Core>`.

Best,
Gary

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

* Re: [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers
  2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
                   ` (23 preceding siblings ...)
  2026-04-27 22:11 ` [PATCH REF 24/24] gpu: drm: tyr: use HRT lifetime for IoMem Danilo Krummrich
@ 2026-04-28  9:37 ` Uwe Kleine-König
  2026-04-28 10:04   ` Danilo Krummrich
  24 siblings, 1 reply; 30+ messages in thread
From: Uwe Kleine-König @ 2026-04-28  9:37 UTC (permalink / raw)
  To: Danilo Krummrich, m.wilczynski
  Cc: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, bhelgaas, kwilczynski, abdiel.janulgue,
	robin.murphy, markus.probst, ojeda, boqun, gary, bjorn3_gh,
	lossin, a.hindborg, tmgross, driver-core, linux-kernel, nova-gpu,
	dri-devel, linux-pm, linux-pwm, linux-pci, rust-for-linux

[-- Attachment #1: Type: text/plain, Size: 485 bytes --]

Hello,

On Tue, Apr 28, 2026 at 12:10:58AM +0200, Danilo Krummrich wrote:
>  drivers/pwm/pwm_th1520.rs             |  14 +-

I didn't try to understand what you do here, but there is nothing
scheduled so far for the pwm_th1520 driver in my tree, so

Acked-by: Uwe Kleine-König <ukleinek@kernel.org>

for patches #12 and #19 touching this driver for merging through (I
guess) the rust tree. Maybe Michal wants to say something about the
actual change.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers
  2026-04-28  9:37 ` [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Uwe Kleine-König
@ 2026-04-28 10:04   ` Danilo Krummrich
  0 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-28 10:04 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: m.wilczynski, gregkh, rafael, acourbot, aliceryhl, david.m.ertman,
	ira.weiny, leon, viresh.kumar, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross, driver-core, linux-kernel,
	nova-gpu, dri-devel, linux-pm, linux-pwm, linux-pci,
	rust-for-linux

On Tue Apr 28, 2026 at 11:37 AM CEST, Uwe Kleine-König wrote:
> I didn't try to understand what you do here, but there is nothing
> scheduled so far for the pwm_th1520 driver in my tree, so

Compressed to one sentence, it is driver core plumbing to allow bus device
private data (and registration data) to represent their lifetime being tied to
the device / driver lifecycle with native Rust lifetimes.

Drivers can take advantage of this subsequently; for reference see the nova-core
changes at the end of this series.

> Acked-by: Uwe Kleine-König <ukleinek@kernel.org>
>
> for patches #12 and #19 touching this driver for merging through (I
> guess) the rust tree.

Thanks -- as mentioned, it is driver core plumbing, so eventually I'll take it
through the driver core tree.

- Danilo

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

* Re: [PATCH 06/24] rust: pci: implement Sync for Device<Bound>
  2026-04-27 23:52   ` Gary Guo
@ 2026-04-28 10:11     ` Danilo Krummrich
  0 siblings, 0 replies; 30+ messages in thread
From: Danilo Krummrich @ 2026-04-28 10:11 UTC (permalink / raw)
  To: Gary Guo
  Cc: gregkh, rafael, acourbot, aliceryhl, david.m.ertman, ira.weiny,
	leon, viresh.kumar, m.wilczynski, ukleinek, bhelgaas, kwilczynski,
	abdiel.janulgue, robin.murphy, markus.probst, ojeda, boqun,
	bjorn3_gh, lossin, a.hindborg, tmgross, driver-core, linux-kernel,
	nova-gpu, dri-devel, linux-pm, linux-pwm, linux-pci,
	rust-for-linux

On Tue Apr 28, 2026 at 1:52 AM CEST, Gary Guo wrote:
> Given that you're now implementing for two ctx marker types, it might worth changing
> the existing one to spell out the `Normal` explicitly (also saves you from
> having to spell it out in the comment).

I don't think this belongs in this commit and I'm not sure it is worth creating
new ones for only this purpose.

> It might also make sense to write a comment to say that why it's not the case
> for `Device<Core>`.

I think the documentation in [1] explains (or at least implies) it. Where do you
suggest to put such a comment? It seems a bit repretitive to have it for all bus
devices.

[1] https://rust.docs.kernel.org/kernel/device/struct.Core.html

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

end of thread, other threads:[~2026-04-28 10:11 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-27 22:10 [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
2026-04-27 22:10 ` [PATCH 01/24] rust: driver core: drop drvdata before devres release Danilo Krummrich
2026-04-27 22:11 ` [PATCH 02/24] rust: types: add `ForLt` trait for higher-ranked lifetime support Danilo Krummrich
2026-04-27 22:16   ` Danilo Krummrich
2026-04-27 22:11 ` [PATCH 03/24] rust: devres: add ForLt support to Devres Danilo Krummrich
2026-04-27 22:11 ` [PATCH 04/24] rust: device: generalize drvdata methods over ForLt Danilo Krummrich
2026-04-27 22:11 ` [PATCH 05/24] rust: driver: make Adapter trait lifetime-parameterized Danilo Krummrich
2026-04-27 22:11 ` [PATCH 06/24] rust: pci: implement Sync for Device<Bound> Danilo Krummrich
2026-04-27 23:52   ` Gary Guo
2026-04-28 10:11     ` Danilo Krummrich
2026-04-27 22:11 ` [PATCH 07/24] rust: platform: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 08/24] rust: auxiliary: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 09/24] rust: usb: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 10/24] rust: device: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 11/24] rust: pci: make Driver trait lifetime-parameterized Danilo Krummrich
2026-04-27 22:11 ` [PATCH 12/24] rust: platform: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 13/24] rust: auxiliary: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 14/24] rust: auxiliary: generalize Registration over ForLt Danilo Krummrich
2026-04-27 22:11 ` [PATCH 15/24] samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data Danilo Krummrich
2026-04-27 22:11 ` [PATCH 16/24] rust: usb: make Driver trait lifetime-parameterized Danilo Krummrich
2026-04-27 22:11 ` [PATCH 17/24] rust: i2c: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH 18/24] rust: pci: make Bar lifetime-parameterized Danilo Krummrich
2026-04-27 22:11 ` [PATCH 19/24] rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized Danilo Krummrich
2026-04-27 22:11 ` [PATCH 20/24] samples: rust: rust_driver_pci: use HRT lifetime for Bar Danilo Krummrich
2026-04-27 22:11 ` [PATCH REF 21/24] gpu: nova-core: " Danilo Krummrich
2026-04-27 22:11 ` [PATCH REF 22/24] gpu: nova-core: unregister sysmem flush page from Drop Danilo Krummrich
2026-04-27 22:11 ` [PATCH REF 23/24] gpu: nova-core: replace ARef<Device> with &'a Device in SysmemFlush Danilo Krummrich
2026-04-27 22:11 ` [PATCH REF 24/24] gpu: drm: tyr: use HRT lifetime for IoMem Danilo Krummrich
2026-04-28  9:37 ` [PATCH 00/24] rust: device: Higher-Ranked Lifetime Types for device drivers Uwe Kleine-König
2026-04-28 10:04   ` Danilo Krummrich

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