[PATCH v4 13/17] gpu: nova-core: add a GSP message queue drain
From: John Hubbard
Date: Sat Sep 12 2026 - 00:47:37 EST
The GSP posts unsolicited events on the GSP-to-CPU queue whenever it has
something to report, whether or not a caller is waiting for a reply.
Every existing way to read the queue asks for one message type and waits
on a deadline. A caller that only wants to empty the queue had nothing
to call.
Add a drain that logs and consumes whatever the GSP has already posted
and returns as soon as the queue is empty, without waiting. It holds the
queue mutex, so no reply is among the messages it reads, and it treats
every one of them as an event.
Assisted-by: LLM
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 41 +++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 4595aa2176e5..acee444e898d 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -632,6 +632,21 @@ pub(crate) fn await_msg<M: MessageFromGsp>(&self) -> Result<M>
{
self.inner.lock().await_msg()
}
+
+ /// Logs and consumes every message the GSP has already posted, and returns without waiting for
+ /// more.
+ ///
+ /// No caller is waiting for a reply while this holds the queue mutex, so every message is
+ /// logged as an event. See "Draining the GSP-to-CPU queue" in
+ /// `Documentation/gpu/nova/core/interrupts.rst`.
+ ///
+ /// # Errors
+ ///
+ /// `EIO` if the queue is poisoned, or if a message fails framing or checksum validation.
+ #[expect(dead_code)]
+ pub(crate) fn drain(&self) -> Result {
+ self.inner.lock().drain()
+ }
}
/// Inner mutex protected state of [`Cmdq`].
@@ -973,4 +988,30 @@ fn log_event(&self, function: Result<MsgFunction, u32>, seq: u32) {
}
}
}
+
+ /// Logs and consumes every message the queue holds.
+ ///
+ /// # Errors
+ ///
+ /// `EIO` if the queue is poisoned, a message fails framing or checksum validation, or a
+ /// message's page count overflows a `u32`.
+ fn drain(&mut self) -> Result {
+ while !self.gsp_mem.driver_read_area().0.is_empty() {
+ // A message is available, so this returns without waiting.
+ let msg = self.wait_for_msg(Delta::ZERO)?;
+
+ let pages =
+ u32::try_from(msg.header.length().div_ceil(GSP_PAGE_SIZE)).map_err(|_| {
+ dev_err!(&self.dev, "GSP drain: message length overflow\n");
+ EIO
+ })?;
+ let function = msg.header.function();
+ let seq = msg.header.sequence();
+
+ self.gsp_mem.advance_cpu_read_ptr(pages);
+ self.log_event(function, seq);
+ }
+
+ Ok(())
+ }
}
--
2.55.0