On Thu, Jun 26, 2025 at 10:00:43PM +0200, Danilo Krummrich wrote: > 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@xxxxxxxxxx> > Signed-off-by: Danilo Krummrich <dakr@xxxxxxxxxx> > --- > rust/kernel/devres.rs | 73 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 73 insertions(+) > > diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs > index 3ce8d6161778..92aca78874ff 100644 > --- a/rust/kernel/devres.rs > +++ b/rust/kernel/devres.rs > @@ -353,3 +353,76 @@ 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 { > + /// The [`ForeignOwnable`] pointer type consumed by [`register_release`]. > + type Ptr: ForeignOwnable; > + > + /// Called once the [`Device`] given to [`register_release`] is unbound. > + fn release(this: Self::Ptr); > +} > + I would like to point out the limitation of this design, say you have a `Foo` that can ipml `Release`, with this, I think you could only support either `Arc<Foo>` or `KBox<Foo>`. You cannot support both as the input for `register_release()`. Maybe we want: pub trait Release<Ptr: ForeignOwnable> { fn release(this: Ptr); } ? Regards, Boqun > +/// 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; > +/// > +/// impl Registration { > +/// fn new() -> Result<Arc<Self>> { > +/// // register > +/// > +/// Ok(Arc::new(Self, GFP_KERNEL)?) > +/// } > +/// } > +/// > +/// impl Release for Registration { > +/// type Ptr = Arc<Self>; > +/// > +/// fn release(this: Arc<Self>) { > +/// // unregister > +/// } > +/// } > +/// > +/// fn from_bound_context(dev: &Device<Bound>) -> Result { > +/// let reg = Registration::new()?; > +/// > +/// devres::register_release(dev, reg.clone()) > +/// } > +/// ``` > +pub fn register_release<P>(dev: &Device<Bound>, data: P) -> Result > +where > + P: ForeignOwnable, > + P::Target: Release<Ptr = P> + Send, > +{ > + 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, > + P::Target: Release<Ptr = P>, > + { > + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid. > + let data = unsafe { P::from_foreign(ptr.cast()) }; > + > + P::Target::release(data); > + } > + > + // 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 >