Skip to content

Commit 6c80828

Browse files
committed
Add sync module with a mutex implementation.
1 parent e2b0b85 commit 6c80828

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub mod prelude;
4141
pub mod printk;
4242
pub mod random;
4343
mod static_assert;
44+
pub mod sync;
4445

4546
#[cfg(CONFIG_SYSCTL)]
4647
pub mod sysctl;

rust/kernel/sync/guard.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/// Allows mutual exclusion primitives that implement the [`Lock`] trait to automatically unlock
4+
/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
5+
/// protected by the lock.
6+
#[must_use = "the lock unlocks immediately when the guard is unused"]
7+
pub struct Guard<'a, L: Lock + ?Sized> {
8+
pub(crate) lock: &'a L,
9+
}
10+
11+
// SAFETY: `Guard` is sync when the data protected by the lock is also sync. This is more
12+
// conservative than the default compiler implementation; more details can be found on
13+
// https://github.com/rust-lang/rust/issues/41622 -- it refers to MutexGuard from the standard
14+
// library.
15+
unsafe impl<L> Sync for Guard<'_, L>
16+
where
17+
L: Lock + ?Sized,
18+
L::Inner: Sync,
19+
{
20+
}
21+
22+
impl<L: Lock + ?Sized> core::ops::Deref for Guard<'_, L> {
23+
type Target = L::Inner;
24+
25+
fn deref(&self) -> &Self::Target {
26+
// SAFETY: The caller owns the lock, so it is safe to deref the protected data.
27+
unsafe { &*self.lock.locked_data().get() }
28+
}
29+
}
30+
31+
impl<L: Lock + ?Sized> core::ops::DerefMut for Guard<'_, L> {
32+
fn deref_mut(&mut self) -> &mut L::Inner {
33+
// SAFETY: The caller owns the lock, so it is safe to deref the protected data.
34+
unsafe { &mut *self.lock.locked_data().get() }
35+
}
36+
}
37+
38+
impl<L: Lock + ?Sized> Drop for Guard<'_, L> {
39+
fn drop(&mut self) {
40+
// SAFETY: The caller owns the lock, so it is safe to unlock it.
41+
unsafe { self.lock.unlock() };
42+
}
43+
}
44+
45+
impl<'a, L: Lock + ?Sized> Guard<'a, L> {
46+
/// Constructs a new lock guard.
47+
///
48+
/// # Safety
49+
///
50+
/// The caller must ensure that it owns the lock.
51+
pub(crate) unsafe fn new(lock: &'a L) -> Self {
52+
Self { lock }
53+
}
54+
}
55+
56+
/// A generic mutual exclusion primitive.
57+
///
58+
/// [`Guard`] is written such that any mutual exclusion primitive that can implement this trait can
59+
/// also benefit from having an automatic way to unlock itself. Additional primitives like
60+
/// [`Condvar`] and [`LockedBy`] also use this trait.
61+
pub trait Lock {
62+
/// The type of the data protected by the lock.
63+
type Inner: ?Sized;
64+
65+
/// Acquires the lock, making the caller its owner.
66+
fn lock_noguard(&self);
67+
68+
/// Releases the lock, giving up ownership of the lock.
69+
///
70+
/// # Safety
71+
///
72+
/// It must only be called by the current owner of the lock.
73+
unsafe fn unlock(&self);
74+
75+
/// Returns the data protected by the lock.
76+
///
77+
/// # Safety
78+
///
79+
/// It must only be called by the current owner of the lock.
80+
unsafe fn locked_data(&self) -> &core::cell::UnsafeCell<Self::Inner>;
81+
}

rust/kernel/sync/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
mod guard;
4+
mod mutex;
5+
6+
pub use guard::{Guard, Lock};
7+
pub use mutex::Mutex;
8+
9+
/// Safely initialises an object that has an `init` function that takes a name and a lock class as
10+
/// arguments, examples of these are [`Mutex`], [`SpinLock`], and [`Condvar`]. Each of them also
11+
/// provides a more specialised name that uses this macro.
12+
#[macro_export]
13+
macro_rules! init_with_lockdep {
14+
($obj:expr, $name:literal) => {{
15+
static mut CLASS: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
16+
core::mem::MaybeUninit::uninit();
17+
// SAFETY: `CLASS` is never used by rust code directly; the kernel may change it though.
18+
unsafe { $obj.init($crate::cstr!($name), CLASS.as_mut_ptr()) };
19+
}};
20+
}

rust/kernel/sync/mutex.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use super::{Guard, Lock};
4+
use crate::{bindings, CStr};
5+
use core::{cell::UnsafeCell, pin::Pin};
6+
7+
/// Safely initialises a [`Mutex`] wih the given name and new lock class.
8+
#[macro_export]
9+
macro_rules! mutex_init {
10+
($mutex:expr, $name:literal) => {
11+
$crate::init_with_lockdep!($mutex, $name)
12+
};
13+
}
14+
15+
/// Exposes the kernel's `struct mutex`. When multiple threads attempt to lock the same mutex, only
16+
/// one at a time is allowed to progress, the others will block (sleep) until the mutex is
17+
/// unlocked, at which point another thread will be allowed to wake up and make progress.
18+
///
19+
/// A [`Mutex`] must first be initialised with a call to [`Mutex::init`] before it can be used. The
20+
/// [`mutex_init`] macro is provided to automatically assign a new lock class to a mutex instance.
21+
///
22+
/// Since it may block, [`Mutex`] needs to be used with care in atomic contexts.
23+
pub struct Mutex<T: ?Sized> {
24+
/// The kernel `struct mutex` object.
25+
mutex: UnsafeCell<bindings::mutex>,
26+
27+
/// The data protected by the mutex.
28+
data: UnsafeCell<T>,
29+
}
30+
31+
// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can.
32+
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
33+
// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the
34+
// data it protects is also `Sync`.
35+
unsafe impl<T: ?Sized + Sync> Sync for Mutex<T> {}
36+
37+
impl<T> Mutex<T> {
38+
/// Constructs a new mutex.
39+
///
40+
/// # Safety
41+
///
42+
/// The caller must call [`Mutex::init`] before using the mutex.
43+
pub unsafe fn new(t: T) -> Self {
44+
Self {
45+
mutex: UnsafeCell::new(bindings::mutex::default()),
46+
data: UnsafeCell::new(t),
47+
}
48+
}
49+
}
50+
51+
impl<T: ?Sized> Mutex<T> {
52+
/// Initialises the mutex so that it can be safely used.
53+
///
54+
/// Callers are encouraged to use the [`mutex_init`] macro as it automatically creates a new
55+
/// lock class on each usage.
56+
///
57+
/// # Safety
58+
///
59+
/// `key` must point to a valid memory location as it will be used by the kernel.
60+
pub unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
61+
bindings::__mutex_init(self.mutex.get(), name.as_ptr() as _, key);
62+
}
63+
64+
/// Locks the mutex and gives the caller access to the data protected by it. Only one thread at
65+
/// a time is allowed to access the protected data.
66+
pub fn lock(&self) -> Guard<Self> {
67+
self.lock_noguard();
68+
// SAFETY: The mutex was just acquired.
69+
unsafe { Guard::new(self) }
70+
}
71+
}
72+
73+
impl<T: ?Sized> Lock for Mutex<T> {
74+
type Inner = T;
75+
76+
#[cfg(not(CONFIG_DEBUG_LOCK_ALLOC))]
77+
fn lock_noguard(&self) {
78+
// SAFETY: `mutex` points to valid memory.
79+
unsafe { bindings::mutex_lock(self.mutex.get()) };
80+
}
81+
82+
#[cfg(CONFIG_DEBUG_LOCK_ALLOC)]
83+
fn lock_noguard(&self) {
84+
// SAFETY: `mutex` points to valid memory.
85+
unsafe { bindings::mutex_lock_nested(self.mutex.get(), 0) };
86+
}
87+
88+
unsafe fn unlock(&self) {
89+
bindings::mutex_unlock(self.mutex.get());
90+
}
91+
92+
unsafe fn locked_data(&self) -> &UnsafeCell<T> {
93+
&self.data
94+
}
95+
}

0 commit comments

Comments
 (0)