Skip to content

Commit 8eea62f

Browse files
Darksonnojeda
authored andcommitted
rust: sync: add global lock support
Add support for creating global variables that are wrapped in a mutex or spinlock. The implementation here is intended to replace the global mutex workaround found in the Rust Binder RFC [1]. In both cases, the global lock must be initialized before first use. The macro is unsafe to use for the same reason. The separate initialization step is required because it is tricky to access the value of __ARCH_SPIN_LOCK_UNLOCKED from Rust. Doing so will require changes to the C side. That change will happen as a follow-up to this patch. Link: https://lore.kernel.org/rust-for-linux/[email protected]/#Z31drivers:android:context.rs [1] Signed-off-by: Alice Ryhl <[email protected]> Reviewed-by: Boqun Feng <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Simplified a few intra-doc links. Formatted a few comments. Reworded title. - Miguel ] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent ae7851c commit 8eea62f

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed

rust/kernel/sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod locked_by;
1414

1515
pub use arc::{Arc, ArcBorrow, UniqueArc};
1616
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
17+
pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
1718
pub use lock::mutex::{new_mutex, Mutex};
1819
pub use lock::spinlock::{new_spinlock, SpinLock};
1920
pub use locked_by::LockedBy;

rust/kernel/sync/lock.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use macros::pin_data;
1313
pub mod mutex;
1414
pub mod spinlock;
1515

16+
pub(super) mod global;
17+
pub use global::{GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
18+
1619
/// The "backend" of a lock.
1720
///
1821
/// It is the actual implementation of the lock, without the need to repeat patterns used in all

rust/kernel/sync/lock/global.rs

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

0 commit comments

Comments
 (0)