* [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt
@ 2026-06-18 23:08 Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
` (6 more replies)
0 siblings, 7 replies; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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 for all lifetime-parameterized types, providing
only the Of<'a> GAT.
- 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 v3:
- Keep UnsafeForLtImpl as the shared helper for both ForLt! and
CovariantForLt!, distinguished by const generic N
- Remove cast_ref_unchecked() from ForLt; lifetime shortening is
handled by borrowing with the target lifetime directly or by
decoupling the HRTB from the outer reference lifetime
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 | 76 +++++++++++++++-----
rust/kernel/devres.rs | 100 ++++++++++++++++++++++++++
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 | 95 +++++++++++++++++-------
rust/macros/for_lt.rs | 53 +++++++++++---
rust/macros/lib.rs | 18 ++++-
samples/rust/rust_driver_auxiliary.rs | 96 ++++++++++++++++++-------
12 files changed, 432 insertions(+), 119 deletions(-)
base-commit: 83f1454877cc292b88baf13c829c16ce6937d120
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:15 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
` (5 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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 | 57 ++++++++++++++-------------
rust/macros/for_lt.rs | 2 +-
rust/macros/lib.rs | 11 +++---
samples/rust/rust_driver_auxiliary.rs | 8 ++--
7 files changed, 54 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 5738d4ac521b..48380ac15f68 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..a11f7509633c 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.
///
-/// 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.
///
@@ -109,14 +110,14 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
#[doc(hidden)]
pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
-// This is a helper trait for implementation `ForLt` to be able to use HRTB.
+// 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
+// `UnsafeForLtImpl` and it will fail to evaluate if the type is not covariant.
+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 0> {
type Of<'a> = <T as WithLt<'a>>::Of;
}
diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
index 364d4113cd10..e1233701d6cc 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 } => {
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] 12+ messages in thread
* [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
` (4 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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.
- CovariantForLt (unsafe): becomes a subtrait of ForLt that
additionally proves the type is covariant over its lifetime
parameter, providing a safe cast_ref() method.
This split allows non-covariant types (e.g. types behind a Mutex) to
implement ForLt and participate in DevresLt / registration data patterns
that use HRTB closures for sound access, without requiring a covariance
proof that would fail to compile.
Both macros share the UnsafeForLtImpl helper type, distinguished by
a const generic N: ForLt! emits N = 0 (no covariance proof),
CovariantForLt! emits N = 1 (with compile-time covariance proof).
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/types.rs | 1 +
rust/kernel/types/for_lt.rs | 72 +++++++++++++++++++++++++++++--------
rust/macros/for_lt.rs | 53 +++++++++++++++++++++------
rust/macros/lib.rs | 19 +++++++++-
4 files changed, 118 insertions(+), 27 deletions(-)
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index cbe6907042d3..c1ed05d1046c 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -14,6 +14,7 @@
#[doc(hidden)]
pub mod for_lt;
pub use for_lt::CovariantForLt;
+pub use for_lt::ForLt;
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
index a11f7509633c..0b53494080b7 100644
--- a/rust/kernel/types/for_lt.rs
+++ b/rust/kernel/types/for_lt.rs
@@ -1,17 +1,59 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
-//! Provide implementation and test of the `CovariantForLt` trait and macro.
+//! Provide implementation and test of the `ForLt` and `CovariantForLt` traits and macros.
//!
-//! This module is hidden and user should just use `CovariantForLt!` directly.
+//! This module is hidden and users should just use `ForLt!` / `CovariantForLt!` directly.
use core::marker::PhantomData;
/// Representation of types generic over a lifetime.
///
-/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter
-/// can be soundly shortened.
+/// # Macro
+///
+/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
+/// type that implements this trait.
///
-/// The lifetime involved must be covariant.
+/// The full syntax is
+///
+/// ```
+/// # use kernel::types::ForLt;
+/// # fn expect_lt<F: ForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// ForLt!(for<'a> TypeThatUse<'a>)
+/// # >();
+/// ```
+///
+/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
+/// is `TypeThatUse<'b>`.
+///
+/// You may also use a short-hand syntax which works similar to lifetime elision.
+/// The macro also accepts types that do not involve a lifetime at all.
+///
+/// ```
+/// # use kernel::types::ForLt;
+/// # fn expect_lt<F: ForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
+/// # >();
+/// # expect_lt::<
+/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
+/// # >();
+/// # expect_lt::<
+/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
+/// # >();
+/// ```
+pub trait ForLt {
+ /// The type parameterized by the lifetime.
+ type Of<'a>: 'a;
+}
+pub use macros::ForLt;
+
+/// [`trait@ForLt`] subtrait for types that are covariant over their lifetime parameter.
+///
+/// Provides a safe [`cast_ref`](CovariantForLt::cast_ref) method for types that are proven to be
+/// covariant. The `CovariantForLt!` macro syntax is the same as `ForLt!`.
///
/// # Macro
///
@@ -84,10 +126,7 @@
/// # Safety
///
/// `Self::Of<'a>` must be covariant over the lifetime `'a`.
-pub unsafe trait CovariantForLt {
- /// The type parameterized by the lifetime.
- type Of<'a>: 'a;
-
+pub unsafe trait CovariantForLt: ForLt {
/// Cast a reference to a shorter lifetime.
#[inline(always)]
fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {
@@ -99,25 +138,28 @@ fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Sel
/// This is intended to be an "unsafe-to-refer-to" type.
///
-/// Must only be used by the `CovariantForLt!` macro.
+/// Must only be used by the `ForLt!` / `CovariantForLt!` macros.
///
/// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.
///
/// `WF` is a type that the macro can use to assert some specific type is well-formed.
///
/// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove
-/// additional properties.
+/// additional properties. `ForLt!` emits `N = 0`; `CovariantForLt!` emits `N = 1` after a
+/// covariance proof.
#[doc(hidden)]
pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);
-// This is a helper trait for implementation `CovariantForLt` to be able to use HRTB.
+// This is a helper trait for implementation of `ForLt` / `CovariantForLt` to be able to use HRTB.
#[doc(hidden)]
pub trait WithLt<'a> {
type Of: 'a;
}
-// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated when naming
-// `UnsafeForLtImpl` and it will fail to evaluate if the type is not covariant.
-unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 0> {
+impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for UnsafeForLtImpl<T, WF, N> {
type Of<'a> = <T as WithLt<'a>>::Of;
}
+
+// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated in the `N` const generic
+// and it will fail to evaluate if the type is not covariant. Only `N = 1` gets this impl.
+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 1> {}
diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
index e1233701d6cc..d5f728a464ca 100644
--- a/rust/macros/for_lt.rs
+++ b/rust/macros/for_lt.rs
@@ -176,8 +176,10 @@ fn prove(&mut self, ty: &'a Type) {
}
}
-pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
- let (ty, lifetime) = match input {
+/// Resolve the higher-ranked type into a concrete `(ty, lifetime)` pair, expanding elided
+/// lifetimes as needed. Shared by both `for_lt` and `covariant_for_lt`.
+fn resolve_hrt(input: HigherRankedType) -> (Type, Lifetime) {
+ match input {
HigherRankedType::Explicit { lifetime, ty, .. } => (ty, lifetime),
HigherRankedType::Implicit { ty } => {
// If there's no explicit `for<'a>` binder, inject a synthetic `'__elided` lifetime
@@ -188,7 +190,42 @@ pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
};
(ty.expand_elided_lifetime(&lifetime), lifetime)
}
- };
+ }
+}
+
+/// Produce the `'static`-substituted type for the WF check. Shared by both macros.
+fn ty_static(ty: &Type, lifetime: &Lifetime) -> Type {
+ ty.replace_lifetime(
+ lifetime,
+ &Lifetime {
+ apostrophe: Span::mixed_site(),
+ ident: format_ident!("static"),
+ },
+ )
+}
+
+pub(crate) fn for_lt(input: HigherRankedType) -> TokenStream {
+ let (ty, lifetime) = resolve_hrt(input);
+
+ // Make sure that the type is wellformed when substituting lifetime with `'static`.
+ //
+ // Currently the Rust compiler doesn't check this, see the `ProveWf` documentation in
+ // `covariant_for_lt` below.
+ //
+ // We prefer to use this way of proving WF-ness as it can work when generics are involved.
+ let ty_static = ty_static(&ty, &lifetime);
+
+ quote!(
+ ::kernel::types::for_lt::UnsafeForLtImpl::<
+ dyn for<#lifetime> ::kernel::types::for_lt::WithLt<#lifetime, Of = #ty>,
+ #ty_static,
+ 0,
+ >
+ )
+}
+
+pub(crate) fn covariant_for_lt(input: HigherRankedType) -> TokenStream {
+ let (ty, lifetime) = resolve_hrt(input);
let mut prover = Prover(&lifetime, Vec::new());
prover.prove(&ty);
@@ -226,13 +263,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
// Currently the Rust compiler doesn't check this, see the above `ProveWf` documentation.
//
// We prefer to use this way of proving WF-ness as it can work when generics are involved.
- let ty_static = ty.replace_lifetime(
- &lifetime,
- &Lifetime {
- apostrophe: Span::mixed_site(),
- ident: format_ident!("static"),
- },
- );
+ let ty_static = ty_static(&ty, &lifetime);
quote!(
::kernel::types::for_lt::UnsafeForLtImpl::<
@@ -241,7 +272,7 @@ fn #cov_proof_name<'__short, '__long: '__short>(
{
#(#proof)*
- 0
+ 1
}
>
)
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 2167cb270928..e970769609f3 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -491,11 +491,28 @@ pub fn kunit_tests(attr: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
-/// Obtain a type that implements [`CovariantForLt`] for the given higher-ranked type.
+/// Obtain a type that implements [`ForLt`] for the given higher-ranked type.
+///
+/// Please refer to the documentation of the [`ForLt`] trait.
+///
+/// [`ForLt`]: trait.ForLt.html
+#[proc_macro]
+#[allow(non_snake_case)]
+pub fn ForLt(input: TokenStream) -> TokenStream {
+ for_lt::for_lt(parse_macro_input!(input)).into()
+}
+
+/// Obtain a type that implements [`CovariantForLt`] (and [`ForLt`]) for the given higher-ranked
+/// type.
+///
+/// Unlike [`ForLt!`], this macro additionally proves that the type is covariant over the lifetime,
+/// providing a safe [`CovariantForLt::cast_ref`] method.
///
/// Please refer to the documentation of the [`CovariantForLt`] trait.
///
/// [`CovariantForLt`]: trait.CovariantForLt.html
+/// [`CovariantForLt::cast_ref`]: trait.CovariantForLt.html#method.cast_ref
+/// [`ForLt`]: trait.ForLt.html
#[proc_macro]
#[allow(non_snake_case)]
pub fn CovariantForLt(input: TokenStream) -> TokenStream {
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:22 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
` (3 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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 | 91 ++++++++++++++++++++++++++++------------
1 file changed, 65 insertions(+), 26 deletions(-)
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 40a0af74a8e5..8013c0fcd82d 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 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<'_>>> {
+ /// Callers must ensure that the lifetime shortening from the original `'static` storage to
+ /// `'_` is sound, e.g. via an HRTB closure or [`CovariantForLt`] guarantee.
+ unsafe fn registration_data_pinned<F: ForLt + '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() {
@@ -300,17 +298,57 @@ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::O
return Err(EINVAL);
}
- // SAFETY: The `TypeId` check above confirms that the stored type matches
- // `F::Of<'static>`; `ptr` remains valid until `Registration::drop()` calls
- // `from_foreign()`.
- let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) };
+ // SAFETY: The `TypeId` check above confirms that the stored type matches `F`'s
+ // encoding; lifetimes are erased at runtime, so borrowing as `F::Of<'_>` is
+ // layout-compatible with the stored `F::Of<'static>`. `ptr` remains valid until
+ // `Registration::drop()` calls `from_foreign()`.
+ let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'_>>>>::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) })
+ }
- // SAFETY: The data was pinned when stored; `cast_ref` only shortens
- // the lifetime, so the pinning guarantee is preserved.
- Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) })
+ /// 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>()? };
+
+ Ok(f(pinned))
+ }
+
+ /// 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
+ /// that the lifetime shortening is sound.
+ ///
+ /// 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, which makes the lifetime shortening
+ // from `'static` to `'_` performed by `registration_data_pinned` sound.
+ unsafe { self.registration_data_pinned::<F>() }
}
}
@@ -399,22 +437,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 +565,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 +587,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] 12+ messages in thread
* [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (2 preceding siblings ...)
2026-06-18 23:08 ` [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:21 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
` (2 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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] 12+ messages in thread
* [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (3 preceding siblings ...)
2026-06-18 23:08 ` [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:21 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
6 siblings, 1 reply; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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 shortens the stored 'static lifetime to the caller's borrow
lifetime in all access methods.
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 | 100 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 11ce500e9b76..e11deff3e1be 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -24,6 +24,8 @@
Arc, //
},
types::{
+ CovariantForLt,
+ ForLt,
ForeignOwnable,
Opaque, //
},
@@ -365,6 +367,104 @@ 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(&F::Of<'a>) -> R,
+ {
+ self.0.access(dev).map(f)
+ }
+
+ /// [`DevresLt`] accessor for [`Revocable::try_access_with`].
+ pub fn try_access_with<R, G>(&self, f: G) -> Option<R>
+ where
+ G: for<'a> FnOnce(&F::Of<'a>) -> R,
+ {
+ self.0.data().try_access_with(f)
+ }
+}
+
+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] 12+ messages in thread
* [PATCH v3 6/7] rust: pci: return DevresLt from Bar::into_devres()
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (4 preceding siblings ...)
2026-06-18 23:08 ` [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
6 siblings, 0 replies; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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] 12+ messages in thread
* [PATCH v3 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres()
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
` (5 preceding siblings ...)
2026-06-18 23:08 ` [PATCH v3 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
@ 2026-06-18 23:08 ` Danilo Krummrich
6 siblings, 0 replies; 12+ messages in thread
From: Danilo Krummrich @ 2026-06-18 23:08 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 3e3fa51ccef9..7102c60fd14b 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,
@@ -86,7 +85,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] 12+ messages in thread
* Re: [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt
2026-06-18 23:08 ` [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
@ 2026-06-18 23:15 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-18 23:15 UTC (permalink / raw)
To: Danilo Krummrich; +Cc: Alexandre Courbot, dri-devel, ojeda
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] A comment in the macro implementation still refers to the old macro name `ForLt!`.
--
commit c45f7e371456a82ca15ef60543a42d6b7346219f
Author: Danilo Krummrich <dakr@kernel.org>
rust: types: rename ForLt to CovariantForLt
Rename ForLt to CovariantForLt to prepare for the introduction of a new
ForLt base trait that does not require covariance.
[ ... ]
> diff --git a/rust/macros/for_lt.rs b/rust/macros/for_lt.rs
> index 364d4113cd107..e1233701d6ccf 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 {
[Severity: Low]
Does the inline comment in Prover::prove() earlier in this file also need
to be updated?
It currently explains why certain reference lifetimes are deferred to the
compiler by citing the old macro name:
// This is to block cases like ForLt!(for<'a> &'static &'a u32), as the
// presence of the type implies 'a: 'static but this is unsound.
Could this comment be updated to refer to CovariantForLt! instead, to match
the macro rename introduced here?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618230834.812007-1-dakr@kernel.org?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access
2026-06-18 23:08 ` [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
@ 2026-06-18 23:21 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-18 23:21 UTC (permalink / raw)
To: Danilo Krummrich; +Cc: ojeda, Alexandre Courbot, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] `DevresGuard` implements `Deref` exposing the inner lifetime `'a` tied to `DevresLt` rather than the guard's lifetime (`'guard`), causing an API soundness issue that can lead to Use-After-Free.
- [Low] Missing `#[inline]` annotations on small forwarding abstraction functions.
--
commit 20febe7248eef3fe1ba711b99c2b480ed7284b88
Author: Danilo Krummrich <dakr@kernel.org>
rust: devres: add DevresLt for ForLt-aware device resource access
This commit introduces DevresLt, a wrapper around Devres that provides
lifetime-aware access to device resources. It adds accessors that correctly
shorten the stored static lifetime to the caller's borrow lifetime, preventing
unsound static lifetimes from being exposed.
> diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
> index 11ce500e9b76f..e11deff3e1be5 100644
> --- a/rust/kernel/devres.rs
> +++ b/rust/kernel/devres.rs
[ ... ]
> @@ -365,6 +367,104 @@ 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 {
[Severity: Low]
This isn't a bug, but shouldn't this trivial forwarding function be annotated
with #[inline]?
[Severity: High]
Could implementing Deref here expose the outer lifetime 'a instead of the
guard's lifetime, potentially leading to a use-after-free?
Because F::Of<'a> is exposed with its outer 'a lifetime, a caller can
trivially extract references with lifetime 'a from it.
When DevresGuard is dropped, the internal RCU guard is released, ending the
RCU read-side critical section. However, the extracted 'a reference is still
valid from the compiler's perspective because it is tied to DevresLt.
If the device is concurrently unbound, devres will revoke and drop the
internal data. Accessing the extracted 'a reference afterwards results in a
use-after-free.
Would it be safer to remove Deref and provide an explicit get method that
yields &'guard F::Of<'guard>?
> + F::cast_ref(&*self.0)
> + }
> +}
[ ... ]
> + /// Return a reference of the [`Device`] this [`DevresLt`] instance has been created with.
> + pub fn device(&self) -> &Device {
[Severity: Low]
This isn't a bug, but should this small forwarding function have an
#[inline] annotation as per the Rust subsystem guidelines?
This also applies to other trivial forwarding methods added in this commit,
such as access_with, try_access_with, and access.
> + self.0.device()
> + }
[ ... ]
> + /// [`DevresLt`] accessor for [`Revocable::try_access`].
> + pub fn try_access(&self) -> Option<DevresGuard<'_, F>> {
[Severity: High]
Is there a risk that tying the guard to the borrow lifetime of &self allows
references to outlive the RCU read-side critical section?
This method borrows &self for lifetime 'a and returns Option<DevresGuard<'a,
F>>. Combined with the Deref implementation, this exposes the longer lifetime
'a to the caller, bypassing the RCU lock's guard lifetime.
[Severity: Low]
This isn't a bug, but would it be appropriate to add an #[inline] annotation
to this forwarding function?
> + self.0.data().try_access().map(DevresGuard)
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618230834.812007-1-dakr@kernel.org?part=5
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type
2026-06-18 23:08 ` [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
@ 2026-06-18 23:21 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-18 23:21 UTC (permalink / raw)
To: Danilo Krummrich; +Cc: ojeda, Alexandre Courbot, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Safe Rust code can cause a Use-After-Free (UAF) and lock a freed Mutex by calling `registration_data_with()` on a surviving `ARef<Device>` after the parent device is unbound.
--
commit 9c83313149d56b7513a5eff4702ae4ca9adae277
Author: Danilo Krummrich <dakr@kernel.org>
rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type
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.
> diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
> index 92ee6a6d348e7..e441ae81fa2cf 100644
> --- a/samples/rust/rust_driver_auxiliary.rs
> +++ b/samples/rust/rust_driver_auxiliary.rs
[ ... ]
> @@ -115,22 +140,39 @@ fn probe<'bound>(
>
> impl ParentDriver {
> fn connect(adev: &auxiliary::Device<Bound>) -> Result {
[ ... ]
> + // 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();
[Severity: Critical]
This is a pre-existing issue, but can this safe Rust code cause a
use-after-free if called on a surviving ARef<Device> after the parent device
is unbound?
In the auxiliary core, Registration::drop() frees the RegistrationData:
rust/kernel/auxiliary.rs:Registration::drop() {
...
drop(unsafe {
Pin::<KBox<RegistrationData<F::Of<'static>>>>::from_foreign(
(*self.adev.as_ptr()).registration_data_rust,
)
});
...
}
However, it leaves the registration_data_rust pointer dangling.
Since the auxiliary_device can safely outlive the Registration (e.g. via a
safe ARef<Device> inside an async task, cdev, or sysfs file), a caller holding
that reference could safely call registration_data_with() and trigger a
use-after-free when the dangling pointer is read:
rust/kernel/auxiliary.rs:Device::registration_data_pinned() {
...
let type_id = unsafe { ptr.cast::<TypeId>().read() };
...
}
In this sample, wouldn't that result in locking a freed Mutex when
data.parent.lock() is called?
> + dev_info!(
> + pdev,
> + "Connected to auxiliary device with index {} (via Mutex).\n",
> + data.index
> + );
> + })?;
> + }
> + _ => return Err(EINVAL),
> + }
>
> Ok(())
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618230834.812007-1-dakr@kernel.org?part=4
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types
2026-06-18 23:08 ` [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
@ 2026-06-18 23:22 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-18 23:22 UTC (permalink / raw)
To: Danilo Krummrich; +Cc: dri-devel, ojeda, Alexandre Courbot
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Small forwarding functions in abstractions are missing `#[inline]` annotations.
--
commit 703db32948a8690227e0f13e2618862778e2205c
Author: Danilo Krummrich <dakr@kernel.org>
rust: auxiliary: add registration_data_with() for ForLt types
This commit adds registration_data_with() to allow non-covariant ForLt
types to be registered by taking an HRTB closure, avoiding concrete lifetime
selection. It also extracts shared logic into a private
registration_data_pinned() helper and relaxes the Registration bound.
> diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
> index 40a0af74a8e5c..8013c0fcd82d8 100644
> --- a/rust/kernel/auxiliary.rs
> +++ b/rust/kernel/auxiliary.rs
[ ... ]
> @@ -300,17 +298,57 @@ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::O
[ ... ]
> + pub fn registration_data_with<F: ForLt + 'static, R>(
> + &self,
> + f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R,
> + ) -> Result<R> {
[Severity: Low]
This isn't a bug, but according to the Rust subsystem guidelines, small or
forwarding functions in abstractions should be annotated with #[inline].
Should this newly added forwarding function have an #[inline] attribute?
> + // 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>()? };
> +
> + Ok(f(pinned))
> + }
[ ... ]
> + pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
[Severity: Low]
This isn't a bug, but since this function was modified to become a small
forwarding function to registration_data_pinned(), should it also receive
an #[inline] annotation to match the subsystem guidelines?
> + // SAFETY: `CovariantForLt` guarantees covariance, which makes the lifetime shortening
> + // from `'static` to `'_` performed by `registration_data_pinned` sound.
> + unsafe { self.registration_data_pinned::<F>() }
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618230834.812007-1-dakr@kernel.org?part=3
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-18 23:22 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 23:08 [PATCH v3 0/7] ForLt/CovariantForLt split, auxiliary closure API and DevresLt Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 1/7] rust: types: rename ForLt to CovariantForLt Danilo Krummrich
2026-06-18 23:15 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 2/7] rust: types: introduce ForLt base trait for CovariantForLt Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 3/7] rust: auxiliary: add registration_data_with() for ForLt types Danilo Krummrich
2026-06-18 23:22 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 4/7] rust: auxiliary: sample: demonstrate ForLt with invariant Mutex type Danilo Krummrich
2026-06-18 23:21 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access Danilo Krummrich
2026-06-18 23:21 ` sashiko-bot
2026-06-18 23:08 ` [PATCH v3 6/7] rust: pci: return DevresLt from Bar::into_devres() Danilo Krummrich
2026-06-18 23:08 ` [PATCH v3 7/7] rust: io: mem: return DevresLt from IoMem/ExclusiveIoMem::into_devres() Danilo Krummrich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.