|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Synchronisation primitives where acccess to their contents can be revoked at runtime. |
| 4 | +
|
| 5 | +use crate::{ |
| 6 | + bindings, |
| 7 | + str::CStr, |
| 8 | + sync::{Guard, Lock, LockFactory, LockInfo, NeedsLockClass, ReadLock, WriteLock}, |
| 9 | + True, |
| 10 | +}; |
| 11 | +use core::{ |
| 12 | + mem::MaybeUninit, |
| 13 | + ops::{Deref, DerefMut}, |
| 14 | + pin::Pin, |
| 15 | +}; |
| 16 | + |
| 17 | +/// The state within the revocable synchronisation primitive. |
| 18 | +/// |
| 19 | +/// We don't use simply `Option<T>` because we need to drop in-place because the contents are |
| 20 | +/// implicitly pinned. |
| 21 | +/// |
| 22 | +/// # Invariants |
| 23 | +/// |
| 24 | +/// The `is_available` field determines if `data` is initialised. |
| 25 | +pub struct Inner<T> { |
| 26 | + is_available: bool, |
| 27 | + data: MaybeUninit<T>, |
| 28 | +} |
| 29 | + |
| 30 | +impl<T> Inner<T> { |
| 31 | + fn new(data: T) -> Self { |
| 32 | + // INVARIANT: `data` is initialised and `is_available` is `true`, so the state matches. |
| 33 | + Self { |
| 34 | + is_available: true, |
| 35 | + data: MaybeUninit::new(data), |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + fn drop_in_place(&mut self) { |
| 40 | + if !self.is_available { |
| 41 | + // Already dropped. |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + // INVARIANT: `data` is being dropped and `is_available` is set to `false`, so the state |
| 46 | + // matches. |
| 47 | + self.is_available = false; |
| 48 | + |
| 49 | + // SAFETY: By the type invariants, `data` is valid because `is_available` was true. |
| 50 | + unsafe { self.data.assume_init_drop() }; |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +impl<T> Drop for Inner<T> { |
| 55 | + fn drop(&mut self) { |
| 56 | + self.drop_in_place(); |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +/// Revocable synchronisation primitive. |
| 61 | +/// |
| 62 | +/// That is, it wraps synchronisation primitives so that access to their contents can be revoked at |
| 63 | +/// runtime, rendering them inacessible. |
| 64 | +/// |
| 65 | +/// Once access is revoked and all concurrent users complete (i.e., all existing instances of |
| 66 | +/// [`RevocableGuard`] are dropped), the wrapped object is also dropped. |
| 67 | +/// |
| 68 | +/// For better ergonomics, we advise the use of specialisations of this struct, for example, |
| 69 | +/// [`super::RevocableMutex`] and [`super::RevocableRwSemaphore`]. Callers that do not need to |
| 70 | +/// sleep while holding on to a guard should use [`crate::revocable::Revocable`] instead, which is |
| 71 | +/// more efficient as it uses RCU to keep objects alive. |
| 72 | +/// |
| 73 | +/// # Examples |
| 74 | +/// |
| 75 | +/// ``` |
| 76 | +/// # use kernel::sync::{Mutex, Revocable}; |
| 77 | +/// # use kernel::revocable_init; |
| 78 | +/// # use core::pin::Pin; |
| 79 | +/// |
| 80 | +/// struct Example { |
| 81 | +/// a: u32, |
| 82 | +/// b: u32, |
| 83 | +/// } |
| 84 | +/// |
| 85 | +/// fn add_two(v: &Revocable<Mutex<()>, Example>) -> Option<u32> { |
| 86 | +/// let mut guard = v.try_write()?; |
| 87 | +/// guard.a += 2; |
| 88 | +/// guard.b += 2; |
| 89 | +/// Some(guard.a + guard.b) |
| 90 | +/// } |
| 91 | +/// |
| 92 | +/// fn example() { |
| 93 | +/// // SAFETY: We call `revocable_init` immediately below. |
| 94 | +/// let mut v = unsafe { Revocable::<Mutex<()>, Example>::new(Example { a: 10, b: 20 }) }; |
| 95 | +/// // SAFETY: We never move out of `v`. |
| 96 | +/// let pinned = unsafe { Pin::new_unchecked(&mut v) }; |
| 97 | +/// revocable_init!(pinned, "example::v"); |
| 98 | +/// assert_eq!(add_two(&v), Some(34)); |
| 99 | +/// v.revoke(); |
| 100 | +/// assert_eq!(add_two(&v), None); |
| 101 | +/// } |
| 102 | +/// ``` |
| 103 | +pub struct Revocable<F: LockFactory, T> { |
| 104 | + inner: F::LockedType<Inner<T>>, |
| 105 | +} |
| 106 | + |
| 107 | +/// Safely initialises a [`Revocable`] instance with the given name, generating a new lock class. |
| 108 | +#[macro_export] |
| 109 | +macro_rules! revocable_init { |
| 110 | + ($mutex:expr, $name:literal) => { |
| 111 | + $crate::init_with_lockdep!($mutex, $name) |
| 112 | + }; |
| 113 | +} |
| 114 | + |
| 115 | +impl<F: LockFactory, T> Revocable<F, T> { |
| 116 | + /// Creates a new revocable instance of the given lock. |
| 117 | + /// |
| 118 | + /// # Safety |
| 119 | + /// |
| 120 | + /// The caller must call [`Revocable::init`] before using the revocable synch primitive. |
| 121 | + pub unsafe fn new(data: T) -> Self { |
| 122 | + Self { |
| 123 | + // SAFETY: The safety requirements of this function require that `Revocable::init` |
| 124 | + // be called before the returned object can be used. Lock initialisation is called |
| 125 | + // from `Revocable::init`. |
| 126 | + inner: unsafe { F::new_lock(Inner::new(data)) }, |
| 127 | + } |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +impl<F: LockFactory, T> NeedsLockClass for Revocable<F, T> |
| 132 | +where |
| 133 | + F::LockedType<Inner<T>>: NeedsLockClass, |
| 134 | +{ |
| 135 | + unsafe fn init( |
| 136 | + self: Pin<&mut Self>, |
| 137 | + name: &'static CStr, |
| 138 | + key1: *mut bindings::lock_class_key, |
| 139 | + key2: *mut bindings::lock_class_key, |
| 140 | + ) { |
| 141 | + // SAFETY: `inner` is pinned when `self` is. |
| 142 | + let inner = unsafe { self.map_unchecked_mut(|r| &mut r.inner) }; |
| 143 | + |
| 144 | + // SAFETY: The safety requirements of this function satisfy the ones for `inner.init` |
| 145 | + // (they're the same). |
| 146 | + unsafe { inner.init(name, key1, key2) }; |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +impl<F: LockFactory, T> Revocable<F, T> |
| 151 | +where |
| 152 | + F::LockedType<Inner<T>>: Lock<Inner = Inner<T>>, |
| 153 | +{ |
| 154 | + /// Revokes access to and drops the wrapped object. |
| 155 | + /// |
| 156 | + /// Revocation and dropping happen after ongoing accessors complete. |
| 157 | + pub fn revoke(&self) { |
| 158 | + self.lock().drop_in_place(); |
| 159 | + } |
| 160 | + |
| 161 | + /// Tries to lock the \[revocable\] wrapped object in write (exclusive) mode. |
| 162 | + /// |
| 163 | + /// Returns `None` if the object has been revoked and is therefore no longer accessible. |
| 164 | + /// |
| 165 | + /// Returns a guard that gives access to the object otherwise; the object is guaranteed to |
| 166 | + /// remain accessible while the guard is alive. Callers are allowed to sleep while holding on |
| 167 | + /// to the returned guard. |
| 168 | + pub fn try_write(&self) -> Option<RevocableGuard<'_, F, T, WriteLock>> { |
| 169 | + let inner = self.lock(); |
| 170 | + if !inner.is_available { |
| 171 | + return None; |
| 172 | + } |
| 173 | + Some(RevocableGuard::new(inner)) |
| 174 | + } |
| 175 | + |
| 176 | + fn lock(&self) -> Guard<'_, F::LockedType<Inner<T>>> { |
| 177 | + let ctx = self.inner.lock_noguard(); |
| 178 | + // SAFETY: The lock was acquired in the call above. |
| 179 | + unsafe { Guard::new(&self.inner, ctx) } |
| 180 | + } |
| 181 | +} |
| 182 | + |
| 183 | +impl<F: LockFactory, T> Revocable<F, T> |
| 184 | +where |
| 185 | + F::LockedType<Inner<T>>: Lock<ReadLock, Inner = Inner<T>>, |
| 186 | +{ |
| 187 | + /// Tries to lock the \[revocable\] wrapped object in read (shared) mode. |
| 188 | + /// |
| 189 | + /// Returns `None` if the object has been revoked and is therefore no longer accessible. |
| 190 | + /// |
| 191 | + /// Returns a guard that gives access to the object otherwise; the object is guaranteed to |
| 192 | + /// remain accessible while the guard is alive. Callers are allowed to sleep while holding on |
| 193 | + /// to the returned guard. |
| 194 | + pub fn try_read(&self) -> Option<RevocableGuard<'_, F, T, ReadLock>> { |
| 195 | + let ctx = self.inner.lock_noguard(); |
| 196 | + // SAFETY: The lock was acquired in the call above. |
| 197 | + let inner = unsafe { Guard::new(&self.inner, ctx) }; |
| 198 | + if !inner.is_available { |
| 199 | + return None; |
| 200 | + } |
| 201 | + Some(RevocableGuard::new(inner)) |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +/// A guard that allows access to a revocable object and keeps it alive. |
| 206 | +pub struct RevocableGuard<'a, F: LockFactory, T, I: LockInfo> |
| 207 | +where |
| 208 | + F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>, |
| 209 | +{ |
| 210 | + guard: Guard<'a, F::LockedType<Inner<T>>, I>, |
| 211 | +} |
| 212 | + |
| 213 | +impl<'a, F: LockFactory, T, I: LockInfo> RevocableGuard<'a, F, T, I> |
| 214 | +where |
| 215 | + F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>, |
| 216 | +{ |
| 217 | + fn new(guard: Guard<'a, F::LockedType<Inner<T>>, I>) -> Self { |
| 218 | + Self { guard } |
| 219 | + } |
| 220 | +} |
| 221 | + |
| 222 | +impl<F: LockFactory, T, I: LockInfo<Writable = True>> RevocableGuard<'_, F, T, I> |
| 223 | +where |
| 224 | + F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>, |
| 225 | +{ |
| 226 | + /// Returns a pinned mutable reference to the wrapped object. |
| 227 | + pub fn as_pinned_mut(&mut self) -> Pin<&mut T> { |
| 228 | + // SAFETY: Revocable mutexes must be pinned, so we choose to always project the data as |
| 229 | + // pinned as well (i.e., we guarantee we never move it). |
| 230 | + unsafe { Pin::new_unchecked(&mut *self) } |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +impl<F: LockFactory, T, I: LockInfo> Deref for RevocableGuard<'_, F, T, I> |
| 235 | +where |
| 236 | + F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>, |
| 237 | +{ |
| 238 | + type Target = T; |
| 239 | + |
| 240 | + fn deref(&self) -> &Self::Target { |
| 241 | + unsafe { &*self.guard.data.as_ptr() } |
| 242 | + } |
| 243 | +} |
| 244 | + |
| 245 | +impl<F: LockFactory, T, I: LockInfo<Writable = True>> DerefMut for RevocableGuard<'_, F, T, I> |
| 246 | +where |
| 247 | + F::LockedType<Inner<T>>: Lock<I, Inner = Inner<T>>, |
| 248 | +{ |
| 249 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 250 | + unsafe { &mut *self.guard.data.as_mut_ptr() } |
| 251 | + } |
| 252 | +} |
0 commit comments