* [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt
@ 2026-06-03 1:10 Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
The ForLt trait currently guarantees covariance, which allows safe
lifetime shortening via cast_ref(). However, some types (e.g. those
containing Mutex<&'bound T>) are invariant over their lifetime parameter
and cannot safely use cast_ref().
This series splits ForLt into two traits:
- ForLt: base trait providing unsafe cast_ref_unchecked() for all
lifetime-parameterized types.
- CovariantForLt: unsafe subtrait that guarantees covariance,
providing a safe cast_ref() method.
For invariant types, a closure-based API (registration_data_with()) is
added to the auxiliary subsystem. The closure's HRTB prevents the caller
from choosing a concrete lifetime, which would be unsound for invariant
types.
On top of that, this series adds DevresLt<F: ForLt>, a thin wrapper
around Devres<F::Of<'static>> that shortens the stored 'static lifetime
back to the caller's borrow scope. DevresLt provides both closure-based
access (access_with/try_access_with for ForLt types) and direct
reference access (access/try_access for CovariantForLt types).
Also implement ForLt and CovariantForLt for Bar, IoMem and
ExclusiveIoMem, and update their into_devres() methods to return
DevresLt. Provide convenience type aliases DevresBar, DevresIoMem and
DevresExclusiveIoMem.
Changes in v2:
- Fold the ForLt -> CovariantForLt rename and the new ForLt base trait
into this series
- Add closure-based registration_data_with() for auxiliary ForLt types
- Add auxiliary sample demonstrating ForLt with an invariant Mutex type
- DevresLt: add closure-based access_with()/try_access_with() for ForLt
types alongside direct access()/try_access() for CovariantForLt types
- Make DevresLt::new() unsafe; callers must guarantee the data outlives
the device binding
- Implement both ForLt and CovariantForLt (previously just ForLt) for
Bar, IoMem, ExclusiveIoMem
- Various safety comment and documentation improvements
Danilo Krummrich (7):
rust: types: rename ForLt to CovariantForLt
rust: types: introduce ForLt base trait for CovariantForLt
rust: auxiliary: add registration_data_with() for ForLt types
rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type
rust: devres: add DevresLt for ForLt-aware device resource access
rust: pci: return DevresLt from Bar::into_devres()
rust: io: mem: return DevresLt from
IoMem/ExclusiveIoMem::into_devres()
drivers/gpu/nova-core/driver.rs | 4 +-
drivers/pwm/pwm_th1520.rs | 5 +-
rust/kernel/auxiliary.rs | 74 +++++++++++++---
rust/kernel/devres.rs | 110 +++++++++++++++++++++++
rust/kernel/io/mem.rs | 65 +++++++++-----
rust/kernel/pci.rs | 1 +
rust/kernel/pci/io.rs | 37 +++++---
rust/kernel/types.rs | 1 +
rust/kernel/types/for_lt.rs | 122 ++++++++++++++++++++------
rust/macros/for_lt.rs | 52 ++++++++---
rust/macros/lib.rs | 18 +++-
samples/rust/rust_driver_auxiliary.rs | 96 ++++++++++++++------
12 files changed, 472 insertions(+), 113 deletions(-)
base-commit: 0023a1e8d01a9d400257d30c851bd16a29568809
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 11:59 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Rename ForLt to CovariantForLt to prepare for the introduction of a new
ForLt base trait that does not require covariance.
The existing ForLt trait requires covariance, which enables the safe
cast_ref() method. This rename preserves the same semantics under a more
precise name, making room for a weaker ForLt trait in a subsequent
commit.
No functional change.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/gpu/nova-core/driver.rs | 4 +-
rust/kernel/auxiliary.rs | 23 +++++-----
rust/kernel/types.rs | 2 +-
rust/kernel/types/for_lt.rs | 61 ++++++++++++++-------------
rust/macros/for_lt.rs | 4 +-
rust/macros/lib.rs | 11 +++--
samples/rust/rust_driver_auxiliary.rs | 8 ++--
7 files changed, 57 insertions(+), 56 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index ade73da68be5..dce8b3b8dc6f 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -15,7 +15,7 @@
Atomic,
Relaxed, //
},
- types::ForLt,
+ types::CovariantForLt,
};
use crate::gpu::Gpu;
@@ -29,7 +29,7 @@ pub(crate) struct NovaCore<'bound> {
pub(crate) gpu: Gpu<'bound>,
bar: pci::Bar<'bound, BAR0_SIZE>,
#[allow(clippy::type_complexity)]
- _reg: auxiliary::Registration<'bound, ForLt!(())>,
+ _reg: auxiliary::Registration<'bound, CovariantForLt!(())>,
}
pub(crate) struct NovaCoreDriver;
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index c42928d5a239..40a0af74a8e5 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -20,7 +20,7 @@
},
prelude::*,
types::{
- ForLt,
+ CovariantForLt,
ForeignOwnable,
Opaque, //
},
@@ -272,16 +272,16 @@ pub fn parent(&self) -> &device::Device<device::Bound> {
/// Returns a pinned reference to the registration data set by the registering (parent) driver.
///
- /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The returned
+ /// `F` is the [`CovariantForLt`](trait@CovariantForLt) encoding of the data type. The returned
/// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via
- /// [`ForLt::cast_ref`].
+ /// [`CovariantForLt::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<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
// 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() {
@@ -399,8 +399,9 @@ 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 `F` is a [`ForLt`](trait@ForLt) encoding of the registration
-/// data type. For non-lifetime-parameterized types, use [`ForLt!(T)`](macro@ForLt).
+/// The type parameter `F` is a [`CovariantForLt`](trait@CovariantForLt) encoding of the
+/// registration data type. For non-lifetime-parameterized types, use
+/// [`CovariantForLt!(T)`](macro@CovariantForLt).
/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`].
///
/// # Invariants
@@ -408,12 +409,12 @@ struct RegistrationData<T> {
/// `self.adev` always holds a valid pointer to an initialized and registered
/// [`struct auxiliary_device`] whose `registration_data_rust` field points to a
/// valid `Pin<KBox<RegistrationData<F::Of<'static>>>>`.
-pub struct Registration<'a, F: ForLt + 'static> {
+pub struct Registration<'a, F: CovariantForLt + 'static> {
adev: NonNull<bindings::auxiliary_device>,
_phantom: PhantomData<F::Of<'a>>,
}
-impl<'a, F: ForLt> Registration<'a, F>
+impl<'a, F: CovariantForLt> Registration<'a, F>
where
for<'b> F::Of<'b>: Send + Sync,
{
@@ -525,7 +526,7 @@ pub fn new<E>(
}
}
-impl<F: ForLt> Drop for Registration<'_, F> {
+impl<F: CovariantForLt> Drop for Registration<'_, F> {
fn drop(&mut self) {
// SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered
// `struct auxiliary_device`.
@@ -547,7 +548,7 @@ fn drop(&mut self) {
}
// SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
-unsafe impl<F: ForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
+unsafe impl<F: CovariantForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
// SAFETY: `Registration` does not expose any methods or fields that need synchronization.
-unsafe impl<F: ForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
+unsafe impl<F: CovariantForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index ac316fd7b538..cbe6907042d3 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -13,7 +13,7 @@
#[doc(hidden)]
pub mod for_lt;
-pub use for_lt::ForLt;
+pub use for_lt::CovariantForLt;
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
index d44323c28e8d..ef510ab6c092 100644
--- a/rust/kernel/types/for_lt.rs
+++ b/rust/kernel/types/for_lt.rs
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
-//! Provide implementation and test of the `ForLt` trait and macro.
+//! Provide implementation and test of the `CovariantForLt` trait and macro.
//!
-//! This module is hidden and user should just use `ForLt!` directly.
+//! This module is hidden and user should just use `CovariantForLt!` directly.
use core::marker::PhantomData;
@@ -15,38 +15,39 @@
///
/// # Macro
///
-/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
-/// type that implements this trait.
+/// It is not recommended to implement this trait directly. `CovariantForLt!` macro is provided to
+/// obtain a type that implements this trait.
///
/// The full syntax is
///
/// ```
-/// # use kernel::types::ForLt;
-/// # fn expect_lt<F: ForLt>() {}
+/// # use kernel::types::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
/// # struct TypeThatUse<'a>(&'a ());
/// # expect_lt::<
-/// ForLt!(for<'a> TypeThatUse<'a>)
+/// CovariantForLt!(for<'a> TypeThatUse<'a>)
/// # >();
/// ```
///
-/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
+/// which gives a type so that
+/// `<CovariantForLt!(for<'a> TypeThatUse<'a>) as CovariantForLt>::Of<'b>`
/// is `TypeThatUse<'b>`.
///
/// You may also use a short-hand syntax which works similar to lifetime elision.
/// The macro also accepts types that do not involve a lifetime at all.
///
/// ```
-/// # use kernel::types::ForLt;
-/// # fn expect_lt<F: ForLt>() {}
+/// # use kernel::types::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
/// # struct TypeThatUse<'a>(&'a ());
/// # expect_lt::<
-/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
+/// CovariantForLt!(TypeThatUse<'_>) // Equivalent to `CovariantForLt!(for<'a> TypeThatUse<'a>)`.
/// # >();
/// # expect_lt::<
-/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
+/// CovariantForLt!(&u32) // Equivalent to `CovariantForLt!(for<'a> &'a u32)`.
/// # >();
/// # expect_lt::<
-/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
+/// CovariantForLt!(u32) // Equivalent to `CovariantForLt!(for<'a> u32)`.
/// # >();
/// ```
///
@@ -55,10 +56,10 @@
/// it.
///
/// ```ignore,compile_fail
-/// # use kernel::types::ForLt;
-/// # fn expect_lt<F: ForLt>() {}
+/// # use kernel::types::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
/// # expect_lt::<
-/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.
+/// CovariantForLt!(fn(&u32)) // Contravariant, will fail compilation.
/// # >();
/// ```
///
@@ -67,23 +68,23 @@
/// the generic parameter but is in a separate item.
///
/// ```
-/// # use kernel::types::ForLt;
-/// fn expect_lt<F: ForLt>() {}
+/// # use kernel::types::CovariantForLt;
+/// fn expect_lt<F: CovariantForLt>() {}
/// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")]
/// fn generic_fn<T: 'static>() {
/// // Syntactically proven by the macro
-/// expect_lt::<ForLt!(&T)>();
+/// expect_lt::<CovariantForLt!(&T)>();
/// // Syntactically proven by the macro
-/// expect_lt::<ForLt!(&KBox<T>)>();
+/// expect_lt::<CovariantForLt!(&KBox<T>)>();
/// // Cannot be syntactically proven, need to check covariance of `KBox`
-/// // expect_lt::<ForLt!(&KBox<&T>)>();
+/// // expect_lt::<CovariantForLt!(&KBox<&T>)>();
/// }
/// ```
///
/// # Safety
///
/// `Self::Of<'a>` must be covariant over the lifetime `'a`.
-pub unsafe trait ForLt {
+pub unsafe trait CovariantForLt {
/// The type parameterized by the lifetime.
type Of<'a>: 'a;
@@ -94,11 +95,11 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
unsafe { core::mem::transmute(long) }
}
}
-pub use macros::ForLt;
+pub use macros::CovariantForLt;
-/// This is intended to be an "unsafe-to-refer-to" type.
+/// Helper type for the `CovariantForLt!` macro.
///
-/// Must only be used by the `ForLt!` macro.
+/// Must only be used by the `CovariantForLt!` macro.
///
/// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.
///
@@ -107,16 +108,16 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
/// `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)>);
+pub struct CovariantForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
-// This is a helper trait for implementation `ForLt` to be able to use HRTB.
+// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
#[doc(hidden)]
pub trait WithLt<'a> {
type Of: 'a;
}
-// 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> {
+// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
+// `CovariantForLtImpl` and it will fail to evaluate if the type is not covariant.
+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for CovariantForLtImpl<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
index 364d4113cd10..3cb094d00548 100644
--- a/rust/macros/for_lt.rs
+++ b/rust/macros/for_lt.rs
@@ -176,7 +176,7 @@ fn prove(&mut self, ty: &'a Type) {
}
}
-pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
+pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
let (ty, lifetime) = match input {
HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
HigherRankedType::Implicit { ty } => {
@@ -235,7 +235,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
);
quote!(
- ::kernel::types::for_lt::UnsafeForLtImpl::<
+ ::kernel::types::for_lt::CovariantForLtImpl::<
dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
#ty_static,
{
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 4a48fabbc268..2167cb270928 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -491,14 +491,13 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
-/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
+/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
///
-/// Please refer to the documentation of the [`ForLt`] trait.
+/// Please refer to the documentation of the [`CovariantForLt`] trait.
///
-/// [`ForLt`]: trait.ForLt.html
+/// [`CovariantForLt`]: trait.CovariantForLt.html
#[proc_macro]
-// The macro shares the name with the trait.
#[allow(non_snake_case)]
-pub fn ForLt(input: TokenStream) -> TokenStream {
- for_lt::for_lt(parse_macro_input!(input)).into()
+pub fn CovariantForLt(input: TokenStream) -> TokenStream {
+ for_lt::covariant_for_lt(parse_macro_input!(input)).into()
}
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index 2c1351040e45..92ee6a6d348e 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -13,7 +13,7 @@
driver,
pci,
prelude::*,
- types::ForLt,
+ types::CovariantForLt,
InPlaceModule, //
};
@@ -60,8 +60,8 @@ struct Data<'bound> {
#[allow(clippy::type_complexity)]
struct ParentData<'bound> {
- _reg0: auxiliary::Registration<'bound, ForLt!(Data<'_>)>,
- _reg1: auxiliary::Registration<'bound, ForLt!(Data<'_>)>,
+ _reg0: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
+ _reg1: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
}
kernel::pci_device_table!(
@@ -115,7 +115,7 @@ fn probe<'bound>(
impl ParentDriver {
fn connect(adev: &auxiliary::Device<Bound>) -> Result {
- let data = adev.registration_data::<ForLt!(Data<'_>)>()?;
+ let data = adev.registration_data::<CovariantForLt!(Data<'_>)>()?;
let pdev = data.parent;
dev_info!(
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 12:04 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
` (4 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Add a new ForLt trait as a base for CovariantForLt:
- ForLt (non-unsafe): represents a type generic over a lifetime, with
no covariance guarantee. Provides unsafe fn cast_ref_unchecked()
for callers that can justify lifetime shortening via a round-trip
safety argument.
- CovariantForLt (unsafe): becomes a subtrait of ForLt that
additionally proves the type is covariant over its lifetime
parameter, providing a safe cast_ref() method.
This split allows non-covariant types (e.g. types behind a Mutex) to
implement ForLt and participate in DevresLt / registration data patterns
where the round-trip argument suffices, without requiring a covariance
proof that would fail to compile.
The internal macro backend is split accordingly: ForLt! emits ForLtImpl
(no covariance proof), CovariantForLt! emits CovariantForLtImpl (with
compile-time covariance proof).
No existing callers change; they all use CovariantForLt.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/types.rs | 1 +
rust/kernel/types/for_lt.rs | 101 ++++++++++++++++++++++++++++++------
rust/macros/for_lt.rs | 50 ++++++++++++++----
rust/macros/lib.rs | 19 ++++++-
4 files changed, 145 insertions(+), 26 deletions(-)
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index cbe6907042d3..c1ed05d1046c 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -14,6 +14,7 @@
#[doc(hidden)]
pub mod for_lt;
pub use for_lt::CovariantForLt;
+pub use for_lt::ForLt;
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
index ef510ab6c092..e1774b03dd1f 100644
--- a/rust/kernel/types/for_lt.rs
+++ b/rust/kernel/types/for_lt.rs
@@ -1,17 +1,74 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
-//! Provide implementation and test of the `CovariantForLt` trait and macro.
+//! Provide implementation and test of the `ForLt` and `CovariantForLt` traits and macros.
//!
-//! This module is hidden and user should just use `CovariantForLt!` directly.
+//! This module is hidden and users should just use `ForLt!` / `CovariantForLt!` directly.
use core::marker::PhantomData;
/// Representation of types generic over a lifetime.
///
-/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter
-/// can be soundly shortened.
+/// # Macro
+///
+/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
+/// type that implements this trait.
+///
+/// The full syntax is
+///
+/// ```
+/// # use kernel::types::ForLt;
+/// # fn expect_lt<F: ForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// ForLt!(for<'a> TypeThatUse<'a>)
+/// # >();
+/// ```
+///
+/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
+/// is `TypeThatUse<'b>`.
+///
+/// You may also use a short-hand syntax which works similar to lifetime elision.
+/// The macro also accepts types that do not involve a lifetime at all.
+///
+/// ```
+/// # use kernel::types::ForLt;
+/// # fn expect_lt<F: ForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
+/// # >();
+/// # expect_lt::<
+/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
+/// # >();
+/// # expect_lt::<
+/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
+/// # >();
+/// ```
+pub trait ForLt {
+ /// The type parameterized by the lifetime.
+ type Of<'a>: 'a;
+
+ /// Cast a reference to a shorter lifetime.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the lifetime shortening is sound for their use case,
+ /// e.g. because the `'long` reference originated from a `'short`-to-`'static` transmute
+ /// and this is the reverse leg of that round-trip.
+ #[inline(always)]
+ unsafe fn cast_ref_unchecked<'r, 'short: 'r, 'long: 'short>(
+ long: &'r Self::Of<'long>,
+ ) -> &'r Self::Of<'short> {
+ // SAFETY: Caller guarantees the lifetime shortening is sound.
+ unsafe { core::mem::transmute(long) }
+ }
+}
+pub use macros::ForLt;
+
+/// [`trait@ForLt`] subtrait for types that are covariant over their lifetime parameter.
///
-/// The lifetime involved must be covariant.
+/// Provides a safe [`cast_ref`](CovariantForLt::cast_ref) method for types that are proven to be
+/// covariant. The `CovariantForLt!` macro syntax is the same as `ForLt!`.
///
/// # Macro
///
@@ -84,10 +141,7 @@
/// # Safety
///
/// `Self::Of<'a>` must be covariant over the lifetime `'a`.
-pub unsafe trait CovariantForLt {
- /// The type parameterized by the lifetime.
- type Of<'a>: 'a;
-
+pub unsafe trait CovariantForLt: ForLt {
/// Cast a reference to a shorter lifetime.
#[inline(always)]
fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {
@@ -97,27 +151,44 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
}
pub use macros::CovariantForLt;
-/// Helper type for the `CovariantForLt!` macro.
+/// Helper type for the `ForLt!` macro.
///
-/// Must only be used by the `CovariantForLt!` macro.
+/// 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.
+#[doc(hidden)]
+pub struct ForLtImpl<T: ?Sized, WF>(PhantomData<(WF, T)>);
+
+/// Helper type for the `CovariantForLt!` macro.
+///
+/// Must only be used by the `CovariantForLt!` macro.
+///
+/// `T` and `WF` are the same as in [`ForLtImpl`].
///
/// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove
/// additional properties.
#[doc(hidden)]
pub struct CovariantForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
-// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
+// This is a helper trait for implementation `ForLt` to be able to use HRTB.
#[doc(hidden)]
pub trait WithLt<'a> {
type Of: 'a;
}
-// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
-// `CovariantForLtImpl` and it will fail to evaluate if the type is not covariant.
-unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for CovariantForLtImpl<T, WF, 0> {
+impl<T: ?Sized + for<'a> WithLt<'a>, WF> ForLt for ForLtImpl<T, WF> {
+ type Of<'a> = <T as WithLt<'a>>::Of;
+}
+
+impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for CovariantForLtImpl<T, WF, N> {
type Of<'a> = <T as WithLt<'a>>::Of;
}
+
+// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated in the `N` const generic
+// and it will fail to evaluate if the type is not covariant.
+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> CovariantForLt
+ for CovariantForLtImpl<T, WF, N>
+{
+}
diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
index 3cb094d00548..ad9809563fab 100644
--- a/rust/macros/for_lt.rs
+++ b/rust/macros/for_lt.rs
@@ -176,8 +176,10 @@ fn prove(&mut self, ty: &'a Type) {
}
}
-pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
- let (ty, lifetime) = match input {
+/// Resolve the higher-ranked type into a concrete `(ty, lifetime)` pair, expanding elided
+/// lifetimes as needed. Shared by both `for_lt` and `covariant_for_lt`.
+fn resolve_hrt(input: HigherRankedType) -> (Type, Lifetime) {
+ match input {
HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
HigherRankedType::Implicit { ty } => {
// If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime
@@ -188,7 +190,41 @@ pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
};
(ty.expand_elided_lifetime(&lifetime), lifetime)
}
- };
+ }
+}
+
+/// Produce the `'static`-substituted type for the WF check. Shared by both macros.
+fn ty_static(ty: &Type, lifetime: &Lifetime) -> Type {
+ ty.replace_lifetime(
+ lifetime,
+ &Lifetime {
+ apostrophe: Span::mixed_site(),
+ ident: format_ident!("static"),
+ },
+ )
+}
+
+pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
+ let (ty, lifetime) = resolve_hrt(input);
+
+ // Make sure that the type is wellformed when substituting lifetime with `'static`.
+ //
+ // Currently the Rust compiler doesn't check this, see the `ProveWf` documentation in
+ // `covariant_for_lt` below.
+ //
+ // We prefer to use this way of proving WF-ness as it can work when generics are involved.
+ let ty_static = ty_static(&ty, &lifetime);
+
+ quote!(
+ ::kernel::types::for_lt::ForLtImpl::<
+ dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
+ #ty_static,
+ >
+ )
+}
+
+pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
+ let (ty, lifetime) = resolve_hrt(input);
let mut prover = Prover(&lifetime, Vec::new());
prover.prove(&ty);
@@ -226,13 +262,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
// Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation.
//
// We prefer to use this way of proving WF-ness as it can work when generics are involved.
- let ty_static = ty.replace_lifetime(
- &lifetime,
- &Lifetime {
- apostrophe: Span::mixed_site(),
- ident: format_ident!("static"),
- },
- );
+ let ty_static = ty_static(&ty, &lifetime);
quote!(
::kernel::types::for_lt::CovariantForLtImpl::<
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 2167cb270928..e970769609f3 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -491,11 +491,28 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
-/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
+/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
+///
+/// Please refer to the documentation of the [`ForLt`] trait.
+///
+/// [`ForLt`]: trait.ForLt.html
+#[proc_macro]
+#[allow(non_snake_case)]
+pub fn ForLt(input: TokenStream) -> TokenStream {
+ for_lt::for_lt(parse_macro_input!(input)).into()
+}
+
+/// Obtain a type that implements [`CovariantForLt`] (and [`ForLt`]) for the given higher-ranked
+/// type.
+///
+/// Unlike [`ForLt!`], this macro additionally proves that the type is covariant over the lifetime,
+/// providing a safe [`CovariantForLt::cast_ref`] method.
///
/// Please refer to the documentation of the [`CovariantForLt`] trait.
///
/// [`CovariantForLt`]: trait.CovariantForLt.html
+/// [`CovariantForLt::cast_ref`]: trait.CovariantForLt.html#method.cast_ref
+/// [`ForLt`]: trait.ForLt.html
#[proc_macro]
#[allow(non_snake_case)]
pub fn CovariantForLt(input: TokenStream) -> TokenStream {
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 12:05 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
` (3 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Add registration_data_with() taking a for<'a> closure that receives
Pin<&'a F::Of<'a>>, which works with any ForLt type. Taking a for<'a>
closure rather than returning a direct reference prevents callers from
choosing a concrete lifetime for the data, which is required for
soundness with non-covariant ForLt types.
Extract the common null-check, TypeId-check and KBox-borrow logic into a
private registration_data_pinned() helper shared by both
registration_data_with() and the existing registration_data().
Relax Registration's bound from CovariantForLt to ForLt so that
non-covariant types can be registered.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/auxiliary.rs | 89 ++++++++++++++++++++++++++++++----------
1 file changed, 68 insertions(+), 21 deletions(-)
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 40a0af74a8e5..81549a3e347e 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -21,6 +21,7 @@
prelude::*,
types::{
CovariantForLt,
+ ForLt,
ForeignOwnable,
Opaque, //
},
@@ -270,18 +271,15 @@ pub fn parent(&self) -> &device::Device<device::Bound> {
unsafe { parent.as_bound() }
}
- /// Returns a pinned reference to the registration data set by the registering (parent) driver.
+ /// Returns the stored registration data as a pinned `'static` reference.
///
- /// `F` is the [`CovariantForLt`](trait@CovariantForLt) encoding of the data type. The returned
- /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via
- /// [`CovariantForLt::cast_ref`].
+ /// Performs null and [`TypeId`] checks, then borrows the stored [`KBox`].
///
- /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
- /// [`Registration::new()`].
+ /// # Safety
///
- /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
- /// registered by a C driver.
- pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ /// The returned `'static` lifetime was transmuted from the device's bound lifetime during
+ /// registration. Callers must shorten it before exposing it.
+ unsafe fn registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'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() {
@@ -306,10 +304,58 @@ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::O
let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) };
// SAFETY: `data` is a structurally pinned field of `RegistrationData`.
- let pinned: Pin<&F::Of<'_>> = unsafe { wrapper.map_unchecked(|w| &w.data) };
+ Ok(unsafe { wrapper.map_unchecked(|w| &w.data) })
+ }
+
+ /// Access the registration data set by the registering (parent) driver through a closure.
+ ///
+ /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The closure receives a pinned
+ /// reference to the registration data.
+ ///
+ /// For covariant types that implement [`trait@CovariantForLt`], prefer
+ /// [`registration_data`](Self::registration_data) which returns a direct reference.
+ ///
+ /// 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_with<F: ForLt + 'static, R>(
+ &self,
+ f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R,
+ ) -> Result<R> {
+ // SAFETY: The HRTB closure prevents the caller from smuggling in references with a
+ // concrete short lifetime, making the round-trip from `'static` sound regardless of
+ // variance.
+ let pinned = unsafe { self.registration_data_pinned::<F>()? };
+
+ // SAFETY: See above; the closure's HRTB makes the round-trip sound.
+ let short = unsafe { F::cast_ref_unchecked(pinned.get_ref()) };
+
+ // SAFETY: The data was pinned before the lifetime was shortened; pinning is
+ // orthogonal to lifetimes.
+ Ok(f(unsafe { Pin::new_unchecked(short) }))
+ }
- // SAFETY: The data was pinned when stored; `cast_ref` only shortens
- // the lifetime, so the pinning guarantee is preserved.
+ /// Returns a pinned reference to the registration data set by the registering (parent) driver.
+ ///
+ /// This method is only available when `F` implements [`trait@CovariantForLt`], which guarantees
+ /// safe lifetime shortening via [`CovariantForLt::cast_ref`].
+ ///
+ /// For non-covariant types, use the closure-based [`Self::registration_data_with`].
+ ///
+ /// 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<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ // SAFETY: CovariantForLt guarantees covariance, so cast_ref safely shortens the
+ // `'static` lifetime.
+ let pinned = unsafe { self.registration_data_pinned::<F>()? };
+
+ // SAFETY: The data was pinned before the lifetime was shortened; pinning is orthogonal
+ // to lifetimes.
Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) })
}
}
@@ -399,22 +445,23 @@ 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 `F` is a [`CovariantForLt`](trait@CovariantForLt) encoding of the
-/// registration data type. For non-lifetime-parameterized types, use
-/// [`CovariantForLt!(T)`](macro@CovariantForLt).
-/// The data 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()`] and
+/// [`Device::registration_data_with()`].
///
/// # Invariants
///
/// `self.adev` always holds a valid pointer to an initialized and registered
/// [`struct auxiliary_device`] whose `registration_data_rust` field points to a
/// valid `Pin<KBox<RegistrationData<F::Of<'static>>>>`.
-pub struct Registration<'a, F: CovariantForLt + 'static> {
+pub struct Registration<'a, F: ForLt + 'static> {
adev: NonNull<bindings::auxiliary_device>,
_phantom: PhantomData<F::Of<'a>>,
}
-impl<'a, F: CovariantForLt> Registration<'a, F>
+impl<'a, F: ForLt> Registration<'a, F>
where
for<'b> F::Of<'b>: Send + Sync,
{
@@ -526,7 +573,7 @@ pub fn new<E>(
}
}
-impl<F: CovariantForLt> Drop for Registration<'_, F> {
+impl<F: ForLt> Drop for Registration<'_, F> {
fn drop(&mut self) {
// SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered
// `struct auxiliary_device`.
@@ -548,7 +595,7 @@ fn drop(&mut self) {
}
// SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
-unsafe impl<F: CovariantForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
+unsafe impl<F: ForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
// SAFETY: `Registration` does not expose any methods or fields that need synchronization.
-unsafe impl<F: CovariantForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
+unsafe impl<F: ForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (2 preceding siblings ...)
2026-06-03 1:10 ` [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Extend the auxiliary driver sample to demonstrate both access patterns:
- registration_data() with CovariantForLt!(Data<'_>) for the covariant
data type that holds a plain &'bound reference.
- registration_data_with() with ForLt!(MutexData<'_>) for an invariant
data type that wraps a Mutex<&'bound Device>. Since Mutex<T> is
invariant over T, MutexData cannot implement CovariantForLt and must
use the closure-based accessor.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
samples/rust/rust_driver_auxiliary.rs | 94 +++++++++++++++++++--------
1 file changed, 68 insertions(+), 26 deletions(-)
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index 92ee6a6d348e..e441ae81fa2c 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -11,14 +11,21 @@
Core, //
},
driver,
+ new_mutex,
pci,
prelude::*,
- types::CovariantForLt,
+ sync::Mutex,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ },
InPlaceModule, //
};
const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
const AUXILIARY_NAME: &CStr = c"auxiliary";
+const COVARIANT_DEV_ID: u32 = 0;
+const INVARIANT_DEV_ID: u32 = 1;
struct AuxiliaryDriver;
@@ -56,12 +63,26 @@ struct Data<'bound> {
parent: &'bound pci::Device<Bound>,
}
+/// Registration data with interior mutability.
+///
+/// `Mutex<&'bound T>` is invariant over `'bound`, so this type cannot implement
+/// [`CovariantForLt`](trait@CovariantForLt). Access must go through the closure-based
+/// [`auxiliary::Device::registration_data_with()`].
+#[pin_data]
+struct MutexData<'bound> {
+ #[pin]
+ parent: Mutex<&'bound pci::Device<Bound>>,
+ index: u32,
+}
+
struct ParentDriver;
#[allow(clippy::type_complexity)]
+#[pin_data]
struct ParentData<'bound> {
_reg0: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
- _reg1: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
+ #[pin]
+ _reg1: auxiliary::Registration<'bound, ForLt!(MutexData<'_>)>,
}
kernel::pci_device_table!(
@@ -81,17 +102,17 @@ fn probe<'bound>(
pdev: &'bound pci::Device<Core<'_>>,
_info: &'bound Self::IdInfo,
) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
- Ok(ParentData {
+ try_pin_init!(ParentData {
// SAFETY: `ParentData` is the driver's private data, which is dropped when the
// device is unbound; i.e. `mem::forget()` is never called on it.
_reg0: unsafe {
auxiliary::Registration::new_with_lt(
pdev.as_ref(),
AUXILIARY_NAME,
- 0,
+ COVARIANT_DEV_ID,
MODULE_NAME,
Data {
- index: 0,
+ index: COVARIANT_DEV_ID,
parent: pdev,
},
)?
@@ -101,12 +122,16 @@ fn probe<'bound>(
auxiliary::Registration::new_with_lt(
pdev.as_ref(),
AUXILIARY_NAME,
- 1,
+ INVARIANT_DEV_ID,
MODULE_NAME,
- Data {
- index: 1,
- parent: pdev,
- },
+ pin_init!(MutexData {
+ parent <- {
+ let pdev: &pci::Device<Bound> = pdev;
+
+ new_mutex!(pdev)
+ },
+ index: INVARIANT_DEV_ID,
+ }),
)?
},
})
@@ -115,22 +140,39 @@ fn probe<'bound>(
impl ParentDriver {
fn connect(adev: &auxiliary::Device<Bound>) -> Result {
- let data = adev.registration_data::<CovariantForLt!(Data<'_>)>()?;
- let pdev = data.parent;
-
- dev_info!(
- pdev,
- "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
- adev.id(),
- pdev.vendor_id(),
- pdev.device_id()
- );
-
- dev_info!(
- pdev,
- "Connected to auxiliary device with index {}.\n",
- data.index
- );
+ match adev.id() {
+ // CovariantForLt types can use the direct-reference accessor.
+ COVARIANT_DEV_ID => {
+ let data = adev.registration_data::<CovariantForLt!(Data<'_>)>()?;
+ let pdev = data.parent;
+
+ dev_info!(
+ pdev,
+ "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
+ adev.id(),
+ pdev.vendor_id(),
+ pdev.device_id()
+ );
+
+ dev_info!(
+ pdev,
+ "Connected to auxiliary device with index {}.\n",
+ data.index
+ );
+ }
+ // Invariant ForLt types (e.g. containing a Mutex) require the closure-based accessor.
+ INVARIANT_DEV_ID => {
+ adev.registration_data_with::<ForLt!(MutexData<'_>), _>(|data| {
+ let pdev = *data.parent.lock();
+ dev_info!(
+ pdev,
+ "Connected to auxiliary device with index {} (via Mutex).\n",
+ data.index
+ );
+ })?;
+ }
+ _ => return Err(EINVAL),
+ }
Ok(())
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 5/7] rust: devres: add DevresLt for ForLt-aware device resource access
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (3 preceding siblings ...)
2026-06-03 1:10 ` [PATCH v2 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
6 siblings, 0 replies; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Devres<T> stores resources as T and returns &'a T from access(). For
lifetime-parameterized types like Bar<'a, SIZE> that are transmuted to
'static for storage, this exposes the synthetic 'static lifetime to
callers -- any method on the stored type that returns a reference with
its lifetime parameter would yield a &'static reference, which is
unsound.
Add DevresLt<F: ForLt>, a thin wrapper around Devres<F::Of<'static>>
that applies ForLt's lifetime-shortening operations in all access
methods to shorten the stored 'static lifetime to the caller's borrow
lifetime.
DevresLt::new() is unsafe because the caller must guarantee that the
data remains valid for the device's full bound scope; the internal
transmute from F::Of<'a> to F::Of<'static> would otherwise allow
use-after-free.
Two access patterns are provided:
- CovariantForLt types get direct-reference accessors (access,
try_access) that return shortened references via
CovariantForLt::cast_ref.
- Plain ForLt types use closure-based accessors (access_with,
try_access_with) whose universally quantified lifetime prevents
callers from smuggling in concrete short-lived references.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/devres.rs | 110 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 110 insertions(+)
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 82cbd8b969fb..23db91377f5e 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -24,6 +24,8 @@
Arc, //
},
types::{
+ CovariantForLt,
+ ForLt,
ForeignOwnable,
Opaque, //
},
@@ -365,6 +367,114 @@ fn drop(&mut self) {
}
}
+/// Guard returned by [`DevresLt::try_access`].
+///
+/// Dereferences to `F::Of<'a>`, shortening the lifetime of the stored data to the guard's borrow
+/// lifetime.
+pub struct DevresGuard<'a, F: CovariantForLt>(RevocableGuard<'a, F::Of<'static>>);
+
+impl<'a, F: CovariantForLt> core::ops::Deref for DevresGuard<'a, F> {
+ type Target = F::Of<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ F::cast_ref(&*self.0)
+ }
+}
+
+/// Device-managed resource with [`ForLt`](trait@ForLt)-aware access.
+///
+/// `DevresLt` wraps [`Devres`] and shortens the stored `'static` lifetime to the caller's borrow
+/// lifetime in all access methods.
+///
+/// Types that implement [`trait@CovariantForLt`] get direct-reference accessors ([`Self::access`],
+/// [`Self::try_access`]). Plain [`ForLt`](trait@ForLt) types use closure-based accessors
+/// ([`Self::access_with`], [`Self::try_access_with`]).
+pub struct DevresLt<F: ForLt>(Devres<F::Of<'static>>)
+where
+ F::Of<'static>: Send;
+
+impl<F: ForLt> DevresLt<F>
+where
+ F::Of<'static>: Send,
+{
+ /// Creates a new [`DevresLt`] instance of the given `data`.
+ ///
+ /// # Safety
+ ///
+ /// The data must remain valid for the device's full bound scope. [`DevresLt`] allows
+ /// access until the device is unbound, which may outlast `'a`.
+ pub unsafe fn new<'a, E>(
+ dev: &'a Device<Bound>,
+ data: impl PinInit<F::Of<'a>, E>,
+ ) -> Result<Self>
+ where
+ Error: From<E>,
+ {
+ // SAFETY: The caller guarantees the data is valid for the device's full bound scope.
+ // Lifetimes do not affect layout, so F::Of<'a> and F::Of<'static> have identical
+ // representation; casting the slot pointer is sound.
+ let data = unsafe {
+ pin_init::pin_init_from_closure::<F::Of<'static>, E>(move |slot| {
+ data.__pinned_init(slot.cast())
+ })
+ };
+
+ Ok(Self(Devres::new(dev, data)?))
+ }
+
+ /// Return a reference of the [`Device`] this [`DevresLt`] instance has been created with.
+ pub fn device(&self) -> &Device {
+ self.0.device()
+ }
+
+ /// Obtain `&F::Of<'_>`, bypassing the [`Revocable`], through a closure.
+ ///
+ /// This method works like [`DevresLt::access`](DevresLt::access) but accepts any
+ /// [`trait@ForLt`] type, not just [`trait@CovariantForLt`].
+ pub fn access_with<R, G>(&self, dev: &Device<Bound>, f: G) -> Result<R>
+ where
+ G: for<'a> FnOnce(&'a F::Of<'a>) -> R,
+ {
+ self.0.access(dev).map(|data| {
+ // SAFETY: The closure's HRTB `for<'a>` prevents the caller from smuggling in
+ // references with a concrete short lifetime, making the round-trip from `'static`
+ // sound regardless of variance.
+ f(unsafe { F::cast_ref_unchecked(data) })
+ })
+ }
+
+ /// [`DevresLt`] accessor for [`Revocable::try_access_with`].
+ pub fn try_access_with<R, G>(&self, f: G) -> Option<R>
+ where
+ G: for<'a> FnOnce(&'a F::Of<'a>) -> R,
+ {
+ self.0.data().try_access_with(|data| {
+ // SAFETY: The closure's HRTB `for<'a>` prevents the caller from smuggling in
+ // references with a concrete short lifetime, making the round-trip from `'static`
+ // sound regardless of variance.
+ f(unsafe { F::cast_ref_unchecked(data) })
+ })
+ }
+}
+
+impl<F: CovariantForLt> DevresLt<F>
+where
+ F::Of<'static>: Send,
+{
+ /// Obtain `&'a F::Of<'a>`, bypassing the [`Revocable`].
+ ///
+ /// This method works like [`Devres::access`], but shortens the returned reference's lifetime
+ /// from `'static` to `'a` via [`CovariantForLt::cast_ref`].
+ pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a F::Of<'a>> {
+ self.0.access(dev).map(F::cast_ref)
+ }
+
+ /// [`DevresLt`] accessor for [`Revocable::try_access`].
+ pub fn try_access(&self) -> Option<DevresGuard<'_, F>> {
+ self.0.data().try_access().map(DevresGuard)
+ }
+}
+
/// 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] 11+ messages in thread
* [PATCH v2 6/7] rust: pci: return DevresLt from Bar::into_devres()
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (4 preceding siblings ...)
2026-06-03 1:10 ` [PATCH v2 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
6 siblings, 0 replies; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Implement ForLt and CovariantForLt for Bar<'static, SIZE> so that
DevresLt can shorten the stored 'static lifetime back to the caller's
borrow lifetime.
CovariantForLt is sound because Bar<'a, SIZE> only holds &'a
Device<Bound>, which is covariant over 'a.
Since DevresLt::new() handles the lifetime transmutation internally,
into_devres() no longer needs an explicit transmute to Bar<'static>.
Add a DevresBar<SIZE> type alias for convenience.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/pci.rs | 1 +
rust/kernel/pci/io.rs | 37 ++++++++++++++++++++++++++-----------
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 5071cae6543f..f783b9d9fa26 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -45,6 +45,7 @@
ConfigSpace,
ConfigSpaceKind,
ConfigSpaceSize,
+ DevresBar,
Extended,
Normal, //
};
diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
index 0461e01aaa20..7a0d2d74129d 100644
--- a/rust/kernel/pci/io.rs
+++ b/rust/kernel/pci/io.rs
@@ -6,7 +6,7 @@
use crate::{
bindings,
device,
- devres::Devres,
+ devres::DevresLt,
io::{
Io,
IoCapable,
@@ -14,7 +14,11 @@
Mmio,
MmioRaw, //
},
- prelude::*, //
+ prelude::*,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ }, //
};
use core::{
marker::PhantomData,
@@ -151,6 +155,19 @@ pub struct Bar<'a, const SIZE: usize = 0> {
num: i32,
}
+impl<const SIZE: usize> ForLt for Bar<'static, SIZE> {
+ type Of<'a> = Bar<'a, SIZE>;
+}
+
+// SAFETY: `Bar<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,
+// which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for Bar<'static, SIZE> {}
+
+/// A device-managed PCI BAR mapping.
+///
+/// See [`Bar::into_devres`].
+pub type DevresBar<const SIZE: usize> = DevresLt<Bar<'static, SIZE>>;
+
impl<'a, const SIZE: usize> Bar<'a, SIZE> {
pub(super) fn new(
pdev: &'a Device<device::Bound>,
@@ -223,15 +240,13 @@ fn release(&self) {
/// 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)
+ /// The returned [`DevresBar`] can outlive the original borrow and be stored in driver data.
+ /// Access to the BAR is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresBar<SIZE>> {
+ let pdev = self.pdev;
+ // SAFETY: `Bar` only holds a reference to the device and an I/O mapping, both of which
+ // remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(pdev.as_ref(), self) }
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres()
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (5 preceding siblings ...)
2026-06-03 1:10 ` [PATCH v2 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
@ 2026-06-03 1:10 ` Danilo Krummrich
6 siblings, 0 replies; 11+ messages in thread
From: Danilo Krummrich @ 2026-06-03 1:10 UTC (permalink / raw)
To: gregkh, rafael, dakr, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, acourbot, ecourtney, m.wilczynski,
david.m.ertman, ira.weiny, leon, daniel.almeida, bhelgaas,
kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
Implement ForLt and CovariantForLt for IoMem<'static, SIZE> and
ExclusiveIoMem<'static, SIZE> so that DevresLt can shorten the stored
'static lifetime back to the caller's borrow lifetime.
CovariantForLt is sound because both types only hold &'a Device<Bound>,
which is covariant over 'a.
Since DevresLt::new() handles the lifetime transmutation internally,
into_devres() no longer needs an explicit transmute to 'static.
Add DevresIoMem<SIZE> and DevresExclusiveIoMem<SIZE> type aliases.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/pwm/pwm_th1520.rs | 5 ++-
rust/kernel/io/mem.rs | 65 +++++++++++++++++++++++++++------------
2 files changed, 47 insertions(+), 23 deletions(-)
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
index 48808cd80737..7e3a06ed369f 100644
--- a/drivers/pwm/pwm_th1520.rs
+++ b/drivers/pwm/pwm_th1520.rs
@@ -24,9 +24,8 @@
use kernel::{
clk::Clk,
device::{Bound, Core, Device},
- devres,
io::{
- mem::IoMem,
+ mem::DevresIoMem,
Io, //
},
of, platform,
@@ -92,7 +91,7 @@ struct Th1520WfHw {
#[pin_data(PinnedDrop)]
struct Th1520PwmDriverData {
#[pin]
- iomem: devres::Devres<IoMem<'static, TH1520_PWM_REG_SIZE>>,
+ iomem: DevresIoMem<TH1520_PWM_REG_SIZE>,
clk: Clk,
}
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index fc2a3e24f8d5..4691a01a3cf7 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -9,7 +9,7 @@
Bound,
Device, //
},
- devres::Devres,
+ devres::DevresLt,
io::{
self,
resource::{
@@ -20,6 +20,10 @@
MmioRaw, //
},
prelude::*,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ },
};
/// An IO request for a specific device and resource.
@@ -172,6 +176,19 @@ pub struct ExclusiveIoMem<'a, const SIZE: usize> {
_region: Region,
}
+impl<const SIZE: usize> ForLt for ExclusiveIoMem<'static, SIZE> {
+ type Of<'a> = ExclusiveIoMem<'a, SIZE>;
+}
+
+// SAFETY: `ExclusiveIoMem<'a, SIZE>` is covariant over `'a`; it holds an `IoMem<'a, SIZE>`,
+// which holds `&'a Device<Bound>`, which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for ExclusiveIoMem<'static, SIZE> {}
+
+/// A device-managed exclusive I/O memory region.
+///
+/// See [`ExclusiveIoMem::into_devres`].
+pub type DevresExclusiveIoMem<const SIZE: usize> = DevresLt<ExclusiveIoMem<'static, SIZE>>;
+
impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
/// Creates a new `ExclusiveIoMem` instance.
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
@@ -198,15 +215,13 @@ fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
/// 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)
+ /// The returned [`DevresExclusiveIoMem`] can outlive the original borrow and be stored in
+ /// driver data. Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresExclusiveIoMem<SIZE>> {
+ let dev = self.iomem.dev;
+ // SAFETY: `ExclusiveIoMem` only holds a device reference and an I/O mapping, both of
+ // which remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
@@ -232,6 +247,19 @@ pub struct IoMem<'a, const SIZE: usize = 0> {
io: MmioRaw<SIZE>,
}
+impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> {
+ type Of<'a> = IoMem<'a, SIZE>;
+}
+
+// SAFETY: `IoMem<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,
+// which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for IoMem<'static, SIZE> {}
+
+/// A device-managed I/O memory region.
+///
+/// See [`IoMem::into_devres`].
+pub type DevresIoMem<const SIZE: usize> = DevresLt<IoMem<'static, SIZE>>;
+
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
@@ -271,16 +299,13 @@ fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
/// 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)
+ /// The returned [`DevresIoMem`] can outlive the original borrow and be stored in driver data.
+ /// Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresIoMem<SIZE>> {
+ let dev = self.dev;
+ // SAFETY: `IoMem` only holds a device reference and an I/O mapping, both of which
+ // remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt
2026-06-03 1:10 ` [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
@ 2026-06-03 11:59 ` Gary Guo
0 siblings, 0 replies; 11+ messages in thread
From: Gary Guo @ 2026-06-03 11:59 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, acourbot, ecourtney,
m.wilczynski, david.m.ertman, ira.weiny, leon, daniel.almeida,
bhelgaas, kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
On Wed Jun 3, 2026 at 2:10 AM BST, Danilo Krummrich wrote:
> Rename ForLt to CovariantForLt to prepare for the introduction of a new
> ForLt base trait that does not require covariance.
>
> The existing ForLt trait requires covariance, which enables the safe
> cast_ref() method. This rename preserves the same semantics under a more
> precise name, making room for a weaker ForLt trait in a subsequent
> commit.
>
> No functional change.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> drivers/gpu/nova-core/driver.rs | 4 +-
> rust/kernel/auxiliary.rs | 23 +++++-----
> rust/kernel/types.rs | 2 +-
> rust/kernel/types/for_lt.rs | 61 ++++++++++++++-------------
> rust/macros/for_lt.rs | 4 +-
> rust/macros/lib.rs | 11 +++--
> samples/rust/rust_driver_auxiliary.rs | 8 ++--
> 7 files changed, 57 insertions(+), 56 deletions(-)
>
> diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
> index d44323c28e8d..ef510ab6c092 100644
> --- a/rust/kernel/types/for_lt.rs
> +++ b/rust/kernel/types/for_lt.rs
> @@ -1,8 +1,8 @@
> // SPDX-License-Identifier: Apache-2.0 OR MIT
>
> -//! Provide implementation and test of the `ForLt` trait and macro.
> +//! Provide implementation and test of the `CovariantForLt` trait and macro.
> //!
> -//! This module is hidden and user should just use `ForLt!` directly.
> +//! This module is hidden and user should just use `CovariantForLt!` directly.
>
> use core::marker::PhantomData;
>
> @@ -15,38 +15,39 @@
> ///
> /// # Macro
> ///
> -/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
> -/// type that implements this trait.
> +/// It is not recommended to implement this trait directly. `CovariantForLt!` macro is provided to
> +/// obtain a type that implements this trait.
> ///
> /// The full syntax is
> ///
> /// ```
> -/// # use kernel::types::ForLt;
> -/// # fn expect_lt<F: ForLt>() {}
> +/// # use kernel::types::CovariantForLt;
> +/// # fn expect_lt<F: CovariantForLt>() {}
> /// # struct TypeThatUse<'a>(&'a ());
> /// # expect_lt::<
> -/// ForLt!(for<'a> TypeThatUse<'a>)
> +/// CovariantForLt!(for<'a> TypeThatUse<'a>)
> /// # >();
> /// ```
> ///
> -/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
> +/// which gives a type so that
> +/// `<CovariantForLt!(for<'a> TypeThatUse<'a>) as CovariantForLt>::Of<'b>`
> /// is `TypeThatUse<'b>`.
> ///
> /// You may also use a short-hand syntax which works similar to lifetime elision.
> /// The macro also accepts types that do not involve a lifetime at all.
> ///
> /// ```
> -/// # use kernel::types::ForLt;
> -/// # fn expect_lt<F: ForLt>() {}
> +/// # use kernel::types::CovariantForLt;
> +/// # fn expect_lt<F: CovariantForLt>() {}
> /// # struct TypeThatUse<'a>(&'a ());
> /// # expect_lt::<
> -/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
> +/// CovariantForLt!(TypeThatUse<'_>) // Equivalent to `CovariantForLt!(for<'a> TypeThatUse<'a>)`.
> /// # >();
> /// # expect_lt::<
> -/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
> +/// CovariantForLt!(&u32) // Equivalent to `CovariantForLt!(for<'a> &'a u32)`.
> /// # >();
> /// # expect_lt::<
> -/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
> +/// CovariantForLt!(u32) // Equivalent to `CovariantForLt!(for<'a> u32)`.
> /// # >();
> /// ```
> ///
> @@ -55,10 +56,10 @@
> /// it.
> ///
> /// ```ignore,compile_fail
> -/// # use kernel::types::ForLt;
> -/// # fn expect_lt<F: ForLt>() {}
> +/// # use kernel::types::CovariantForLt;
> +/// # fn expect_lt<F: CovariantForLt>() {}
> /// # expect_lt::<
> -/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.
> +/// CovariantForLt!(fn(&u32)) // Contravariant, will fail compilation.
> /// # >();
> /// ```
> ///
> @@ -67,23 +68,23 @@
> /// the generic parameter but is in a separate item.
> ///
> /// ```
> -/// # use kernel::types::ForLt;
> -/// fn expect_lt<F: ForLt>() {}
> +/// # use kernel::types::CovariantForLt;
> +/// fn expect_lt<F: CovariantForLt>() {}
> /// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")]
> /// fn generic_fn<T: 'static>() {
> /// // Syntactically proven by the macro
> -/// expect_lt::<ForLt!(&T)>();
> +/// expect_lt::<CovariantForLt!(&T)>();
> /// // Syntactically proven by the macro
> -/// expect_lt::<ForLt!(&KBox<T>)>();
> +/// expect_lt::<CovariantForLt!(&KBox<T>)>();
> /// // Cannot be syntactically proven, need to check covariance of `KBox`
> -/// // expect_lt::<ForLt!(&KBox<&T>)>();
> +/// // expect_lt::<CovariantForLt!(&KBox<&T>)>();
> /// }
> /// ```
> ///
> /// # Safety
> ///
> /// `Self::Of<'a>` must be covariant over the lifetime `'a`.
> -pub unsafe trait ForLt {
> +pub unsafe trait CovariantForLt {
> /// The type parameterized by the lifetime.
> type Of<'a>: 'a;
>
> @@ -94,11 +95,11 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
> unsafe { core::mem::transmute(long) }
> }
> }
> -pub use macros::ForLt;
> +pub use macros::CovariantForLt;
>
> -/// This is intended to be an "unsafe-to-refer-to" type.
> +/// Helper type for the `CovariantForLt!` macro.
> ///
> -/// Must only be used by the `ForLt!` macro.
> +/// Must only be used by the `CovariantForLt!` macro.
> ///
> /// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.
> ///
> @@ -107,16 +108,16 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
> /// `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)>);
> +pub struct CovariantForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
I think I prefer this name to be stayed the same (so it has the "Unsafe" in it
and is not too long).
You can actually use this same helper type for both `ForLt!` and
`CovariantForLt!` later in patch 2, by using different `N` based on whether
covariance check has been conducted. So
impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for UnsafeForLtImpl<T, WF, N> {
type Of<'a> = <T as WithLt<'a>>::Of;
}
impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 1> {}
Best,
Gary
>
> -// This is a helper trait for implementation `ForLt` to be able to use HRTB.
> +// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
> #[doc(hidden)]
> pub trait WithLt<'a> {
> type Of: 'a;
> }
>
> -// 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> {
> +// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
> +// `CovariantForLtImpl` and it will fail to evaluate if the type is not covariant.
> +unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for CovariantForLtImpl<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
> index 364d4113cd10..3cb094d00548 100644
> --- a/rust/macros/for_lt.rs
> +++ b/rust/macros/for_lt.rs
> @@ -176,7 +176,7 @@ fn prove(&mut self, ty: &'a Type) {
> }
> }
>
> -pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
> +pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> let (ty, lifetime) = match input {
> HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
> HigherRankedType::Implicit { ty } => {
> @@ -235,7 +235,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
> );
>
> quote!(
> - ::kernel::types::for_lt::UnsafeForLtImpl::<
> + ::kernel::types::for_lt::CovariantForLtImpl::<
> dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
> #ty_static,
> {
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 4a48fabbc268..2167cb270928 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -491,14 +491,13 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
> .into()
> }
>
> -/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
> +/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
> ///
> -/// Please refer to the documentation of the [`ForLt`] trait.
> +/// Please refer to the documentation of the [`CovariantForLt`] trait.
> ///
> -/// [`ForLt`]: trait.ForLt.html
> +/// [`CovariantForLt`]: trait.CovariantForLt.html
> #[proc_macro]
> -// The macro shares the name with the trait.
> #[allow(non_snake_case)]
> -pub fn ForLt(input: TokenStream) -> TokenStream {
> - for_lt::for_lt(parse_macro_input!(input)).into()
> +pub fn CovariantForLt(input: TokenStream) -> TokenStream {
> + for_lt::covariant_for_lt(parse_macro_input!(input)).into()
> }
> diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
> index 2c1351040e45..92ee6a6d348e 100644
> --- a/samples/rust/rust_driver_auxiliary.rs
> +++ b/samples/rust/rust_driver_auxiliary.rs
> @@ -13,7 +13,7 @@
> driver,
> pci,
> prelude::*,
> - types::ForLt,
> + types::CovariantForLt,
> InPlaceModule, //
> };
>
> @@ -60,8 +60,8 @@ struct Data<'bound> {
>
> #[allow(clippy::type_complexity)]
> struct ParentData<'bound> {
> - _reg0: auxiliary::Registration<'bound, ForLt!(Data<'_>)>,
> - _reg1: auxiliary::Registration<'bound, ForLt!(Data<'_>)>,
> + _reg0: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
> + _reg1: auxiliary::Registration<'bound, CovariantForLt!(Data<'_>)>,
> }
>
> kernel::pci_device_table!(
> @@ -115,7 +115,7 @@ fn probe<'bound>(
>
> impl ParentDriver {
> fn connect(adev: &auxiliary::Device<Bound>) -> Result {
> - let data = adev.registration_data::<ForLt!(Data<'_>)>()?;
> + let data = adev.registration_data::<CovariantForLt!(Data<'_>)>()?;
> let pdev = data.parent;
>
> dev_info!(
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt
2026-06-03 1:10 ` [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
@ 2026-06-03 12:04 ` Gary Guo
0 siblings, 0 replies; 11+ messages in thread
From: Gary Guo @ 2026-06-03 12:04 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, acourbot, ecourtney,
m.wilczynski, david.m.ertman, ira.weiny, leon, daniel.almeida,
bhelgaas, kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
On Wed Jun 3, 2026 at 2:10 AM BST, Danilo Krummrich wrote:
> Add a new ForLt trait as a base for CovariantForLt:
>
> - ForLt (non-unsafe): represents a type generic over a lifetime, with
> no covariance guarantee. Provides unsafe fn cast_ref_unchecked()
> for callers that can justify lifetime shortening via a round-trip
> safety argument.
>
> - CovariantForLt (unsafe): becomes a subtrait of ForLt that
> additionally proves the type is covariant over its lifetime
> parameter, providing a safe cast_ref() method.
>
> This split allows non-covariant types (e.g. types behind a Mutex) to
> implement ForLt and participate in DevresLt / registration data patterns
> where the round-trip argument suffices, without requiring a covariance
> proof that would fail to compile.
>
> The internal macro backend is split accordingly: ForLt! emits ForLtImpl
> (no covariance proof), CovariantForLt! emits CovariantForLtImpl (with
> compile-time covariance proof).
>
> No existing callers change; they all use CovariantForLt.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/types.rs | 1 +
> rust/kernel/types/for_lt.rs | 101 ++++++++++++++++++++++++++++++------
> rust/macros/for_lt.rs | 50 ++++++++++++++----
> rust/macros/lib.rs | 19 ++++++-
> 4 files changed, 145 insertions(+), 26 deletions(-)
>
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index cbe6907042d3..c1ed05d1046c 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -14,6 +14,7 @@
> #[doc(hidden)]
> pub mod for_lt;
> pub use for_lt::CovariantForLt;
> +pub use for_lt::ForLt;
>
> /// Used to transfer ownership to and from foreign (non-Rust) languages.
> ///
> diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
> index ef510ab6c092..e1774b03dd1f 100644
> --- a/rust/kernel/types/for_lt.rs
> +++ b/rust/kernel/types/for_lt.rs
> @@ -1,17 +1,74 @@
> // SPDX-License-Identifier: Apache-2.0 OR MIT
>
> -//! Provide implementation and test of the `CovariantForLt` trait and macro.
> +//! Provide implementation and test of the `ForLt` and `CovariantForLt` traits and macros.
> //!
> -//! This module is hidden and user should just use `CovariantForLt!` directly.
> +//! This module is hidden and users should just use `ForLt!` / `CovariantForLt!` directly.
>
> use core::marker::PhantomData;
>
> /// Representation of types generic over a lifetime.
> ///
> -/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter
> -/// can be soundly shortened.
> +/// # Macro
> +///
> +/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
> +/// type that implements this trait.
> +///
> +/// The full syntax is
> +///
> +/// ```
> +/// # use kernel::types::ForLt;
> +/// # fn expect_lt<F: ForLt>() {}
> +/// # struct TypeThatUse<'a>(&'a ());
> +/// # expect_lt::<
> +/// ForLt!(for<'a> TypeThatUse<'a>)
> +/// # >();
> +/// ```
> +///
> +/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
> +/// is `TypeThatUse<'b>`.
> +///
> +/// You may also use a short-hand syntax which works similar to lifetime elision.
> +/// The macro also accepts types that do not involve a lifetime at all.
> +///
> +/// ```
> +/// # use kernel::types::ForLt;
> +/// # fn expect_lt<F: ForLt>() {}
> +/// # struct TypeThatUse<'a>(&'a ());
> +/// # expect_lt::<
> +/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
> +/// # >();
> +/// # expect_lt::<
> +/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
> +/// # >();
> +/// # expect_lt::<
> +/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
> +/// # >();
> +/// ```
> +pub trait ForLt {
> + /// The type parameterized by the lifetime.
> + type Of<'a>: 'a;
> +
> + /// Cast a reference to a shorter lifetime.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that the lifetime shortening is sound for their use case,
> + /// e.g. because the `'long` reference originated from a `'short`-to-`'static` transmute
> + /// and this is the reverse leg of that round-trip.
> + #[inline(always)]
> + unsafe fn cast_ref_unchecked<'r, 'short: 'r, 'long: 'short>(
> + long: &'r Self::Of<'long>,
> + ) -> &'r Self::Of<'short> {
> + // SAFETY: Caller guarantees the lifetime shortening is sound.
> + unsafe { core::mem::transmute(long) }
> + }
I think this method should just be gone.
> +}
> +pub use macros::ForLt;
> +
> +/// [`trait@ForLt`] subtrait for types that are covariant over their lifetime parameter.
> ///
> -/// The lifetime involved must be covariant.
> +/// Provides a safe [`cast_ref`](CovariantForLt::cast_ref) method for types that are proven to be
> +/// covariant. The `CovariantForLt!` macro syntax is the same as `ForLt!`.
> ///
> /// # Macro
> ///
> @@ -84,10 +141,7 @@
> /// # Safety
> ///
> /// `Self::Of<'a>` must be covariant over the lifetime `'a`.
> -pub unsafe trait CovariantForLt {
> - /// The type parameterized by the lifetime.
> - type Of<'a>: 'a;
> -
> +pub unsafe trait CovariantForLt: ForLt {
> /// Cast a reference to a shorter lifetime.
> #[inline(always)]
> fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {
> @@ -97,27 +151,44 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
> }
> pub use macros::CovariantForLt;
>
> -/// Helper type for the `CovariantForLt!` macro.
> +/// Helper type for the `ForLt!` macro.
> ///
> -/// Must only be used by the `CovariantForLt!` macro.
> +/// 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.
> +#[doc(hidden)]
> +pub struct ForLtImpl<T: ?Sized, WF>(PhantomData<(WF, T)>);
The `N` is still needed for `ForLtImpl` in case there needs to be a
wellformedness check. Currently the macro emits a `struct` to serve
wellformedness-check purpose and a `fn` to check for covariance, and the first
part still needs to be kept (at least until Rust starts checking WF-ness for
`dyn`).
If you follow my suggestion in patch 1, you should be able to share macro impl
for `CovariantForLt` and `ForLt`, and just have a boolean to decide whether to
to emit the function that check for variance.
Best,
Gary
> +
> +/// Helper type for the `CovariantForLt!` macro.
> +///
> +/// Must only be used by the `CovariantForLt!` macro.
> +///
> +/// `T` and `WF` are the same as in [`ForLtImpl`].
> ///
> /// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove
> /// additional properties.
> #[doc(hidden)]
> pub struct CovariantForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
>
> -// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
> +// This is a helper trait for implementation `ForLt` to be able to use HRTB.
> #[doc(hidden)]
> pub trait WithLt<'a> {
> type Of: 'a;
> }
>
> -// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
> -// `CovariantForLtImpl` and it will fail to evaluate if the type is not covariant.
> -unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for CovariantForLtImpl<T, WF, 0> {
> +impl<T: ?Sized + for<'a> WithLt<'a>, WF> ForLt for ForLtImpl<T, WF> {
> + type Of<'a> = <T as WithLt<'a>>::Of;
> +}
> +
> +impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for CovariantForLtImpl<T, WF, N> {
> type Of<'a> = <T as WithLt<'a>>::Of;
> }
> +
> +// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated in the `N` const generic
> +// and it will fail to evaluate if the type is not covariant.
> +unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> CovariantForLt
> + for CovariantForLtImpl<T, WF, N>
> +{
> +}
> diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
> index 3cb094d00548..ad9809563fab 100644
> --- a/rust/macros/for_lt.rs
> +++ b/rust/macros/for_lt.rs
> @@ -176,8 +176,10 @@ fn prove(&mut self, ty: &'a Type) {
> }
> }
>
> -pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> - let (ty, lifetime) = match input {
> +/// Resolve the higher-ranked type into a concrete `(ty, lifetime)` pair, expanding elided
> +/// lifetimes as needed. Shared by both `for_lt` and `covariant_for_lt`.
> +fn resolve_hrt(input: HigherRankedType) -> (Type, Lifetime) {
> + match input {
> HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
> HigherRankedType::Implicit { ty } => {
> // If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime
> @@ -188,7 +190,41 @@ pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> };
> (ty.expand_elided_lifetime(&lifetime), lifetime)
> }
> - };
> + }
> +}
> +
> +/// Produce the `'static`-substituted type for the WF check. Shared by both macros.
> +fn ty_static(ty: &Type, lifetime: &Lifetime) -> Type {
> + ty.replace_lifetime(
> + lifetime,
> + &Lifetime {
> + apostrophe: Span::mixed_site(),
> + ident: format_ident!("static"),
> + },
> + )
> +}
> +
> +pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
> + let (ty, lifetime) = resolve_hrt(input);
> +
> + // Make sure that the type is wellformed when substituting lifetime with `'static`.
> + //
> + // Currently the Rust compiler doesn't check this, see the `ProveWf` documentation in
> + // `covariant_for_lt` below.
> + //
> + // We prefer to use this way of proving WF-ness as it can work when generics are involved.
> + let ty_static = ty_static(&ty, &lifetime);
> +
> + quote!(
> + ::kernel::types::for_lt::ForLtImpl::<
> + dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
> + #ty_static,
> + >
> + )
> +}
> +
> +pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
> + let (ty, lifetime) = resolve_hrt(input);
>
> let mut prover = Prover(&lifetime, Vec::new());
> prover.prove(&ty);
> @@ -226,13 +262,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
> // Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation.
> //
> // We prefer to use this way of proving WF-ness as it can work when generics are involved.
> - let ty_static = ty.replace_lifetime(
> - &lifetime,
> - &Lifetime {
> - apostrophe: Span::mixed_site(),
> - ident: format_ident!("static"),
> - },
> - );
> + let ty_static = ty_static(&ty, &lifetime);
>
> quote!(
> ::kernel::types::for_lt::CovariantForLtImpl::<
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 2167cb270928..e970769609f3 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -491,11 +491,28 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
> .into()
> }
>
> -/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
> +/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
> +///
> +/// Please refer to the documentation of the [`ForLt`] trait.
> +///
> +/// [`ForLt`]: trait.ForLt.html
> +#[proc_macro]
> +#[allow(non_snake_case)]
> +pub fn ForLt(input: TokenStream) -> TokenStream {
> + for_lt::for_lt(parse_macro_input!(input)).into()
> +}
> +
> +/// Obtain a type that implements [`CovariantForLt`] (and [`ForLt`]) for the given higher-ranked
> +/// type.
> +///
> +/// Unlike [`ForLt!`], this macro additionally proves that the type is covariant over the lifetime,
> +/// providing a safe [`CovariantForLt::cast_ref`] method.
> ///
> /// Please refer to the documentation of the [`CovariantForLt`] trait.
> ///
> /// [`CovariantForLt`]: trait.CovariantForLt.html
> +/// [`CovariantForLt::cast_ref`]: trait.CovariantForLt.html#method.cast_ref
> +/// [`ForLt`]: trait.ForLt.html
> #[proc_macro]
> #[allow(non_snake_case)]
> pub fn CovariantForLt(input: TokenStream) -> TokenStream {
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types
2026-06-03 1:10 ` [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
@ 2026-06-03 12:05 ` Gary Guo
0 siblings, 0 replies; 11+ messages in thread
From: Gary Guo @ 2026-06-03 12:05 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, acourbot, ecourtney,
m.wilczynski, david.m.ertman, ira.weiny, leon, daniel.almeida,
bhelgaas, kwilczynski
Cc: driver-core, linux-kernel, nova-gpu, dri-devel, linux-pwm,
rust-for-linux
On Wed Jun 3, 2026 at 2:10 AM BST, Danilo Krummrich wrote:
> Add registration_data_with() taking a for<'a> closure that receives
> Pin<&'a F::Of<'a>>, which works with any ForLt type. Taking a for<'a>
> closure rather than returning a direct reference prevents callers from
> choosing a concrete lifetime for the data, which is required for
> soundness with non-covariant ForLt types.
>
> Extract the common null-check, TypeId-check and KBox-borrow logic into a
> private registration_data_pinned() helper shared by both
> registration_data_with() and the existing registration_data().
>
> Relax Registration's bound from CovariantForLt to ForLt so that
> non-covariant types can be registered.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/auxiliary.rs | 89 ++++++++++++++++++++++++++++++----------
> 1 file changed, 68 insertions(+), 21 deletions(-)
>
> diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
> index 40a0af74a8e5..81549a3e347e 100644
> --- a/rust/kernel/auxiliary.rs
> +++ b/rust/kernel/auxiliary.rs
> @@ -21,6 +21,7 @@
> prelude::*,
> types::{
> CovariantForLt,
> + ForLt,
> ForeignOwnable,
> Opaque, //
> },
> @@ -270,18 +271,15 @@ pub fn parent(&self) -> &device::Device<device::Bound> {
> unsafe { parent.as_bound() }
> }
>
> - /// Returns a pinned reference to the registration data set by the registering (parent) driver.
> + /// Returns the stored registration data as a pinned `'static` reference.
> ///
> - /// `F` is the [`CovariantForLt`](trait@CovariantForLt) encoding of the data type. The returned
> - /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via
> - /// [`CovariantForLt::cast_ref`].
> + /// Performs null and [`TypeId`] checks, then borrows the stored [`KBox`].
> ///
> - /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
> - /// [`Registration::new()`].
> + /// # Safety
> ///
> - /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
> - /// registered by a C driver.
> - pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
> + /// The returned `'static` lifetime was transmuted from the device's bound lifetime during
> + /// registration. Callers must shorten it before exposing it.
> + unsafe fn registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'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() {
> @@ -306,10 +304,58 @@ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::O
> let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) };
>
> // SAFETY: `data` is a structurally pinned field of `RegistrationData`.
> - let pinned: Pin<&F::Of<'_>> = unsafe { wrapper.map_unchecked(|w| &w.data) };
> + Ok(unsafe { wrapper.map_unchecked(|w| &w.data) })
> + }
> +
> + /// Access the registration data set by the registering (parent) driver through a closure.
> + ///
> + /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The closure receives a pinned
> + /// reference to the registration data.
> + ///
> + /// For covariant types that implement [`trait@CovariantForLt`], prefer
> + /// [`registration_data`](Self::registration_data) which returns a direct reference.
> + ///
> + /// 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_with<F: ForLt + 'static, R>(
> + &self,
> + f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R,
> + ) -> Result<R> {
> + // SAFETY: The HRTB closure prevents the caller from smuggling in references with a
> + // concrete short lifetime, making the round-trip from `'static` sound regardless of
> + // variance.
> + let pinned = unsafe { self.registration_data_pinned::<F>()? };
> +
> + // SAFETY: See above; the closure's HRTB makes the round-trip sound.
> + let short = unsafe { F::cast_ref_unchecked(pinned.get_ref()) };
This shouldn't be necessary, you can just directly call `f`.
Best,
Gary
> +
> + // SAFETY: The data was pinned before the lifetime was shortened; pinning is
> + // orthogonal to lifetimes.
> + Ok(f(unsafe { Pin::new_unchecked(short) }))
> + }
>
> - // SAFETY: The data was pinned when stored; `cast_ref` only shortens
> - // the lifetime, so the pinning guarantee is preserved.
> + /// Returns a pinned reference to the registration data set by the registering (parent) driver.
> + ///
> + /// This method is only available when `F` implements [`trait@CovariantForLt`], which guarantees
> + /// safe lifetime shortening via [`CovariantForLt::cast_ref`].
> + ///
> + /// For non-covariant types, use the closure-based [`Self::registration_data_with`].
> + ///
> + /// 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<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
> + // SAFETY: CovariantForLt guarantees covariance, so cast_ref safely shortens the
> + // `'static` lifetime.
> + let pinned = unsafe { self.registration_data_pinned::<F>()? };
> +
> + // SAFETY: The data was pinned before the lifetime was shortened; pinning is orthogonal
> + // to lifetimes.
> Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) })
> }
> }
> @@ -399,22 +445,23 @@ 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 `F` is a [`CovariantForLt`](trait@CovariantForLt) encoding of the
> -/// registration data type. For non-lifetime-parameterized types, use
> -/// [`CovariantForLt!(T)`](macro@CovariantForLt).
> -/// The data 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()`] and
> +/// [`Device::registration_data_with()`].
> ///
> /// # Invariants
> ///
> /// `self.adev` always holds a valid pointer to an initialized and registered
> /// [`struct auxiliary_device`] whose `registration_data_rust` field points to a
> /// valid `Pin<KBox<RegistrationData<F::Of<'static>>>>`.
> -pub struct Registration<'a, F: CovariantForLt + 'static> {
> +pub struct Registration<'a, F: ForLt + 'static> {
> adev: NonNull<bindings::auxiliary_device>,
> _phantom: PhantomData<F::Of<'a>>,
> }
>
> -impl<'a, F: CovariantForLt> Registration<'a, F>
> +impl<'a, F: ForLt> Registration<'a, F>
> where
> for<'b> F::Of<'b>: Send + Sync,
> {
> @@ -526,7 +573,7 @@ pub fn new<E>(
> }
> }
>
> -impl<F: CovariantForLt> Drop for Registration<'_, F> {
> +impl<F: ForLt> Drop for Registration<'_, F> {
> fn drop(&mut self) {
> // SAFETY: By the type invariant of `Self`, `self.adev.as_ptr()` is a valid registered
> // `struct auxiliary_device`.
> @@ -548,7 +595,7 @@ fn drop(&mut self) {
> }
>
> // SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
> -unsafe impl<F: CovariantForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
> +unsafe impl<F: ForLt> Send for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
>
> // SAFETY: `Registration` does not expose any methods or fields that need synchronization.
> -unsafe impl<F: CovariantForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
> +unsafe impl<F: ForLt> Sync for Registration<'_, F> where for<'a> F::Of<'a>: Send {}
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-03 12:05 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 1:10 [PATCH v2 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
2026-06-03 11:59 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
2026-06-03 12:04 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
2026-06-03 12:05 ` Gary Guo
2026-06-03 1:10 ` [PATCH v2 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
2026-06-03 1:10 ` [PATCH v2 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox