-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathlock.rs
More file actions
318 lines (286 loc) · 11.1 KB
/
Copy pathlock.rs
File metadata and controls
318 lines (286 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// SPDX-License-Identifier: GPL-2.0
//! Generic kernel lock and guard.
//!
//! It contains a generic Rust lock and guard that allow for different backends (e.g., mutexes,
//! spinlocks, raw spinlocks) to be provided with minimal effort.
use super::LockClassKey;
use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard};
use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned, pin::Pin};
use macros::pin_data;
pub mod mutex;
pub mod spinlock;
#[cfg(CONFIG_RROS)]
use crate::c_types;
/// The "backend" of a lock.
///
/// It is the actual implementation of the lock, without the need to repeat patterns used in all
/// locks.
///
/// # Safety
///
/// - Implementers must ensure that only one thread/CPU may access the protected data once the lock
/// is owned, that is, between calls to `lock` and `unlock`.
/// - Implementers must also ensure that `relock` uses the same locking method as the original
/// lock operation.
pub unsafe trait Backend {
/// The state required by the lock.
type State;
/// The state required to be kept between lock and unlock.
type GuardState;
/// Initialises the lock.
///
/// # Safety
///
/// `ptr` must be valid for write for the duration of the call, while `name` and `key` must
/// remain valid for read indefinitely.
unsafe fn init(
ptr: *mut Self::State,
name: *const core::ffi::c_char,
key: *mut bindings::lock_class_key,
);
/// Acquires the lock, making the caller its owner.
///
/// # Safety
///
/// Callers must ensure that [`Backend::init`] has been previously called.
#[must_use]
unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState;
/// Releases the lock, giving up its ownership.
///
/// # Safety
///
/// It must only be called by the current owner of the lock.
unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
/// Reacquires the lock, making the caller its owner.
///
/// # Safety
///
/// Callers must ensure that `guard_state` comes from a previous call to [`Backend::lock`] (or
/// variant) that has been unlocked with [`Backend::unlock`] and will be relocked now.
unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
// SAFETY: The safety requirements ensure that the lock is initialised.
*guard_state = unsafe { Self::lock(ptr) };
}
}
/// A mutual exclusion primitive.
///
/// Exposes one of the kernel locking primitives. Which one is exposed depends on the lock
/// [`Backend`] specified as the generic parameter `B`.
#[cfg(not(CONFIG_RROS_SPINLOCK))]
#[pin_data]
pub struct Lock<T: ?Sized, B: Backend> {
/// The kernel lock object.
#[pin]
state: Opaque<B::State>,
/// Some locks are known to be self-referential (e.g., mutexes), while others are architecture
/// or config defined (e.g., spinlocks). So we conservatively require them to be pinned in case
/// some architecture uses self-references now or in the future.
#[pin]
_pin: PhantomPinned,
/// The data protected by the lock.
pub(crate) data: UnsafeCell<T>,
}
// SAFETY: `Lock` can be transferred across thread boundaries iff the data it protects can.
#[cfg(not(CONFIG_RROS_SPINLOCK))]
unsafe impl<T: ?Sized + Send, B: Backend> Send for Lock<T, B> {}
// SAFETY: `Lock` serialises the interior mutability it provides, so it is `Sync` as long as the
// data it protects is `Send`.
#[cfg(not(CONFIG_RROS_SPINLOCK))]
unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T, B: Backend> Lock<T, B> {
/// Constructs a new lock initialiser.
#[allow(clippy::new_ret_no_self)]
pub fn new(t: T, name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> {
pin_init!(Self {
data: UnsafeCell::new(t),
_pin: PhantomPinned,
// SAFETY: `slot` is valid while the closure is called and both `name` and `key` have
// static lifetimes so they live indefinitely.
state <- Opaque::ffi_init(|slot| unsafe {
B::init(slot, name.as_char_ptr(), key.as_ptr())
}),
})
}
}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T: ?Sized, B: Backend> Lock<T, B> {
/// Acquires the lock and gives the caller access to the data protected by it.
pub fn lock(&self) -> Guard<'_, T, B> {
// SAFETY: The constructor of the type calls `init`, so the existence of the object proves
// that `init` was called.
let state = unsafe { B::lock(self.state.get()) };
// SAFETY: The lock was just acquired.
unsafe { Guard::new(self, state) }
}
}
/// A lock guard.
///
/// Allows mutual exclusion primitives that implement the [`Backend`] trait to automatically unlock
/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
/// protected by the lock.
#[cfg(not(CONFIG_RROS_SPINLOCK))]
#[must_use = "the lock unlocks immediately when the guard is unused"]
pub struct Guard<'a, T: ?Sized, B: Backend> {
pub(crate) lock: &'a Lock<T, B>,
pub(crate) state: B::GuardState,
_not_send: PhantomData<*mut ()>,
}
// SAFETY: `Guard` is sync when the data protected by the lock is also sync.
#[cfg(not(CONFIG_RROS_SPINLOCK))]
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
// SAFETY: The caller owns the lock, so it is safe to unlock it.
unsafe { B::unlock(self.lock.state.get(), &self.state) };
// SAFETY: The lock was just unlocked above and is being relocked now.
let _relock =
ScopeGuard::new(|| unsafe { B::relock(self.lock.state.get(), &mut self.state) });
cb();
}
}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SAFETY: The caller owns the lock, so it is safe to deref the protected data.
unsafe { &*self.lock.data.get() }
}
}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T: ?Sized, B: Backend> core::ops::DerefMut for Guard<'_, T, B> {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: The caller owns the lock, so it is safe to deref the protected data.
unsafe { &mut *self.lock.data.get() }
}
}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<T: ?Sized, B: Backend> Drop for Guard<'_, T, B> {
fn drop(&mut self) {
// SAFETY: The caller owns the lock, so it is safe to unlock it.
unsafe { B::unlock(self.lock.state.get(), &self.state) };
}
}
#[cfg(not(CONFIG_RROS_SPINLOCK))]
impl<'a, T: ?Sized, B: Backend> Guard<'a, T, B> {
/// Constructs a new immutable lock guard.
///
/// # Safety
///
/// The caller must ensure that it owns the lock.
pub(crate) unsafe fn new(lock: &'a Lock<T, B>, state: B::GuardState) -> Self {
Self {
lock,
state,
_not_send: PhantomData,
}
}
}
extern "C" {
fn rust_helper_cond_resched() -> c_types::c_int;
}
/// Safely initialises an object that has an `init` function that takes a name and a lock class as
/// arguments, examples of these are [`Mutex`] and [`SpinLock`]. Each of them also provides a more
/// specialised name that uses this macro.
#[cfg(CONFIG_RROS)]
#[doc(hidden)]
#[macro_export]
macro_rules! init_with_lockdep {
($obj:expr, $name:expr) => {{
static mut CLASS: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
core::mem::MaybeUninit::uninit();
let obj = $obj;
let name = $crate::c_str!($name);
// SAFETY: `CLASS` is never used by Rust code directly; the kernel may change it though.
#[allow(unused_unsafe)]
unsafe {
$crate::sync::NeedsLockClass::init(obj, name, CLASS.as_mut_ptr())
};
}};
}
/// A trait for types that need a lock class during initialisation.
///
/// Implementers of this trait benefit from the [`init_with_lockdep`] macro that generates a new
/// class for each initialisation call site.
#[cfg(CONFIG_RROS)]
pub trait NeedsLockClass {
/// Initialises the type instance so that it can be safely used.
///
/// Callers are encouraged to use the [`init_with_lockdep`] macro as it automatically creates a
/// new lock class on each usage.
///
/// # Safety
///
/// `key` must point to a valid memory location as it will be used by the kernel.
unsafe fn init(self: Pin<&mut Self>, name: &'static CStr, key: *mut bindings::lock_class_key);
}
/// Reschedules the caller's task if needed.
#[cfg(CONFIG_RROS_SPINLOCK)]
pub fn cond_resched() -> bool {
// SAFETY: No arguments, reschedules `current` if needed.
unsafe { rust_helper_cond_resched() != 0 }
}
/// Automatically initialises static instances of synchronisation primitives.
///
/// The syntax resembles that of regular static variables, except that the value assigned is that
/// of the protected type (if one exists). In the examples below, all primitives except for
/// [`CondVar`] require the inner value to be supplied.
///
/// # Examples
///
/// ```ignore
/// # use kernel::{init_static_sync, sync::{CondVar, Mutex, RevocableMutex, SpinLock}};
/// struct Test {
/// a: u32,
/// b: u32,
/// }
///
/// init_static_sync! {
/// static A: Mutex<Test> = Test { a: 10, b: 20 };
///
/// /// Documentation for `B`.
/// pub static B: Mutex<u32> = 0;
///
/// pub(crate) static C: SpinLock<Test> = Test { a: 10, b: 20 };
/// static D: CondVar;
///
/// static E: RevocableMutex<Test> = Test { a: 30, b: 40 };
/// }
/// ```
#[cfg(CONFIG_RROS)]
#[macro_export]
macro_rules! init_static_sync {
($($(#[$outer:meta])* $v:vis static $id:ident : $t:ty $(= $value:expr)?;)*) => {
$(
$(#[$outer])*
$v static $id: $t = {
#[link_section = ".ctors"]
#[used]
static TMP: extern "C" fn() = {
extern "C" fn constructor() {
// SAFETY: This locally-defined function is only called from a constructor,
// which guarantees that `$id` is not accessible from other threads
// concurrently.
#[allow(clippy::cast_ref_to_mut)]
let mutable = unsafe { &mut *(&$id as *const _ as *mut $t) };
// SAFETY: It's a shared static, so it cannot move.
let pinned = unsafe { core::pin::Pin::new_unchecked(mutable) };
$crate::init_with_lockdep!(pinned, stringify!($id));
}
constructor
};
$crate::init_static_sync!(@call_new $t, $($value)?)
};
)*
};
(@call_new $t:ty, $value:expr) => {{
let v = $value;
// SAFETY: the initialisation function is called by the constructor above.
unsafe { <$t>::new(v) }
}};
(@call_new $t:ty,) => {
// SAFETY: the initialisation function is called by the constructor above.
unsafe { <$t>::new() }
};
}