Commit 20c96ed2 authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

rust: devres: do not dereference to the internal Revocable



We can't expose direct access to the internal Revocable, since this
allows users to directly revoke the internal Revocable without Devres
having the chance to synchronize with the devres callback -- we have to
guarantee that the internal Revocable has been fully revoked before
the device is fully unbound.

Hence, remove the corresponding Deref implementation and, instead,
provide indirect accessors for the internal Revocable.

Note that we can still support Devres::revoke() by implementing the
required synchronization (which would be almost identical to the
synchronization in Devres::drop()).

Fixes: 76c01ded ("rust: add devres abstraction")
Reviewed-by: default avatarBenno Lossin <lossin@kernel.org>
Link: https://lore.kernel.org/r/20250611174827.380555-1-dakr@kernel.org


Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent f744201c
Loading
Loading
Loading
Loading
+16 −11
Original line number Diff line number Diff line
@@ -12,13 +12,11 @@
    error::{Error, Result},
    ffi::c_void,
    prelude::*,
    revocable::Revocable,
    sync::{Arc, Completion},
    revocable::{Revocable, RevocableGuard},
    sync::{rcu, Arc, Completion},
    types::ARef,
};

use core::ops::Deref;

#[pin_data]
struct DevresInner<T> {
    dev: ARef<Device>,
@@ -230,15 +228,22 @@ pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a 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)
    }
}

@@ -246,7 +251,7 @@ impl<T> Drop for Devres<T> {
    fn drop(&mut self) {
        // 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.revoke_nosync() } {
        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,