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 | 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