forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 435
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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. It also allows users to wait without releasing any locks, which makes it just a wait queue (with the possibility of spurious wakes). Cc: Peter Zijlstra <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Will Deacon <[email protected]> Cc: Waiman Long <[email protected]> Signed-off-by: Wedson Almeida Filho <[email protected]>
- Loading branch information
Showing
5 changed files
with
262 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! A condition variable. | ||
//! | ||
//! This module allows Rust code to use the kernel's [`struct wait_queue_head`] as a condition | ||
//! variable. | ||
use super::{lock::Backend, Guard, LockClassKey}; | ||
use crate::{bindings, init::PinInit, pin_init, str::CStr, task::Task, types::Opaque}; | ||
use core::marker::PhantomPinned; | ||
use macros::pin_data; | ||
|
||
/// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class. | ||
#[macro_export] | ||
macro_rules! new_condvar { | ||
($name:literal) => {{ | ||
let name = $crate::c_str!($name); | ||
$crate::sync::CondVar::new(name, $crate::static_lock_class!()) | ||
}}; | ||
} | ||
|
||
/// A conditional variable. | ||
/// | ||
/// Exposes the kernel's [`struct wait_queue_head`] as a condition variable. It allows the caller to | ||
/// atomically release the given lock and go to sleep. It reacquires the lock when it wakes up. And | ||
/// it wakes up when notified by another thread (via [`CondVar::notify_one`] or | ||
/// [`CondVar::notify_all`]) or because the thread received a signal. | ||
/// | ||
/// # Examples | ||
/// | ||
/// The following is an example of using a condvar with a mutex: | ||
/// | ||
/// ``` | ||
/// use kernel::sync::{CondVar, Mutex}; | ||
/// use kernel::{new_condvar, new_mutex}; | ||
/// | ||
/// #[pin_data] | ||
/// pub struct Example { | ||
/// #[pin] | ||
/// value: Mutex<u32>, | ||
/// | ||
/// #[pin] | ||
/// value_changed: CondVar, | ||
/// } | ||
/// | ||
/// /// Waits for `e.value` to become `v`. | ||
/// fn wait_for_vaue(e: &Example, v: u32) { | ||
/// let mut guard = e.value.lock(); | ||
/// while *guard != v { | ||
/// e.value_changed.wait_uninterruptible(&mut guard); | ||
/// } | ||
/// } | ||
/// | ||
/// /// Increments `e.value` and notifies all potential waiters. | ||
/// fn increment(e: &Example) { | ||
/// *e.value.lock() += 1; | ||
/// e.value_changed.notify_all(); | ||
/// } | ||
/// | ||
/// /// Allocates a new boxed `Example`. | ||
/// fn new_example() -> Result<Pin<Box<Example>>> { | ||
/// Box::pin_init(pin_init!(Example { | ||
/// value <- new_mutex!(0, "Example::vlaue"), | ||
/// value_changed <- new_condvar!("Example::value_changed"), | ||
/// })) | ||
/// } | ||
/// ``` | ||
/// | ||
/// The following is an example of using a condvar with an atomic variable: | ||
/// | ||
/// ``` | ||
/// use kernel::sync::CondVar; | ||
/// use core::sync::atomic::{AtomicU32, Ordering}; | ||
/// | ||
/// /// Example. | ||
/// #[pin_data] | ||
/// pub struct Example { | ||
/// value: AtomicU32, | ||
/// | ||
/// #[pin] | ||
/// value_changed: CondVar, | ||
/// } | ||
/// | ||
/// /// Waits for `e.value` to become `v`. | ||
/// fn wait_for_vaue(e: &Example, v: u32) { | ||
/// while e.value.load(Ordering::Acquire) != v { | ||
/// e.value_changed.wait_nolock_uninterruptible(); | ||
/// } | ||
/// } | ||
/// | ||
/// /// Increments `e.value` and notifies all potential waiters. | ||
/// fn increment(e: &Example) { | ||
/// e.value.fetch_add(1, Ordering::Release); | ||
/// e.value_changed.notify_all(); | ||
/// } | ||
/// | ||
/// /// Allocates a new boxed `Example`. | ||
/// fn new_example() -> Result<Pin<Box<Example>>> { | ||
/// Box::pin_init(pin_init!(Example { | ||
/// value: AtomicU32::new(0), | ||
/// value_changed <- new_condvar!("Example::value_changed"), | ||
/// })) | ||
/// } | ||
/// ``` | ||
/// | ||
/// [`struct wait_queue_head`]: ../../../include/linux/wait.h | ||
#[pin_data] | ||
pub struct CondVar { | ||
#[pin] | ||
pub(crate) wait_list: Opaque<bindings::wait_queue_head>, | ||
|
||
/// A condvar needs to be pinned because it contains a [`struct list_head`] that is | ||
/// self-referential, so it cannot be safely moved once it is initialised. | ||
#[pin] | ||
_pin: PhantomPinned, | ||
} | ||
|
||
// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread. | ||
#[allow(clippy::non_send_fields_in_send_ty)] | ||
unsafe impl Send for CondVar {} | ||
|
||
// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads | ||
// concurrently. | ||
unsafe impl Sync for CondVar {} | ||
|
||
impl CondVar { | ||
/// Constructs a new condvar initialiser. | ||
#[allow(clippy::new_ret_no_self)] | ||
pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { | ||
pin_init!(Self { | ||
_pin: PhantomPinned, | ||
// SAFETY: `__init_waitqueue_head` initialises the waitqueue head, and both `name` and | ||
// `key` have static lifetimes so they live indefinitely. | ||
wait_list <- unsafe { | ||
Opaque::ffi_init2( | ||
bindings::__init_waitqueue_head, | ||
name.as_char_ptr(), | ||
key.as_ptr(), | ||
) | ||
}, | ||
}) | ||
} | ||
|
||
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the | ||
/// thread to sleep. It wakes up when notified by [`CondVar::notify_one`] or | ||
/// [`CondVar::notify_all`], or when the thread receives a signal. | ||
/// | ||
/// Returns whether there is a signal pending. | ||
pub fn wait_internal<T, B>(&self, wait_state: u32, guard: Option<&mut Guard<'_, T, B>>) | ||
where | ||
T: ?Sized, | ||
B: Backend, | ||
{ | ||
let wait = Opaque::<bindings::wait_queue_entry>::uninit(); | ||
|
||
// SAFETY: `wait` points to valid memory. | ||
unsafe { bindings::init_wait(wait.get()) }; | ||
|
||
// SAFETY: Both `wait` and `wait_list` point to valid memory. | ||
unsafe { | ||
bindings::prepare_to_wait_exclusive(self.wait_list.get(), wait.get(), wait_state as _) | ||
}; | ||
|
||
if let Some(g) = guard { | ||
// SAFETY: No arguments, switches to another thread. | ||
g.do_unlocked(|| unsafe { bindings::schedule() }); | ||
} else { | ||
// SAFETY: No arguments, switches to another thread. | ||
unsafe { bindings::schedule() }; | ||
} | ||
|
||
// SAFETY: Both `wait` and `wait_list` point to valid memory. | ||
unsafe { bindings::finish_wait(self.wait_list.get(), wait.get()) }; | ||
} | ||
|
||
/// Releases the lock and waits for a notification in interruptible mode. | ||
/// | ||
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the | ||
/// thread to sleep. It wakes up when notified by [`CondVar::notify_one`] or | ||
/// [`CondVar::notify_all`], or when the thread receives a signal. It may also wake up | ||
/// spuriously. | ||
/// | ||
/// Returns whether there is a signal pending. | ||
#[must_use = "wait returns if a signal is pending, so the caller must check the return value"] | ||
pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool { | ||
self.wait_internal(bindings::TASK_INTERRUPTIBLE, Some(guard)); | ||
Task::current().signal_pending() | ||
} | ||
|
||
/// Waits for a notification in interruptible mode. | ||
/// | ||
/// It may also wake up spuriously. | ||
/// | ||
/// Returns whether there is a signal pending. | ||
#[must_use = "wait returns if a signal is pending, so the caller must check the return value"] | ||
pub fn wait_nolock(&self) -> bool { | ||
self.wait_internal::<(), super::lock::mutex::MutexBackend>( | ||
bindings::TASK_INTERRUPTIBLE, | ||
None, | ||
); | ||
Task::current().signal_pending() | ||
} | ||
|
||
/// Releases the lock and waits for a notification in uninterruptible mode. | ||
/// | ||
/// Similar to [`CondVar::wait`], except that the wait is not interruptible. That is, the | ||
/// thread won't wake up due to signals. It may, however, wake up supirously. | ||
pub fn wait_uninterruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) { | ||
self.wait_internal(bindings::TASK_UNINTERRUPTIBLE, Some(guard)) | ||
} | ||
|
||
/// Waits for a notification in uninterruptible mode. | ||
/// | ||
/// Similar to [`CondVar::wait`], except that the wait is not interruptible. That is, the | ||
/// thread won't wake up due to signals. It may, however, wake up supirously. | ||
pub fn wait_nolock_uninterruptible(&self) { | ||
self.wait_internal::<(), super::lock::mutex::MutexBackend>( | ||
bindings::TASK_UNINTERRUPTIBLE, | ||
None, | ||
) | ||
} | ||
|
||
/// Calls the kernel function to notify the appropriate number of threads with the given flags. | ||
fn notify(&self, count: i32, flags: u32) { | ||
// SAFETY: `wait_list` points to valid memory. | ||
unsafe { | ||
bindings::__wake_up( | ||
self.wait_list.get(), | ||
bindings::TASK_NORMAL, | ||
count, | ||
flags as _, | ||
) | ||
}; | ||
} | ||
|
||
/// Wakes a single waiter up, if any. | ||
/// | ||
/// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost | ||
/// completely (as opposed to automatically waking up the next waiter). | ||
pub fn notify_one(&self) { | ||
self.notify(1, 0); | ||
} | ||
|
||
/// Wakes all waiters up, if any. | ||
/// | ||
/// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost | ||
/// completely (as opposed to automatically waking up the next waiter). | ||
pub fn notify_all(&self) { | ||
self.notify(0, 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters