Skip to content

Commit 22390f7

Browse files
committed
rust: sync: introduce CondVar
This is the traditional condition variable or monitor synchronisation primitive. It is implemented with C's `wait_queue_head_t`. It allows users to release a lock and go to sleep while guaranteeing that notifications won't be missed. This is achieved by enqueuing a wait entry before releasing the lock. Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Will Deacon <will@kernel.org> Cc: Waiman Long <longman@redhat.com> Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent 7106a05 commit 22390f7

File tree

5 files changed

+190
-2
lines changed

5 files changed

+190
-2
lines changed

rust/bindings/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <linux/slab.h>
1010
#include <linux/refcount.h>
11+
#include <linux/wait.h>
1112
#include <linux/sched.h>
1213

1314
/* `bindgen` gets confused at certain things. */

rust/helpers.c

+7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/mutex.h>
2525
#include <linux/spinlock.h>
2626
#include <linux/sched/signal.h>
27+
#include <linux/wait.h>
2728

2829
__noreturn void rust_helper_BUG(void)
2930
{
@@ -76,6 +77,12 @@ void rust_helper_spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
7677
}
7778
EXPORT_SYMBOL_GPL(rust_helper_spin_unlock_irqrestore);
7879

80+
void rust_helper_init_wait(struct wait_queue_entry *wq_entry)
81+
{
82+
init_wait(wq_entry);
83+
}
84+
EXPORT_SYMBOL_GPL(rust_helper_init_wait);
85+
7986
int rust_helper_signal_pending(struct task_struct *t)
8087
{
8188
return signal_pending(t);

rust/kernel/sync.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
use crate::types::Opaque;
99

1010
mod arc;
11+
mod condvar;
1112
mod lock;
1213

1314
pub use arc::{Arc, ArcBorrow, UniqueArc};
14-
pub use lock::{mutex::Mutex, spinlock::SpinLock};
15+
pub use condvar::CondVar;
16+
pub use lock::{mutex::Mutex, spinlock::SpinLock, Guard};
1517

1618
/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`.
1719
#[repr(transparent)]

rust/kernel/sync/condvar.rs

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A condition variable.
4+
//!
5+
//! This module allows Rust code to use the kernel's [`struct wait_queue_head`] as a condition
6+
//! variable.
7+
8+
use super::{lock::Backend, Guard, LockClassKey};
9+
use crate::{bindings, init::PinInit, pin_init, str::CStr, task::Task, types::Opaque};
10+
use core::marker::PhantomPinned;
11+
use macros::pin_data;
12+
13+
/// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class.
14+
#[macro_export]
15+
macro_rules! new_condvar {
16+
($name:literal) => {{
17+
let name = $crate::c_str!($name);
18+
$crate::sync::CondVar::new(name, $crate::static_lock_class!())
19+
}};
20+
}
21+
22+
/// A conditional variable.
23+
///
24+
/// Exposes the kernel's [`struct wait_queue_head`] as a condition variable. It allows the caller to
25+
/// atomically release the given lock and go to sleep. It reacquires the lock when it wakes up. And
26+
/// it wakes up when notified by another thread (via [`CondVar::notify_one`] or
27+
/// [`CondVar::notify_all`]) or because the thread received a signal. It may also wake up
28+
/// spuriously.
29+
///
30+
/// Instances of [`CondVar`] need a lock class and to be pinned. The recommended way to create such
31+
/// instances is with the [`pin_init`](crate::pin_init) and [`new_condvar`] macros.
32+
///
33+
/// # Examples
34+
///
35+
/// The following is an example of using a condvar with a mutex:
36+
///
37+
/// ```
38+
/// use kernel::sync::{CondVar, Mutex};
39+
/// use kernel::{new_condvar, new_mutex};
40+
///
41+
/// #[pin_data]
42+
/// pub struct Example {
43+
/// #[pin]
44+
/// value: Mutex<u32>,
45+
///
46+
/// #[pin]
47+
/// value_changed: CondVar,
48+
/// }
49+
///
50+
/// /// Waits for `e.value` to become `v`.
51+
/// fn wait_for_vaue(e: &Example, v: u32) {
52+
/// let mut guard = e.value.lock();
53+
/// while *guard != v {
54+
/// e.value_changed.wait_uninterruptible(&mut guard);
55+
/// }
56+
/// }
57+
///
58+
/// /// Increments `e.value` and notifies all potential waiters.
59+
/// fn increment(e: &Example) {
60+
/// *e.value.lock() += 1;
61+
/// e.value_changed.notify_all();
62+
/// }
63+
///
64+
/// /// Allocates a new boxed `Example`.
65+
/// fn new_example() -> Result<Pin<Box<Example>>> {
66+
/// Box::pin_init(pin_init!(Example {
67+
/// value <- new_mutex!(0, "Example::vlaue"),
68+
/// value_changed <- new_condvar!("Example::value_changed"),
69+
/// }))
70+
/// }
71+
/// ```
72+
///
73+
/// [`struct wait_queue_head`]: ../../../include/linux/wait.h
74+
#[pin_data]
75+
pub struct CondVar {
76+
#[pin]
77+
pub(crate) wait_list: Opaque<bindings::wait_queue_head>,
78+
79+
/// A condvar needs to be pinned because it contains a [`struct list_head`] that is
80+
/// self-referential, so it cannot be safely moved once it is initialised.
81+
#[pin]
82+
_pin: PhantomPinned,
83+
}
84+
85+
// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread.
86+
#[allow(clippy::non_send_fields_in_send_ty)]
87+
unsafe impl Send for CondVar {}
88+
89+
// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads
90+
// concurrently.
91+
unsafe impl Sync for CondVar {}
92+
93+
impl CondVar {
94+
/// Constructs a new condvar initialiser.
95+
#[allow(clippy::new_ret_no_self)]
96+
pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> {
97+
pin_init!(Self {
98+
_pin: PhantomPinned,
99+
// SAFETY: `__init_waitqueue_head` initialises the waitqueue head, and both `name` and
100+
// `key` have static lifetimes so they live indefinitely.
101+
wait_list <- unsafe {
102+
Opaque::ffi_init2(
103+
bindings::__init_waitqueue_head,
104+
name.as_char_ptr(),
105+
key.as_ptr(),
106+
)
107+
},
108+
})
109+
}
110+
111+
fn wait_internal<T: ?Sized, B: Backend>(&self, wait_state: u32, guard: &mut Guard<'_, T, B>) {
112+
let wait = Opaque::<bindings::wait_queue_entry>::uninit();
113+
114+
// SAFETY: `wait` points to valid memory.
115+
unsafe { bindings::init_wait(wait.get()) };
116+
117+
// SAFETY: Both `wait` and `wait_list` point to valid memory.
118+
unsafe {
119+
bindings::prepare_to_wait_exclusive(self.wait_list.get(), wait.get(), wait_state as _)
120+
};
121+
122+
// SAFETY: No arguments, switches to another thread.
123+
guard.do_unlocked(|| unsafe { bindings::schedule() });
124+
125+
// SAFETY: Both `wait` and `wait_list` point to valid memory.
126+
unsafe { bindings::finish_wait(self.wait_list.get(), wait.get()) };
127+
}
128+
129+
/// Releases the lock and waits for a notification in interruptible mode.
130+
///
131+
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
132+
/// thread to sleep, reacquiring the lock on wake up. It wakes up when notified by
133+
/// [`CondVar::notify_one`] or [`CondVar::notify_all`], or when the thread receives a signal.
134+
/// It may also wake up spuriously.
135+
///
136+
/// Returns whether there is a signal pending.
137+
#[must_use = "wait returns if a signal is pending, so the caller must check the return value"]
138+
pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool {
139+
self.wait_internal(bindings::TASK_INTERRUPTIBLE, guard);
140+
Task::current().signal_pending()
141+
}
142+
143+
/// Releases the lock and waits for a notification in uninterruptible mode.
144+
///
145+
/// Similar to [`CondVar::wait`], except that the wait is not interruptible. That is, the
146+
/// thread won't wake up due to signals. It may, however, wake up supirously.
147+
pub fn wait_uninterruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) {
148+
self.wait_internal(bindings::TASK_UNINTERRUPTIBLE, guard)
149+
}
150+
151+
/// Calls the kernel function to notify the appropriate number of threads with the given flags.
152+
fn notify(&self, count: i32, flags: u32) {
153+
// SAFETY: `wait_list` points to valid memory.
154+
unsafe {
155+
bindings::__wake_up(
156+
self.wait_list.get(),
157+
bindings::TASK_NORMAL,
158+
count,
159+
flags as _,
160+
)
161+
};
162+
}
163+
164+
/// Wakes a single waiter up, if any.
165+
///
166+
/// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost
167+
/// completely (as opposed to automatically waking up the next waiter).
168+
pub fn notify_one(&self) {
169+
self.notify(1, 0);
170+
}
171+
172+
/// Wakes all waiters up, if any.
173+
///
174+
/// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost
175+
/// completely (as opposed to automatically waking up the next waiter).
176+
pub fn notify_all(&self) {
177+
self.notify(0, 0);
178+
}
179+
}

rust/kernel/sync/lock.rs

-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {
177177
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
178178

179179
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
180-
#[allow(dead_code)]
181180
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
182181
// SAFETY: The caller owns the lock, so it is safe to unlock it.
183182
unsafe { B::unlock(self.lock.state.get(), &self.state) };

0 commit comments

Comments
 (0)