Skip to content

Commit be5ff7d

Browse files
authored
Merge pull request torvalds#717 from wedsonaf/smutex
rust: add a simple Rust mutex implementation
2 parents 18bb7ec + 47e93d4 commit be5ff7d

File tree

3 files changed

+290
-0
lines changed

3 files changed

+290
-0
lines changed

rust/helpers.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,12 @@ const struct of_device_id *rust_helper_of_match_device(
514514
}
515515
EXPORT_SYMBOL_GPL(rust_helper_of_match_device);
516516

517+
void rust_helper_init_completion(struct completion *c)
518+
{
519+
init_completion(c);
520+
}
521+
EXPORT_SYMBOL_GPL(rust_helper_init_completion);
522+
517523
/*
518524
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
519525
* as the Rust `usize` type, so we can use it in contexts where Rust

rust/kernel/sync/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ mod mutex;
3131
mod revocable_mutex;
3232
mod rwsem;
3333
mod seqlock;
34+
pub mod smutex;
3435
mod spinlock;
3536

3637
pub use arc::{Ref, RefBorrow, UniqueRef};

rust/kernel/sync/smutex.rs

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A simple mutex implementation.
4+
//!
5+
//! Differently from [`super::Mutex`], this implementation does not require pinning, so the
6+
//! ergonomics are much improved, though the implementation is not as feature-rich as the C-based
7+
//! one. The main advantage is that it doesn't impose unsafe blocks on callers.
8+
//!
9+
//! The mutex is made up of 2 words in addition to the data it protects. The first one is accessed
10+
//! concurrently by threads trying to acquire and release the mutex, it contains a "stack" of
11+
//! waiters and a "locked" bit; the second one is only accessible by the thread holding the mutex,
12+
//! it contains a queue of waiters. Waiters are moved from the stack to the queue when the mutex is
13+
//! next unlocked while the stack is non-empty and the queue is empty. A single waiter is popped
14+
//! from the wait queue when the owner of the mutex unlocks it.
15+
//!
16+
//! The initial state of the mutex is `<locked=0, stack=[], queue=[]>`, meaning that it isn't
17+
//! locked and both the waiter stack and queue are empty.
18+
//!
19+
//! A lock operation transitions the mutex to state `<locked=1, stack=[], queue=[]>`.
20+
//!
21+
//! An unlock operation transitions the mutex back to the initial state, however, an attempt to
22+
//! lock the mutex while it's already locked results in a waiter being created (on the stack) and
23+
//! pushed onto the stack, so the state is `<locked=1, stack=[W1], queue=[]>`.
24+
//!
25+
//! Another thread trying to lock the mutex results in another waiter being pushed onto the stack,
26+
//! so the state becomes `<locked=1, stack=[W2, W1], queue=[]>`.
27+
//!
28+
//! In such states (queue is empty but stack is non-empty), the unlock operation is performed in
29+
//! three steps:
30+
//! 1. The stack is popped (but the mutex remains locked), so the state is:
31+
//! `<locked=1, stack=[], queue=[]>`
32+
//! 2. The stack is turned into a queue by reversing it, so the state is:
33+
//! `<locked=1, stack=[], queue=[W1, W2]>
34+
//! 3. Finally, the lock is released, and the first waiter is awakened, so the state is:
35+
//! `<locked=0, stack=[], queue=[W2]>`
36+
//!
37+
//! The mutex remains accessible to any threads attempting to lock it in any of the intermediate
38+
//! states above. For example, while it is locked, other threads may add waiters to the stack
39+
//! (which is ok because we want to release the ones on the queue first); another example is that
40+
//! another thread may acquire the mutex before waiter W1 in the example above, this makes the
41+
//! mutex unfair but this is desirable because the thread is running already and may in fact
42+
//! release the lock before W1 manages to get scheduled -- it also mitigates the lock convoy
43+
//! problem when the releasing thread wants to immediately acquire the lock again: it will be
44+
//! allowed to do so (as long as W1 doesn't get to it first).
45+
//!
46+
//! When the waiter queue is non-empty, unlocking the mutex always results in the first waiter being
47+
//! popped form the queue and awakened.
48+
49+
use super::{mutex::EmptyGuardContext, CreatableLock, Guard, Lock};
50+
use crate::{bindings, str::CStr, Opaque};
51+
use core::sync::atomic::{AtomicUsize, Ordering};
52+
use core::{cell::UnsafeCell, pin::Pin};
53+
54+
/// The value that is OR'd into the [`Mutex::waiter_stack`] when the mutex is locked.
55+
const LOCKED: usize = 1;
56+
57+
/// A simple mutex.
58+
///
59+
/// This is mutual-exclusion primitive. It guarantees that only one thread at a time may access the
60+
/// data it protects. When multiple threads attempt to lock the same mutex, only one at a time is
61+
/// allowed to progress, the others will block (sleep) until the mutex is unlocked, at which point
62+
/// another thread will be allowed to wake up and make progress.
63+
///
64+
/// # Examples
65+
///
66+
/// ```
67+
/// # use kernel::{Result, sync::Ref, sync::smutex::Mutex};
68+
///
69+
/// struct Example {
70+
/// a: u32,
71+
/// b: u32,
72+
/// }
73+
///
74+
/// static EXAMPLE: Mutex<Example> = Mutex::new(Example{ a: 10, b: 20 });
75+
///
76+
/// fn inc_a(example: &Mutex<Example>) {
77+
/// let mut guard = example.lock();
78+
/// guard.a += 1;
79+
/// }
80+
///
81+
/// fn sum(example: &Mutex<Example>) -> u32 {
82+
/// let guard = example.lock();
83+
/// guard.a + guard.b
84+
/// }
85+
///
86+
/// fn try_new(a: u32, b: u32) -> Result<Ref<Mutex<Example>>> {
87+
/// Ref::try_new(Mutex::new(Example {a, b}))
88+
/// }
89+
/// ```
90+
pub struct Mutex<T: ?Sized> {
91+
/// A stack of waiters.
92+
///
93+
/// It is accessed atomically by threads lock/unlocking the mutex. Additionally, the
94+
/// least-significant bit is used to indicate whether the mutex is locked or not.
95+
waiter_stack: AtomicUsize,
96+
97+
/// A queue of waiters.
98+
///
99+
/// This is only accessible to the holder of the mutex. When the owner of the mutex is
100+
/// unlocking it, it will move waiters from the stack to the queue when the queue is empty and
101+
/// the stack non-empty.
102+
waiter_queue: UnsafeCell<*mut Waiter>,
103+
104+
/// The data protected by the mutex.
105+
data: UnsafeCell<T>,
106+
}
107+
108+
// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can.
109+
#[allow(clippy::non_send_fields_in_send_ty)]
110+
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
111+
112+
// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the
113+
// data it protects is `Send`.
114+
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
115+
116+
impl<T> Mutex<T> {
117+
/// Creates a new instance of the mutex.
118+
pub const fn new(data: T) -> Self {
119+
Self {
120+
waiter_stack: AtomicUsize::new(0),
121+
waiter_queue: UnsafeCell::new(core::ptr::null_mut()),
122+
data: UnsafeCell::new(data),
123+
}
124+
}
125+
}
126+
127+
impl<T: ?Sized> Mutex<T> {
128+
/// Locks the mutex and gives the caller access to the data protected by it. Only one thread at
129+
/// a time is allowed to access the protected data.
130+
pub fn lock(&self) -> Guard<'_, Self> {
131+
let ctx = self.lock_noguard();
132+
// SAFETY: The mutex was just acquired.
133+
unsafe { Guard::new(self, ctx) }
134+
}
135+
}
136+
137+
impl<T> CreatableLock for Mutex<T> {
138+
type CreateArgType = T;
139+
140+
unsafe fn new_lock(data: Self::CreateArgType) -> Self {
141+
Self::new(data)
142+
}
143+
144+
unsafe fn init_lock(
145+
self: Pin<&mut Self>,
146+
_name: &'static CStr,
147+
_key: *mut bindings::lock_class_key,
148+
) {
149+
}
150+
}
151+
152+
// SAFETY: The mutex implementation ensures mutual exclusion.
153+
unsafe impl<T: ?Sized> Lock for Mutex<T> {
154+
type Inner = T;
155+
type GuardContext = EmptyGuardContext;
156+
157+
fn lock_noguard(&self) -> EmptyGuardContext {
158+
loop {
159+
// Try the fast path: the caller owns the mutex if we manage to set the `LOCKED` bit.
160+
//
161+
// The `acquire` order matches with one of the `release` ones in `unlock`.
162+
if self.waiter_stack.fetch_or(LOCKED, Ordering::Acquire) & LOCKED == 0 {
163+
return EmptyGuardContext;
164+
}
165+
166+
// Slow path: we'll likely need to wait, so initialise a local waiter struct.
167+
let mut waiter = Waiter {
168+
completion: Opaque::uninit(),
169+
next: core::ptr::null_mut(),
170+
};
171+
172+
// SAFETY: The completion object was just allocated on the stack and is valid for
173+
// writes.
174+
unsafe { bindings::init_completion(waiter.completion.get()) };
175+
176+
// Try to enqueue the waiter by pushing into onto the waiter stack. We want to do it
177+
// only while the mutex is locked by another thread.
178+
loop {
179+
// We use relaxed here because we're just reading the value we'll CAS later (which
180+
// has a stronger ordering on success).
181+
let mut v = self.waiter_stack.load(Ordering::Relaxed);
182+
if v & LOCKED == 0 {
183+
// The mutex was released by another thread, so try to acquire it.
184+
//
185+
// The `acquire` order matches with one of the `release` ones in `unlock`.
186+
v = self.waiter_stack.fetch_or(LOCKED, Ordering::Acquire);
187+
if v & LOCKED == 0 {
188+
return EmptyGuardContext;
189+
}
190+
}
191+
192+
waiter.next = (v & !LOCKED) as _;
193+
194+
// The `release` order matches with `acquire` in `unlock` when the stack is swapped
195+
// out. We use release order here to ensure that the other thread can see our
196+
// waiter fully initialised.
197+
if self
198+
.waiter_stack
199+
.compare_exchange(
200+
v,
201+
(&mut waiter as *mut _ as usize) | LOCKED,
202+
Ordering::Release,
203+
Ordering::Relaxed,
204+
)
205+
.is_ok()
206+
{
207+
break;
208+
}
209+
}
210+
211+
// Wait for the owner to lock to wake this thread up.
212+
//
213+
// SAFETY: Completion object was previously initialised with `init_completion` and
214+
// remains valid.
215+
unsafe { bindings::wait_for_completion(waiter.completion.get()) };
216+
}
217+
}
218+
219+
unsafe fn unlock(&self, _: &mut EmptyGuardContext) {
220+
// SAFETY: The caller owns the mutex, so it is safe to manipulate the local wait queue.
221+
let mut waiter = unsafe { *self.waiter_queue.get() };
222+
loop {
223+
// If we have a non-empty local queue of waiters, pop the first one, release the mutex,
224+
// and wake it up (the popped waiter).
225+
if !waiter.is_null() {
226+
// SAFETY: The caller owns the mutex, so it is safe to manipulate the local wait
227+
// queue.
228+
unsafe { *self.waiter_queue.get() = (*waiter).next };
229+
230+
// The `release` order matches with one of the `acquire` ones in `lock_noguard`.
231+
self.waiter_stack.fetch_and(!LOCKED, Ordering::Release);
232+
233+
// Wake up the first waiter.
234+
//
235+
// SAFETY: The completion object was initialised before being added to the wait
236+
// stack and is only removed above, when called completed. So it is safe for
237+
// writes.
238+
unsafe { bindings::complete_all((*waiter).completion.get()) };
239+
return;
240+
}
241+
242+
// Try the fast path when there are no local waiters.
243+
//
244+
// The `release` order matches with one of the `acquire` ones in `lock_noguard`.
245+
if self
246+
.waiter_stack
247+
.compare_exchange(LOCKED, 0, Ordering::Release, Ordering::Relaxed)
248+
.is_ok()
249+
{
250+
return;
251+
}
252+
253+
// We don't have a local queue, so pull the whole stack off, reverse it, and use it as a
254+
// local queue. Since we're manipulating this queue, we need to keep ownership of the
255+
// mutex.
256+
//
257+
// The `acquire` order matches with the `release` one in `lock_noguard` where a waiter
258+
// is pushed onto the stack. It ensures that we see the fully-initialised waiter.
259+
let mut stack =
260+
(self.waiter_stack.swap(LOCKED, Ordering::Acquire) & !LOCKED) as *mut Waiter;
261+
while !stack.is_null() {
262+
// SAFETY: The caller still owns the mutex, so it is safe to manipulate the
263+
// elements of the wait queue, which will soon become that wait queue.
264+
let next = unsafe { (*stack).next };
265+
266+
// SAFETY: Same as above.
267+
unsafe { (*stack).next = waiter };
268+
269+
waiter = stack;
270+
stack = next;
271+
}
272+
}
273+
}
274+
275+
fn locked_data(&self) -> &UnsafeCell<T> {
276+
&self.data
277+
}
278+
}
279+
280+
struct Waiter {
281+
completion: Opaque<bindings::completion>,
282+
next: *mut Waiter,
283+
}

0 commit comments

Comments
 (0)