Re: [PATCH 3/5] rust: sync: add WaitQueue infrastructure
"Gary Guo" <[email protected]> Mon, 27 Jul 2026 13:02:06 +0100
| Newsgroups | org.kernel.vger.rust-for-linux,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On Sun Jul 26, 2026 at 11:36 PM BST, Danilo Krummrich wrote: > Implement a wait queue wrapping the kernel's struct wait_queue_head, > with wait_event()-style methods that take a condition closure directly. > > The API mirrors the C wait_event() family: > > - wait_event() > - wait_event_interruptible() > - wait_event_timeout() > - wait_event_interruptible_timeout() > - wake_up() / wake_up_all() / wake_up_sync() > > Interruptible and timeout variants return Result<(), WaitError>, where > WaitError maps to ERESTARTSYS (signal) or ETIMEDOUT (timeout). > > Signed-off-by: Danilo Krummrich <[email protected]> > --- > rust/kernel/sync.rs | 6 + > rust/kernel/sync/wait.rs | 388 +++++++++++++++++++++++++++++++++++++++ > 2 files changed, 394 insertions(+) > create mode 100644 rust/kernel/sync/wait.rs > > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs > index df4f2604ff9b..31f0999e5747 100644 > --- a/rust/kernel/sync.rs > +++ b/rust/kernel/sync.rs > @@ -21,6 +21,7 @@ > pub mod rcu; > mod refcount; > mod set_once; > +mod wait; > > pub use arc::{Arc, ArcBorrow, UniqueArc}; > pub use completion::Completion; > @@ -38,6 +39,11 @@ > pub use locked_by::LockedBy; > pub use refcount::Refcount; > pub use set_once::SetOnce; > +pub use wait::{ > + new_waitqueue, > + WaitError, > + WaitQueue, // > +}; > > /// Represents a lockdep class. > /// > diff --git a/rust/kernel/sync/wait.rs b/rust/kernel/sync/wait.rs > new file mode 100644 > index 000000000000..ba4ee0f8d4d4 > --- /dev/null > +++ b/rust/kernel/sync/wait.rs > @@ -0,0 +1,388 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Wait queue. > +//! > +//! C header: [`include/linux/wait.h`](srctree/include/linux/wait.h) > + > +use super::LockClassKey; > +use crate::{ > + prelude::*, > + str::CStr, > + task::{ > + self, > + TASK_INTERRUPTIBLE, > + TASK_NORMAL, > + TASK_UNINTERRUPTIBLE, // Hmm, I am not sure why we are exposing these as constants from kernel::task. Regardless, Given that you're using them for bindings, you should probably get them from bindings::TASK_* instead. > + }, > + time::Jiffies, > + types::Opaque, > +}; > + > +use core::{ > + pin::Pin, > + ptr, // > +}; > + > +/// Creates a [`WaitQueue`] initialiser with the given name and a newly-created lock class. > +#[macro_export] > +macro_rules! new_waitqueue { > + ($($name:literal)?) => { > + $crate::sync::WaitQueue::new( > + $crate::optional_name!($($name)?), > + $crate::static_lock_class!(), > + ) > + }; > +} > +pub use new_waitqueue; > + > +/// Exposes the kernel's [`struct wait_queue_head`] as a Rust wait queue. > +/// > +/// A `WaitQueue` allows a thread to sleep until a caller-supplied condition becomes true, > +/// re-checking the condition on each wake-up. This matches the C `wait_event()` family of macros. > +/// > +/// For waiting with a lock guard (the condition variable pattern), use [`CondVar`](super::CondVar) > +/// instead. > +/// > +/// Instances of `WaitQueue` need a lock class and to be pinned. The recommended way to create such > +/// instances is with the [`pin_init!`] and [`new_waitqueue!`] macros. > +/// > +/// # Examples > +/// > +/// ``` > +/// use kernel::sync::{ > +/// atomic::{ > +/// Atomic, > +/// Relaxed, > +/// }, > +/// new_waitqueue, > +/// WaitQueue, > +/// }; > +/// > +/// #[pin_data] > +/// pub struct Example { > +/// value: Atomic<i32>, > +/// #[pin] > +/// queue: WaitQueue, > +/// } > +/// > +/// fn wait_for_value(e: &Example, v: i32) { > +/// e.queue.wait_event(|| e.value.load(Relaxed) == v); > +/// } > +/// > +/// fn set_value(e: &Example, v: i32) { > +/// e.value.store(v, Relaxed); > +/// e.queue.wake_up(); > +/// } > +/// ``` > +/// > +/// [`struct wait_queue_head`]: srctree/include/linux/wait.h > +#[pin_data] > +pub struct WaitQueue { > + #[pin] > + wait_queue_head: Opaque<bindings::wait_queue_head>, > +} > + > +// SAFETY: `WaitQueue` only uses a `struct wait_queue_head`, which is safe to use on any thread. > +unsafe impl Send for WaitQueue {} > + > +// SAFETY: `WaitQueue` only uses a `struct wait_queue_head`, which is safe to use on multiple > +// threads concurrently. > +unsafe impl Sync for WaitQueue {} > > [snip] > > + > +/// Error returned by [`WaitQueue`] wait functions. > +#[derive(Debug, PartialEq)] > +pub enum WaitError { > + /// Interrupted by a signal. > + Signal, > + /// The timeout elapsed without the condition being met. > + Timeout, > +} Do we want /// Interrupted by a signal pub struct Interrupted; /// Timed out pub struct TimedOut; pub enum WaitError { ... } impl From<Interrupted> for WaitError { ... } impl From<TimedOut> for WaitError { ... } so that the user of wait_event_interruptible won't need to handle `WaitError::Timeout` and vice versa? (I'd say that both these "error" types are quite commonly special handled so they probably do deserve there own error type) Best, Gary > + > +impl From<WaitError> for Error { > + #[inline] > + fn from(e: WaitError) -> Error { > + match e { > + WaitError::Signal => ERESTARTSYS, > + WaitError::Timeout => ETIMEDOUT, > + } > + } > +}