// SPDX-License-Identifier: GPL-2.0 //! Revocable objects. //! //! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence //! of a [`RevocableGuard`] ensures that objects remain valid. use crate::bindings; use core::{ cell::UnsafeCell, marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::drop_in_place, sync::atomic::{AtomicBool, Ordering}, }; /// An object that can become inaccessible at runtime. /// /// Once access is revoked and all concurrent users complete (i.e., all existing instances of /// [`RevocableGuard`] are dropped), the wrapped object is also dropped. /// /// # Examples /// /// ``` /// # use kernel::revocable::Revocable; /// /// struct Example { /// a: u32, /// b: u32, /// } /// /// fn add_two(v: &Revocable) -> Option { /// let guard = v.try_access()?; /// Some(guard.a + guard.b) /// } /// /// let v = Revocable::new(Example { a: 10, b: 20 }); /// assert_eq!(add_two(&v), Some(30)); /// v.revoke(); /// assert_eq!(add_two(&v), None); /// ``` pub struct Revocable { is_available: AtomicBool, data: ManuallyDrop>, } // SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. This is because while the // functionality exposed by `Revocable` can be accessed from any thread/CPU, it is possible that // this isn't supported by the wrapped object. unsafe impl Send for Revocable {} // SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require `Send` // from the wrapped object as well because of `Revocable::revoke`, which can trigger the `Drop` // implementation of the wrapped object from an arbitrary thread. unsafe impl Sync for Revocable {} impl Revocable { /// Creates a new revocable instance of the given data. pub fn new(data: T) -> Self { Self { is_available: AtomicBool::new(true), data: ManuallyDrop::new(UnsafeCell::new(data)), } } } impl Revocable { /// Tries to access the \[revocable\] wrapped object. /// /// Returns `None` if the object has been revoked and is therefore no longer accessible. /// /// Returns a guard that gives access to the object otherwise; the object is guaranteed to /// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep /// because another CPU may be waiting to complete the revocation of this object. pub fn try_access(&self) -> Option> { let guard = RevocableGuard::new(self.data.get()); if self.is_available.load(Ordering::Relaxed) { Some(guard) } else { None } } /// Revokes access to and drops the wrapped object. /// /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`]. If /// there are concurrent users of the object (i.e., ones that called [`Revocable::try_access`] /// beforehand and still haven't dropped the returned guard), this function waits for the /// concurrent access to complete before dropping the wrapped object. pub fn revoke(&self) { if self .is_available .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) .is_ok() { // SAFETY: Just an FFI call, there are no further requirements. unsafe { bindings::synchronize_rcu() }; // SAFETY: We know `self.data` is valid because only one CPU can succeed the // `compare_exchange` above that takes `is_available` from `true` to `false`. unsafe { drop_in_place(self.data.get()) }; } } } impl Drop for Revocable { fn drop(&mut self) { // Drop only if the data hasn't been revoked yet (in which case it has already been // dropped). if *self.is_available.get_mut() { // SAFETY: We know `self.data` is valid because no other CPU has changed // `is_available` to `false` yet, and no other CPU can do it anymore because this CPU // holds the only reference (mutable) to `self` now. unsafe { drop_in_place(self.data.get()) }; } } } /// A guard that allows access to a revocable object and keeps it alive. /// /// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context /// holding the RCU read-side lock. /// /// # Invariants /// /// The RCU read-side lock is held while the guard is alive. pub struct RevocableGuard<'a, T: ?Sized> { data_ref: *const T, _p: PhantomData<&'a ()>, } impl RevocableGuard<'_, T> { fn new(data_ref: *const T) -> Self { // SAFETY: Just an FFI call, there are no further requirements. unsafe { bindings::rcu_read_lock() }; // INVARIANTS: The RCU read-side lock was just acquired. Self { data_ref, _p: PhantomData, } } } impl Drop for RevocableGuard<'_, T> { fn drop(&mut self) { // SAFETY: By the type invariants, we know that we hold the RCU read-side lock. unsafe { bindings::rcu_read_unlock() }; } } impl Deref for RevocableGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { // SAFETY: By the type invariants, we hold the rcu read-side lock, so the object is // guaranteed to remain valid. unsafe { &*self.data_ref } } }