kernel/sync/lock/global.rs
1// SPDX-License-Identifier: GPL-2.0
2
3// Copyright (C) 2024 Google LLC.
4
5//! Support for defining statics containing locks.
6
7use crate::{
8 str::{CStr, CStrExt as _},
9 sync::lock::{Backend, Guard, Lock},
10 sync::{LockClassKey, LockedBy},
11 types::Opaque,
12};
13use core::{
14 cell::UnsafeCell,
15 marker::{PhantomData, PhantomPinned},
16 pin::Pin,
17};
18
19/// Trait implemented for marker types for global locks.
20///
21/// See [`global_lock!`] for examples.
22pub trait GlobalLockBackend {
23 /// The name for this global lock.
24 const NAME: &'static CStr;
25 /// Item type stored in this global lock.
26 type Item: 'static;
27 /// The backend used for this global lock.
28 type Backend: Backend + 'static;
29 /// The class for this global lock.
30 fn get_lock_class() -> Pin<&'static LockClassKey>;
31}
32
33/// Type used for global locks.
34///
35/// See [`global_lock!`] for examples.
36pub struct GlobalLock<B: GlobalLockBackend> {
37 inner: Lock<B::Item, B::Backend>,
38}
39
40impl<B: GlobalLockBackend> GlobalLock<B> {
41 /// Creates a global lock.
42 ///
43 /// # Safety
44 ///
45 /// * Before any other method on this lock is called, [`Self::init`] must be called.
46 /// * The type `B` must not be used with any other lock.
47 pub const unsafe fn new(data: B::Item) -> Self {
48 Self {
49 inner: Lock {
50 state: Opaque::uninit(),
51 data: UnsafeCell::new(data),
52 _pin: PhantomPinned,
53 },
54 }
55 }
56
57 /// Initializes a global lock.
58 ///
59 /// # Safety
60 ///
61 /// Must not be called more than once on a given lock.
62 pub unsafe fn init(&'static self) {
63 // SAFETY: The pointer to `state` is valid for the duration of this call, and both `name`
64 // and `key` are valid indefinitely. The `state` is pinned since we have a `'static`
65 // reference to `self`.
66 //
67 // We have exclusive access to the `state` since the caller of `new` promised to call
68 // `init` before using any other methods. As `init` can only be called once, all other
69 // uses of this lock must happen after this call.
70 unsafe {
71 B::Backend::init(
72 self.inner.state.get(),
73 B::NAME.as_char_ptr(),
74 B::get_lock_class().as_ptr(),
75 )
76 }
77 }
78
79 /// Lock this global lock.
80 #[inline]
81 pub fn lock(&'static self) -> GlobalGuard<B> {
82 GlobalGuard {
83 inner: self.inner.lock(),
84 }
85 }
86
87 /// Try to lock this global lock.
88 #[inline]
89 pub fn try_lock(&'static self) -> Option<GlobalGuard<B>> {
90 Some(GlobalGuard {
91 inner: self.inner.try_lock()?,
92 })
93 }
94}
95
96/// A guard for a [`GlobalLock`].
97///
98/// See [`global_lock!`] for examples.
99pub struct GlobalGuard<B: GlobalLockBackend> {
100 inner: Guard<'static, B::Item, B::Backend>,
101}
102
103impl<B: GlobalLockBackend> core::ops::Deref for GlobalGuard<B> {
104 type Target = B::Item;
105
106 fn deref(&self) -> &Self::Target {
107 &self.inner
108 }
109}
110
111impl<B: GlobalLockBackend> core::ops::DerefMut for GlobalGuard<B>
112where
113 B::Item: Unpin,
114{
115 fn deref_mut(&mut self) -> &mut Self::Target {
116 &mut self.inner
117 }
118}
119
120/// A version of [`LockedBy`] for a [`GlobalLock`].
121///
122/// See [`global_lock!`] for examples.
123pub struct GlobalLockedBy<T: ?Sized, B: GlobalLockBackend> {
124 _backend: PhantomData<B>,
125 value: UnsafeCell<T>,
126}
127
128// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`.
129unsafe impl<T, B> Send for GlobalLockedBy<T, B>
130where
131 T: ?Sized,
132 B: GlobalLockBackend,
133 LockedBy<T, B::Item>: Send,
134{
135}
136
137// SAFETY: The same thread-safety rules as `LockedBy` apply to `GlobalLockedBy`.
138unsafe impl<T, B> Sync for GlobalLockedBy<T, B>
139where
140 T: ?Sized,
141 B: GlobalLockBackend,
142 LockedBy<T, B::Item>: Sync,
143{
144}
145
146impl<T, B: GlobalLockBackend> GlobalLockedBy<T, B> {
147 /// Create a new [`GlobalLockedBy`].
148 ///
149 /// The provided value will be protected by the global lock indicated by `B`.
150 pub fn new(val: T) -> Self {
151 Self {
152 value: UnsafeCell::new(val),
153 _backend: PhantomData,
154 }
155 }
156}
157
158impl<T: ?Sized, B: GlobalLockBackend> GlobalLockedBy<T, B> {
159 /// Access the value immutably.
160 ///
161 /// The caller must prove shared access to the lock.
162 pub fn as_ref<'a>(&'a self, _guard: &'a GlobalGuard<B>) -> &'a T {
163 // SAFETY: The lock is globally unique, so there can only be one guard.
164 unsafe { &*self.value.get() }
165 }
166
167 /// Access the value mutably.
168 ///
169 /// The caller must prove shared exclusive to the lock.
170 pub fn as_mut<'a>(&'a self, _guard: &'a mut GlobalGuard<B>) -> &'a mut T {
171 // SAFETY: The lock is globally unique, so there can only be one guard.
172 unsafe { &mut *self.value.get() }
173 }
174
175 /// Access the value mutably directly.
176 ///
177 /// The caller has exclusive access to this `GlobalLockedBy`, so they do not need to hold the
178 /// lock.
179 pub fn get_mut(&mut self) -> &mut T {
180 self.value.get_mut()
181 }
182}
183
184/// Defines a global lock.
185///
186/// The global mutex must be initialized before first use. Usually this is done by calling
187/// [`GlobalLock::init`] in the module initializer.
188///
189/// # Examples
190///
191/// A global counter:
192///
193/// ```
194/// # mod ex {
195/// # use kernel::prelude::*;
196/// kernel::sync::global_lock! {
197/// // SAFETY: Initialized in module initializer before first use.
198/// unsafe(uninit) static MY_COUNTER: Mutex<u32> = 0;
199/// }
200///
201/// fn increment_counter() -> u32 {
202/// let mut guard = MY_COUNTER.lock();
203/// *guard += 1;
204/// *guard
205/// }
206///
207/// impl kernel::Module for MyModule {
208/// fn init(_module: &'static ThisModule) -> Result<Self> {
209/// // SAFETY: Called exactly once.
210/// unsafe { MY_COUNTER.init() };
211///
212/// Ok(MyModule {})
213/// }
214/// }
215/// # struct MyModule {}
216/// # }
217/// ```
218///
219/// A global mutex used to protect all instances of a given struct:
220///
221/// ```
222/// # mod ex {
223/// # use kernel::prelude::*;
224/// use kernel::sync::{GlobalGuard, GlobalLockedBy};
225///
226/// kernel::sync::global_lock! {
227/// // SAFETY: Initialized in module initializer before first use.
228/// unsafe(uninit) static MY_MUTEX: Mutex<()> = ();
229/// }
230///
231/// /// All instances of this struct are protected by `MY_MUTEX`.
232/// struct MyStruct {
233/// my_counter: GlobalLockedBy<u32, MY_MUTEX>,
234/// }
235///
236/// impl MyStruct {
237/// /// Increment the counter in this instance.
238/// ///
239/// /// The caller must hold the `MY_MUTEX` mutex.
240/// fn increment(&self, guard: &mut GlobalGuard<MY_MUTEX>) -> u32 {
241/// let my_counter = self.my_counter.as_mut(guard);
242/// *my_counter += 1;
243/// *my_counter
244/// }
245/// }
246///
247/// impl kernel::Module for MyModule {
248/// fn init(_module: &'static ThisModule) -> Result<Self> {
249/// // SAFETY: Called exactly once.
250/// unsafe { MY_MUTEX.init() };
251///
252/// Ok(MyModule {})
253/// }
254/// }
255/// # struct MyModule {}
256/// # }
257/// ```
258#[macro_export]
259macro_rules! global_lock {
260 {
261 $(#[$meta:meta])* $pub:vis
262 unsafe(uninit) static $name:ident: $kind:ident<$valuety:ty> = $value:expr;
263 } => {
264 #[doc = ::core::concat!(
265 "Backend type used by [`",
266 ::core::stringify!($name),
267 "`](static@",
268 ::core::stringify!($name),
269 ")."
270 )]
271 #[allow(non_camel_case_types, unreachable_pub)]
272 $pub enum $name {}
273
274 impl $crate::sync::lock::GlobalLockBackend for $name {
275 const NAME: &'static $crate::str::CStr = $crate::c_str!(::core::stringify!($name));
276 type Item = $valuety;
277 type Backend = $crate::global_lock_inner!(backend $kind);
278
279 fn get_lock_class() -> Pin<&'static $crate::sync::LockClassKey> {
280 $crate::static_lock_class!()
281 }
282 }
283
284 $(#[$meta])*
285 $pub static $name: $crate::sync::lock::GlobalLock<$name> = {
286 // Defined here to be outside the unsafe scope.
287 let init: $valuety = $value;
288
289 // SAFETY:
290 // * The user of this macro promises to initialize the macro before use.
291 // * We are only generating one static with this backend type.
292 unsafe { $crate::sync::lock::GlobalLock::new(init) }
293 };
294 };
295}
296pub use global_lock;
297
298#[doc(hidden)]
299#[macro_export]
300macro_rules! global_lock_inner {
301 (backend Mutex) => {
302 $crate::sync::lock::mutex::MutexBackend
303 };
304 (backend SpinLock) => {
305 $crate::sync::lock::spinlock::SpinLockBackend
306 };
307}