[PATCH v4 14/20] drm/tyr: add Wait type for GPU events

From: Deborah Brouwer

Date: Fri Apr 24 2026 - 19:43:05 EST


Add a Wait convenience type wrapping a CondVar and Mutex for sleeping
until a condition is met or a timeout expires.

The helper centralizes a common wait pattern: check the completion
predicate before sleeping, wait interruptibly with a timeout, retry on
spurious or unrelated wakeups, and perform a final predicate check before
returning ETIMEDOUT.

This will be used for CSF firmware responses and other GPU-driven events.

Also add a new_wait! macro so each Wait instance gets a call-site-specific
lockdep class key for its internal mutex.

Co-developed-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Co-developed-by: Beata Michalska <beata.michalska@xxxxxxx>
Signed-off-by: Beata Michalska <beata.michalska@xxxxxxx>
Signed-off-by: Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx>
---
drivers/gpu/drm/tyr/tyr.rs | 1 +
drivers/gpu/drm/tyr/wait.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+)

diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs
index 18b0668bb217..fd83d4b40978 100644
--- a/drivers/gpu/drm/tyr/tyr.rs
+++ b/drivers/gpu/drm/tyr/tyr.rs
@@ -16,6 +16,7 @@
mod regs;
mod slot;
mod vm;
+mod wait;

kernel::module_platform_driver! {
type: TyrPlatformDriverData,
diff --git a/drivers/gpu/drm/tyr/wait.rs b/drivers/gpu/drm/tyr/wait.rs
new file mode 100644
index 000000000000..2a4d691c443c
--- /dev/null
+++ b/drivers/gpu/drm/tyr/wait.rs
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! Code to wait on GPU events.
+#![allow(dead_code)]
+
+use kernel::{
+ new_condvar,
+ prelude::*,
+ sync::{
+ lock::{
+ mutex::MutexBackend,
+ Lock, //
+ },
+ Arc,
+ CondVar,
+ CondVarTimeoutResult,
+ Mutex, //
+ },
+ time::msecs_to_jiffies, //
+};
+
+/// Creates a new [`Wait`] instance with a call-site-specific lockdep class key.
+///
+/// Always prefer this macro over [`Wait::new_with_lock`] when the [`Wait`] instance has
+/// unique locking behaviour that could otherwise trigger false-positive lockdep
+/// warnings.
+#[macro_export]
+macro_rules! new_wait {
+ () => {{
+ let lock = new_mutex!(());
+ $crate::wait::Wait::new_with_lock(lock)
+ }};
+}
+
+/// A convenience type to wait for GPU events.
+///
+/// Wraps a [`CondVar`] and [`Mutex`] pair. The mutex synchronizes predicate checks
+/// with wait/wake operations; the condvar provides the sleep/wake mechanism.
+#[pin_data]
+pub(crate) struct Wait {
+ /// The actual wait/signal mechanism.
+ #[pin]
+ cond: CondVar,
+ /// Synchronizes waiters with notifications.
+ #[pin]
+ lock: Mutex<()>,
+}
+
+impl Wait {
+ /// Creates a new [`Wait`] with a caller-supplied lock instance.
+ ///
+ /// Use [`new_wait!`] instead of calling this directly; the macro ensures a
+ /// per-call-site lockdep class key is registered.
+ pub(crate) fn new_with_lock(lock: impl PinInit<Lock<(), MutexBackend>>) -> Result<Arc<Self>> {
+ Arc::pin_init(
+ pin_init!(Self {
+ cond <- new_condvar!(),
+ lock <- lock,
+ }),
+ GFP_KERNEL,
+ )
+ }
+
+ /// Waits until a GPU event condition is met or the timeout elapses.
+ ///
+ /// Calls `on_woken` before sleeping and after each wakeup. If `on_woken`
+ /// returns [`WaitResult::Retry`], the wait continues; [`WaitResult::Done`]
+ /// returns success.
+ ///
+ /// `on_woken` is called while the internal wait lock is held, so it must be
+ /// cheap and must not call back into code that can notify this wait object.
+ ///
+ /// Returns [`ETIMEDOUT`] if the deadline is reached without the condition
+ /// becoming true, or [`ERESTARTSYS`] if interrupted by a signal.
+ pub(crate) fn wait_interruptible_timeout<F>(&self, timeout_ms: u32, mut on_woken: F) -> Result
+ where
+ F: FnMut() -> Result<WaitResult>,
+ {
+ let mut guard = self.lock.lock();
+ let mut remaining_time = msecs_to_jiffies(timeout_ms);
+
+ loop {
+ // Check the condition before sleeping to avoid missing a wakeup
+ // that arrived between the caller's last check and acquiring the
+ // lock here.
+ if let WaitResult::Done = on_woken()? {
+ return Ok(());
+ }
+
+ match self
+ .cond
+ .wait_interruptible_timeout(&mut guard, remaining_time)
+ {
+ CondVarTimeoutResult::Woken { jiffies } => match on_woken()? {
+ WaitResult::Done => return Ok(()),
+ WaitResult::Retry => remaining_time = jiffies,
+ },
+ CondVarTimeoutResult::Timeout => {
+ // One final check before giving up.
+ if let WaitResult::Done = on_woken()? {
+ return Ok(());
+ }
+ return Err(ETIMEDOUT);
+ }
+ CondVarTimeoutResult::Signal { .. } => return Err(ERESTARTSYS),
+ }
+ }
+ }
+
+ /// Wakes all waiters.
+ ///
+ /// Takes the internal lock so notifications are serialized against waiters
+ /// checking the condition and entering the sleep state.
+ pub(crate) fn notify_all(&self) {
+ let _guard = self.lock.lock();
+ self.cond.notify_all();
+ }
+}
+
+/// The result of a wait operation.
+pub(crate) enum WaitResult {
+ /// The condition was met.
+ Done,
+ /// The wakeup was spurious or for an unrelated event; retry.
+ Retry,
+}

--
2.53.0