summaryrefslogtreecommitdiff
path: root/rust/kernel/devres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/devres.rs')
-rw-r--r--rust/kernel/devres.rs81
1 files changed, 54 insertions, 27 deletions
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 0f79a2ec9474..d0e6c6e162c2 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -12,26 +12,28 @@ use crate::{
error::{Error, Result},
ffi::c_void,
prelude::*,
- revocable::Revocable,
- sync::Arc,
+ revocable::{Revocable, RevocableGuard},
+ sync::{rcu, Arc, Completion},
types::ARef,
};
-use core::ops::Deref;
-
#[pin_data]
struct DevresInner<T> {
dev: ARef<Device>,
callback: unsafe extern "C" fn(*mut c_void),
#[pin]
data: Revocable<T>,
+ #[pin]
+ revoke: Completion,
}
/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
/// manage their lifetime.
///
/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
-/// [`Device`] is unbound respectively, depending on what happens first.
+/// [`Device`] is unbound respectively, depending on what happens first. In any case, it is always
+/// guaranteed that revoking the device resource is completed before the corresponding [`Device`]
+/// is unbound.
///
/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
@@ -42,7 +44,7 @@ struct DevresInner<T> {
/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
/// [`Drop`] implementation.
///
-/// # Example
+/// # Examples
///
/// ```no_run
/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}};
@@ -59,19 +61,19 @@ struct DevresInner<T> {
/// unsafe fn new(paddr: usize) -> Result<Self>{
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
/// // valid for `ioremap`.
-/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) };
+/// let addr = unsafe { bindings::ioremap(paddr as bindings::phys_addr_t, SIZE) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
///
-/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
+/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?))
/// }
/// }
///
/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
/// fn drop(&mut self) {
/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
-/// unsafe { bindings::iounmap(self.0.addr() as _); };
+/// unsafe { bindings::iounmap(self.0.addr() as *mut c_void); };
/// }
/// }
///
@@ -102,6 +104,7 @@ impl<T> DevresInner<T> {
dev: dev.into(),
callback: Self::devres_callback,
data <- Revocable::new(data),
+ revoke <- Completion::new(),
}),
flags,
)?;
@@ -112,8 +115,9 @@ impl<T> DevresInner<T> {
// SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
// detached.
- let ret =
- unsafe { bindings::devm_add_action(dev.as_raw(), Some(inner.callback), data as _) };
+ let ret = unsafe {
+ bindings::devm_add_action(dev.as_raw(), Some(inner.callback), data.cast_mut().cast())
+ };
if ret != 0 {
// SAFETY: We just created another reference to `inner` in order to pass it to
@@ -127,41 +131,48 @@ impl<T> DevresInner<T> {
}
fn as_ptr(&self) -> *const Self {
- self as _
+ self
}
- fn remove_action(this: &Arc<Self>) {
+ fn remove_action(this: &Arc<Self>) -> bool {
// SAFETY:
// - `self.inner.dev` is a valid `Device`,
// - the `action` and `data` pointers are the exact same ones as given to devm_add_action()
// previously,
// - `self` is always valid, even if the action has been released already.
- let ret = unsafe {
+ let success = unsafe {
bindings::devm_remove_action_nowarn(
this.dev.as_raw(),
Some(this.callback),
- this.as_ptr() as _,
+ this.as_ptr().cast_mut().cast(),
)
- };
+ } == 0;
- if ret == 0 {
+ if success {
// SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if
// devm_remove_action_nowarn() was successful we can (and have to) claim back ownership
// of this reference.
let _ = unsafe { Arc::from_raw(this.as_ptr()) };
}
+
+ success
}
#[allow(clippy::missing_safety_doc)]
unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) {
- let ptr = ptr as *mut DevresInner<T>;
+ let ptr = ptr.cast::<DevresInner<T>>();
// Devres owned this memory; now that we received the callback, drop the `Arc` and hence the
// reference.
// SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in
// `DevresInner::new`.
let inner = unsafe { Arc::from_raw(ptr) };
- inner.data.revoke();
+ if !inner.data.revoke() {
+ // If `revoke()` returns false, it means that `Devres::drop` already started revoking
+ // `inner.data` for us. Hence we have to wait until `Devres::drop()` signals that it
+ // completed revoking `inner.data`.
+ inner.revoke.wait_for_completion();
+ }
}
}
@@ -192,7 +203,7 @@ impl<T> Devres<T> {
/// An error is returned if `dev` does not match the same [`Device`] this [`Devres`] instance
/// has been created with.
///
- /// # Example
+ /// # Examples
///
/// ```no_run
/// # #![cfg(CONFIG_PCI)]
@@ -218,20 +229,36 @@ impl<T> Devres<T> {
// SAFETY: `dev` being the same device as the device this `Devres` has been created for
// proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as
// long as `dev` lives; `dev` lives at least as long as `self`.
- Ok(unsafe { self.deref().access() })
+ Ok(unsafe { self.0.data.access() })
+ }
+
+ /// [`Devres`] accessor for [`Revocable::try_access`].
+ pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
+ self.0.data.try_access()
}
-}
-impl<T> Deref for Devres<T> {
- type Target = Revocable<T>;
+ /// [`Devres`] accessor for [`Revocable::try_access_with`].
+ pub fn try_access_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
+ self.0.data.try_access_with(f)
+ }
- fn deref(&self) -> &Self::Target {
- &self.0.data
+ /// [`Devres`] accessor for [`Revocable::try_access_with_guard`].
+ pub fn try_access_with_guard<'a>(&'a self, guard: &'a rcu::Guard) -> Option<&'a T> {
+ self.0.data.try_access_with_guard(guard)
}
}
impl<T> Drop for Devres<T> {
fn drop(&mut self) {
- DevresInner::remove_action(&self.0);
+ // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data
+ // anymore, hence it is safe not to wait for the grace period to finish.
+ if unsafe { self.0.data.revoke_nosync() } {
+ // We revoked `self.0.data` before the devres action did, hence try to remove it.
+ if !DevresInner::remove_action(&self.0) {
+ // We could not remove the devres action, which means that it now runs concurrently,
+ // hence signal that `self.0.data` has been revoked successfully.
+ self.0.revoke.complete_all();
+ }
+ }
}
}