register_release() is useful when a device resource has associated data,
but does not require the capability of accessing it or manually releasing
it.
If we would want to be able to access the device resource and release the
device resource manually before the device is unbound, but still keep
access to the associated data, we could implement it as follows.
struct Registration<T> {
inner: Devres<RegistrationInner>,
data: T,
}
However, if we never need to access the resource or release it manually,
register_release() is great optimization for the above, since it does not
require the synchronization of the Devres type.
Suggested-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/devres.rs | 90 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index eeffdc8115aa..ff33e835c44f 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -16,6 +16,7 @@
sync::{rcu, Completion},
types::{ARef, ForeignOwnable, ScopeGuard, Opaque},
};
+use core::ops::Deref;
use pin_init::Wrapper;
@@ -347,3 +348,92 @@ pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flag
register_foreign(dev, data)
}
+
+/// [`Devres`]-releaseable resource.
+///
+/// Register an object implementing this trait with [`register_release`]. Its `release`
+/// function will be called once the device is being unbound.
+pub trait Release {
+ /// Called once the [`Device`] given to [`register_release`] is unbound.
+ fn release(&self);
+}
+
+impl<T: Release> Release for crate::sync::ArcBorrow<'_, T> {
+ fn release(&self) {
+ self.deref().release();
+ }
+}
+
+impl<T: Release> Release for Pin<&'_ T> {
+ fn release(&self) {
+ self.deref().release();
+ }
+}
+
+impl<T: Release> Release for &'_ T {
+ fn release(&self) {
+ (*self).release();
+ }
+}
+
+/// Consume the `data`, [`Release::release`] and [`Drop::drop`] `data` once `dev` is unbound.
+///
+/// # Examples
+///
+/// ```no_run
+/// use kernel::{device::{Bound, Device}, devres, devres::Release, sync::Arc};
+///
+/// /// Registration of e.g. a class device, IRQ, etc.
+/// struct Registration<T> {
+/// data: T,
+/// }
+///
+/// impl<T> Registration<T> {
+/// fn new(data: T) -> Result<Arc<Self>> {
+/// // register
+///
+/// Ok(Arc::new(Self { data }, GFP_KERNEL)?)
+/// }
+/// }
+///
+/// impl<T> Release for Registration<T> {
+/// fn release(&self) {
+/// // unregister
+/// }
+/// }
+///
+/// fn from_bound_context(dev: &Device<Bound>) -> Result {
+/// let reg = Registration::new(0x42)?;
+///
+/// devres::register_release(dev, reg.clone())
+/// }
+/// ```
+pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result
+where
+ P: ForeignOwnable + 'static,
+ for<'a> P::Borrowed<'a>: Release,
+{
+ let ptr = data.into_foreign();
+
+ #[allow(clippy::missing_safety_doc)]
+ unsafe extern "C" fn callback<P>(ptr: *mut kernel::ffi::c_void)
+ where
+ P: ForeignOwnable,
+ for<'a> P::Borrowed<'a>: Release,
+ {
+ // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid.
+ unsafe { P::borrow(ptr.cast()) }.release();
+
+ // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid.
+ drop(unsafe { P::from_foreign(ptr.cast()) });
+ }
+
+ // SAFETY:
+ // - `dev.as_raw()` is a pointer to a valid and bound device.
+ // - `ptr` is a valid pointer the `ForeignOwnable` devres takes ownership of.
+ to_result(unsafe {
+ // `devm_add_action_or_reset()` also calls `callback` on failure, such that the
+ // `ForeignOwnable` is released eventually.
+ bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::<P>), ptr.cast())
+ })
+}
--
2.49.0
On Tue Jun 24, 2025 at 11:54 PM CEST, Danilo Krummrich wrote:
> +pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result
> +where
> + P: ForeignOwnable + 'static,
> + for<'a> P::Borrowed<'a>: Release,
> +{
> + let ptr = data.into_foreign();
> +
> + #[allow(clippy::missing_safety_doc)]
> + unsafe extern "C" fn callback<P>(ptr: *mut kernel::ffi::c_void)
> + where
> + P: ForeignOwnable,
> + for<'a> P::Borrowed<'a>: Release,
> + {
> + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid.
> + unsafe { P::borrow(ptr.cast()) }.release();
> +
> + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid.
> + drop(unsafe { P::from_foreign(ptr.cast()) });
Maybe this function should just be:
let p = unsafe { P::from_foreign(ptr.cast()) };
p.release();
And we require `P: ForeignOwnable + Release + 'static`?
We then need these impls instead:
impl<T: Release, A: Allocator> Release for Pin<Box<T, A>>;
impl<T: Release, A: Allocator> Release for Box<T, A>;
impl<T: Release> Release for Arc<T>;
Or, we could change `Release` to be:
pub trait Release {
type Ptr: ForeignOwnable;
fn release(this: Self::Ptr);
}
and then `register_release` is:
pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
This way, one can store a `Box<T>` and get access to the `T` at the end.
Or if they store the value in an `Arc<T>`, they have the option to clone
it and give it to somewhere else.
Related questions:
* should we implement `ForeignOwnable` for `&'static T`?
* should we require `'static` in `ForeignOwnable`? At the moment we only
have those kinds supported and it only makes sense, a foreign owned
object can be owned for any amount of time (so it must stay valid
indefinitely).
---
Cheers,
Benno
> + }
> +
> + // SAFETY:
> + // - `dev.as_raw()` is a pointer to a valid and bound device.
> + // - `ptr` is a valid pointer the `ForeignOwnable` devres takes ownership of.
> + to_result(unsafe {
> + // `devm_add_action_or_reset()` also calls `callback` on failure, such that the
> + // `ForeignOwnable` is released eventually.
> + bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::<P>), ptr.cast())
> + })
> +}
On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
> Or, we could change `Release` to be:
>
> pub trait Release {
> type Ptr: ForeignOwnable;
>
> fn release(this: Self::Ptr);
> }
>
> and then `register_release` is:
>
> pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
>
> This way, one can store a `Box<T>` and get access to the `T` at the end.
I think this was also the case before? Well, it was P::Borrowed instead.
> Or if they store the value in an `Arc<T>`, they have the option to clone
> it and give it to somewhere else.
Anyways, I really like this proposal of implementing the Release trait.
> Related questions:
>
> * should we implement `ForeignOwnable` for `&'static T`?
There's already a patch on the list doing this in the context of DebugFS [1].
[1] https://lore.kernel.org/lkml/20250624-debugfs-rust-v7-3-9c8835a7a20f@google.com/
> * should we require `'static` in `ForeignOwnable`? At the moment we only
> have those kinds supported and it only makes sense, a foreign owned
> object can be owned for any amount of time (so it must stay valid
> indefinitely).
Sounds reasonable to me.
On Thu, Jun 26, 2025 at 01:15:34PM +0200, Danilo Krummrich wrote:
> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
> > Or, we could change `Release` to be:
> >
> > pub trait Release {
> > type Ptr: ForeignOwnable;
> >
> > fn release(this: Self::Ptr);
> > }
> >
> > and then `register_release` is:
> >
> > pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
> >
> > This way, one can store a `Box<T>` and get access to the `T` at the end.
>
> I think this was also the case before? Well, it was P::Borrowed instead.
>
> > Or if they store the value in an `Arc<T>`, they have the option to clone
> > it and give it to somewhere else.
>
> Anyways, I really like this proposal of implementing the Release trait.
One downside seems to be that the compiler cannot infer T anymore with this
function signature.
On Thu Jun 26, 2025 at 4:02 PM CEST, Danilo Krummrich wrote:
> On Thu, Jun 26, 2025 at 01:15:34PM +0200, Danilo Krummrich wrote:
>> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
>> > Or, we could change `Release` to be:
>> >
>> > pub trait Release {
>> > type Ptr: ForeignOwnable;
>> >
>> > fn release(this: Self::Ptr);
>> > }
>> >
>> > and then `register_release` is:
>> >
>> > pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
>> >
>> > This way, one can store a `Box<T>` and get access to the `T` at the end.
>>
>> I think this was also the case before? Well, it was P::Borrowed instead.
>>
>> > Or if they store the value in an `Arc<T>`, they have the option to clone
>> > it and give it to somewhere else.
>>
>> Anyways, I really like this proposal of implementing the Release trait.
>
> One downside seems to be that the compiler cannot infer T anymore with this
> function signature.
Yeah... That's a bit annoying.
We might be able to add an associated type to `ForeignOwnable` like
`Target` or `Inner` or whatever.
Then we could do:
pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result
where
P: ForeignOwnable,
P::Inner: Release<Ptr = P>,
---
Cheers,
Benno
On Thu, Jun 26, 2025 at 04:41:55PM +0200, Benno Lossin wrote:
> On Thu Jun 26, 2025 at 4:02 PM CEST, Danilo Krummrich wrote:
> > On Thu, Jun 26, 2025 at 01:15:34PM +0200, Danilo Krummrich wrote:
> >> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
> >> > Or, we could change `Release` to be:
> >> >
> >> > pub trait Release {
> >> > type Ptr: ForeignOwnable;
> >> >
> >> > fn release(this: Self::Ptr);
> >> > }
> >> >
> >> > and then `register_release` is:
> >> >
> >> > pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
> >> >
> >> > This way, one can store a `Box<T>` and get access to the `T` at the end.
> >>
> >> I think this was also the case before? Well, it was P::Borrowed instead.
> >>
> >> > Or if they store the value in an `Arc<T>`, they have the option to clone
> >> > it and give it to somewhere else.
> >>
> >> Anyways, I really like this proposal of implementing the Release trait.
> >
> > One downside seems to be that the compiler cannot infer T anymore with this
> > function signature.
>
> Yeah... That's a bit annoying.
>
> We might be able to add an associated type to `ForeignOwnable` like
> `Target` or `Inner` or whatever.
I think we already have `PointedTo` [1]? But I remember that I've seen a patch
to remove it again [2].
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/types.rs#n32
[2] https://lore.kernel.org/all/20250612-pointed-to-v3-1-b009006d86a1@kernel.org/
> Then we could do:
>
> pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result
> where
> P: ForeignOwnable,
> P::Inner: Release<Ptr = P>,
>
> ---
> Cheers,
> Benno
On Thu, Jun 26, 2025 at 05:32:25PM +0200, Danilo Krummrich wrote:
> On Thu, Jun 26, 2025 at 04:41:55PM +0200, Benno Lossin wrote:
> > On Thu Jun 26, 2025 at 4:02 PM CEST, Danilo Krummrich wrote:
> > > On Thu, Jun 26, 2025 at 01:15:34PM +0200, Danilo Krummrich wrote:
> > >> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
> > >> > Or, we could change `Release` to be:
> > >> >
> > >> > pub trait Release {
> > >> > type Ptr: ForeignOwnable;
> > >> >
> > >> > fn release(this: Self::Ptr);
> > >> > }
> > >> >
> > >> > and then `register_release` is:
> > >> >
> > >> > pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
> > >> >
> > >> > This way, one can store a `Box<T>` and get access to the `T` at the end.
> > >>
> > >> I think this was also the case before? Well, it was P::Borrowed instead.
> > >>
> > >> > Or if they store the value in an `Arc<T>`, they have the option to clone
> > >> > it and give it to somewhere else.
> > >>
> > >> Anyways, I really like this proposal of implementing the Release trait.
> > >
> > > One downside seems to be that the compiler cannot infer T anymore with this
> > > function signature.
> >
> > Yeah... That's a bit annoying.
> >
> > We might be able to add an associated type to `ForeignOwnable` like
> > `Target` or `Inner` or whatever.
>
> I think we already have `PointedTo` [1]? But I remember that I've seen a patch
> to remove it again [2].
Well, not exactly, I think. Arc, for instance, defines PointedTo as ArcInner<T>.
So, I think we indeed want something different.
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/types.rs#n32
> [2] https://lore.kernel.org/all/20250612-pointed-to-v3-1-b009006d86a1@kernel.org/
>
> > Then we could do:
> >
> > pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result
> > where
> > P: ForeignOwnable,
> > P::Inner: Release<Ptr = P>,
> >
> > ---
> > Cheers,
> > Benno
On Thu Jun 26, 2025 at 5:49 PM CEST, Danilo Krummrich wrote:
> On Thu, Jun 26, 2025 at 05:32:25PM +0200, Danilo Krummrich wrote:
>> On Thu, Jun 26, 2025 at 04:41:55PM +0200, Benno Lossin wrote:
>> > On Thu Jun 26, 2025 at 4:02 PM CEST, Danilo Krummrich wrote:
>> > > On Thu, Jun 26, 2025 at 01:15:34PM +0200, Danilo Krummrich wrote:
>> > >> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
>> > >> > Or, we could change `Release` to be:
>> > >> >
>> > >> > pub trait Release {
>> > >> > type Ptr: ForeignOwnable;
>> > >> >
>> > >> > fn release(this: Self::Ptr);
>> > >> > }
>> > >> >
>> > >> > and then `register_release` is:
>> > >> >
>> > >> > pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
>> > >> >
>> > >> > This way, one can store a `Box<T>` and get access to the `T` at the end.
>> > >>
>> > >> I think this was also the case before? Well, it was P::Borrowed instead.
>> > >>
>> > >> > Or if they store the value in an `Arc<T>`, they have the option to clone
>> > >> > it and give it to somewhere else.
>> > >>
>> > >> Anyways, I really like this proposal of implementing the Release trait.
>> > >
>> > > One downside seems to be that the compiler cannot infer T anymore with this
>> > > function signature.
>> >
>> > Yeah... That's a bit annoying.
>> >
>> > We might be able to add an associated type to `ForeignOwnable` like
>> > `Target` or `Inner` or whatever.
>>
>> I think we already have `PointedTo` [1]? But I remember that I've seen a patch
>> to remove it again [2].
>
> Well, not exactly, I think. Arc, for instance, defines PointedTo as ArcInner<T>.
> So, I think we indeed want something different.
Yeah `PointedTo` is used to specify the type of the foreign pointer and
should go away soon.
---
Cheers,
Benno
On Thu Jun 26, 2025 at 1:15 PM CEST, Danilo Krummrich wrote:
> On Thu, Jun 26, 2025 at 12:36:23PM +0200, Benno Lossin wrote:
>> Or, we could change `Release` to be:
>>
>> pub trait Release {
>> type Ptr: ForeignOwnable;
>>
>> fn release(this: Self::Ptr);
>> }
>>
>> and then `register_release` is:
>>
>> pub fn register_release<T: Release>(dev: &Device<Bound>, data: T::Ptr) -> Result
>>
>> This way, one can store a `Box<T>` and get access to the `T` at the end.
>
> I think this was also the case before? Well, it was P::Borrowed instead.
Well you had access to a `&T`, but not the `T` directly.
>> Related questions:
>>
>> * should we implement `ForeignOwnable` for `&'static T`?
>
> There's already a patch on the list doing this in the context of DebugFS [1].
>
> [1] https://lore.kernel.org/lkml/20250624-debugfs-rust-v7-3-9c8835a7a20f@google.com/
Ah, I'm not following that series at the moment.
---
Cheers,
Benno
© 2016 - 2026 Red Hat, Inc.