From: Zhi Wang <[email protected]> GSP plugin shutdown completes through a separate event carrying the GFID. Releasing the queue guard after sending would let another receiver consume that completion.
Keep the guard from sending through the matching event and use the shared Inner receive deadline. Offer GMC events to the completion predicate, pass unmatched events to the caller's handler, and consume interleaved responses and RPC messages. Represent completion with a value and all continuing cases with None. Propagate callback errors after consuming the current valid element. Document callback non-reentrancy and that the receive timeout starts after sending, excluding mutex acquisition and command-queue space waits. Signed-off-by: Zhi Wang <[email protected]> Co-developed-by: Alok Kumar <[email protected]> Signed-off-by: Alok Kumar <[email protected]> --- Documentation/gpu/nova/core/interrupts.rst | 9 +-- drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 65 ++++++++++++++++++++++ drivers/gpu/nova-core/gsp/fw.rs | 9 +++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/Documentation/gpu/nova/core/interrupts.rst b/Documentation/gpu/nova/core/interrupts.rst index 7f61e5d6334e..d686d8004aea 100644 --- a/Documentation/gpu/nova/core/interrupts.rst +++ b/Documentation/gpu/nova/core/interrupts.rst @@ -582,7 +582,8 @@ command reply or an unsolicited event, and the two differ in the function code. arrival with its sequence number, function code, and length. * A GMC message carries a command id in place of a function code. The ``GSP_INIT`` wait and synchronous GMC transactions claim responses with the - expected command id and sequence number. These waits consume interleaved RPC + expected command id and sequence number. A GMC completion-event wait uses its + caller's predicate to select an event. These waits consume interleaved RPC messages as events. Unmatched GMC messages are handled by the wait's callback or logged and consumed; a queue drain with no waiting caller logs unclaimed responses and unknown commands at warning level and drops them. @@ -607,9 +608,9 @@ mutex. Replies and events share one queue and one read pointer, so one lock is held across the whole drain. A thread waiting for a reply logs each event that arrives before the reply and keeps waiting. One receive deadline applies to all messages in the wait. The default timeout is 5 seconds; GMC transactions -can specify another timeout. For send-and-wait operations the receive deadline -starts after sending, so it does not bound waiting for the mutex or for -command-queue space. The thread holds the mutex from sending through receiving, +and completion-event waits can specify another timeout. For send-and-wait +operations the receive deadline starts after sending, so it does not bound +waiting for the mutex or for command-queue space. The thread holds the mutex from sending through receiving, so no other caller consumes the message it waits for. With one lock, a drain waits for an in-flight command's receive to finish or diff --git a/drivers/gpu/nova-core/gsp/cmdq/gmc.rs b/drivers/gpu/nova-core/gsp/cmdq/gmc.rs index 392143e534f6..c5ae5943c8b8 100644 --- a/drivers/gpu/nova-core/gsp/cmdq/gmc.rs +++ b/drivers/gpu/nova-core/gsp/cmdq/gmc.rs @@ -448,6 +448,71 @@ pub(crate) fn send_gmc_and_receive_timeout( }) } + /// Sends an asynchronous GMC command and waits atomically for its event. + /// + /// The command queue remains locked from the send through the matching + /// event, preventing another transaction from consuming its completion. + /// GMC events are passed to `predicate`, then to `handler` if the predicate returns false. + /// GMC responses are debug-logged and consumed without invoking either callback. Interleaved + /// RPC messages are logged as events and consumed. + /// + /// One receive deadline starts after sending and is not extended by other messages. It does + /// not bound waiting for the mutex or for space to send the request. + /// + /// Both callbacks run with the queue locked and must not reenter this queue or reset it. + /// Their second argument is the raw `max_resp_or_status` word of the event header. + /// + /// # Errors + /// + /// - `EMSGSIZE` if the request exceeds the command queue's maximum element size. + /// - `ETIMEDOUT` if space does not become available to send the request, or if no event + /// satisfies `predicate` before the receive deadline. + /// - `EIO` if the command queue slot cannot hold the request headers, the receive queue is + /// stalled by an invalid element, or a received element fails framing validation. + /// + /// Errors from initializing the request headers and from either callback are propagated + /// as-is. The current valid element is consumed before a callback error is returned. + #[expect(dead_code)] + pub(crate) fn send_gmc_and_wait_event( + &self, + command_id: u32, + payload: &[u8], + timeout: Delta, + mut predicate: impl FnMut(u32, u32, u64, &[u8], &[u8]) -> Result<bool>, + mut handler: impl FnMut(u32, u32, u64, &[u8], &[u8]) -> Result, + ) -> Result { + let mut inner = self.inner.lock(); + inner.send_command_element(RawGmcCommand { + command_id, + payload, + max_response_size: 0, + })?; + let dev = inner.dev; + let deadline = Instant::<Monotonic>::now() + timeout; + + inner.await_msg_with_deadline(deadline, |message| { + let GspParsedMessage::Gmc(message) = message else { + message.log(dev); + return Ok(None); + }; + let header = message.header(); + let (payload_0, payload_1) = message.payload(); + if header.command().is_response() { + return Ok(None); + } + + let command = GmcCommand::from(header.command()).0; + let max_resp_or_status = header.raw_status_word(); + let sequence = header.sequence(); + if predicate(command, max_resp_or_status, sequence, payload_0, payload_1)? { + return Ok(Some(())); + } + + handler(command, max_resp_or_status, sequence, payload_0, payload_1)?; + Ok(None) + }) + } + /// Sends `command` to the GSP and waits for the reply. /// /// GMC events that arrive before the reply are passed to `on_event`, and RPC messages are diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs index c75488e7a787..ce4a7c45caf2 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -905,6 +905,15 @@ pub(crate) fn payload_size(&self) -> usize { num::u32_as_usize(self.inner.size) } + /// Returns the raw `max_resp_or_status` word carried by a GMC header. + /// + /// Its meaning for an event is defined by that event's command. + pub(crate) fn raw_status_word(&self) -> u32 { + // SAFETY: Both union views contain a `u32` at the same offset, so every bit pattern is + // valid regardless of whether the header represents a request, response, or event. + unsafe { self.inner.__bindgen_anon_1.request.max_response_size } + } + /// Returns the `NV_STATUS` carried by a response to a command. /// /// Returns `Some` with the `NV_STATUS` code if the `response` flag of [`Self::command`] is set; -- 2.53.0
