Re: [PATCH 06/27] gpu: nova-core: distinguish async GSP RPC traffic in debug logs

Zhi Wang <[email protected]>
Newsgroups dev.linux.lists.nova-gpu,org.kernel.vger.linux-kernel
Message-ID <20260820132605.60078897@inno-dell>
On Tue, 18 Aug 2026 20:51:59 -0700
John Hubbard <[email protected]> wrote:

Looks good to me with one nit: should we use wrapping_add(1) for
tx_async_seq/rx_event_seq?

Reviewed-by: Zhi Wang <[email protected]>

Z.

> The receive path logged every message at the transport layer and did
> not distinguish events from command replies. Async sends shared the
> RPC sequence with waited-for commands, and GSP-initiated events
> arrived with that field unset.
> 
> Print a distinct debug line for async send, sync send, event, and
> command reply. Number async sends and events with driver-local
> counters.
> 
> IS_ASYNC selects only the send log line. Every command still carries
> the next RPC sequence on the wire.
> 
> Assisted-by: Cursor:claude-opus-5
> Signed-off-by: John Hubbard <[email protected]>
> ---
>  drivers/gpu/nova-core/gsp/cmdq.rs             | 111
> ++++++++++++++---- .../gpu/nova-core/gsp/cmdq/continuation.rs    |
> 1 + drivers/gpu/nova-core/gsp/commands.rs         |   2 +
>  drivers/gpu/nova-core/gsp/fw.rs               |  19 +++
>  4 files changed, 107 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs
> b/drivers/gpu/nova-core/gsp/cmdq.rs index ac3e6642031a..d3afe3f9bb37
> 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -87,6 +87,12 @@ pub(crate) trait CommandToGsp {
>      /// Function identifying this command to the GSP.
>      const FUNCTION: MsgFunction;
>  
> +    /// Classifies the send-side debug log as an async send.
> +    ///
> +    /// Default is `false`. Set this only on commands that are sent
> without waiting for a reply. A
> +    /// [`NoReply`] continuation of a waited-for command stays
> `false`.
> +    const IS_ASYNC: bool = false;
> +
>      /// Type generated by [`CommandToGsp::init`], to be written into
> the command queue buffer. type Command: FromBytes + AsBytes;
>  
> @@ -528,6 +534,8 @@ pub(crate) fn new(dev:
> &device::Device<device::Bound>) -> impl PinInit<Self, Err gsp_mem,
>                      elem_seq: 0,
>                      rpc_seq: 0,
> +                    tx_async_seq: 0,
> +                    rx_event_seq: 0,
>                      poisoned: Cell::new(false),
>                  }),
>              }))
> @@ -672,6 +680,12 @@ struct CmdqInner {
>      /// [`CmdqInner::receive_msg`] match that reply to the awaiting
> command. Advances once per /// logical command.
>      rpc_seq: u32,
> +    /// Debug-log sequence for async sends. Those commands do not
> wait for a reply, so this
> +    /// counter is the number printed in the send log.
> +    tx_async_seq: u32,
> +    /// Debug-log sequence for GSP-initiated events. The GSP leaves
> the RPC sequence unset on
> +    /// those messages, so the driver numbers them itself.
> +    rx_event_seq: u32,
>      /// Set once a message with corrupt framing or a bad checksum is
> seen. Such a message has an /// untrusted length, so the queue cannot
> be advanced past it, and every later receive fails /// until the
> queue is torn down and reset. @@ -738,13 +752,24 @@ fn
> send_single_command<M>(&mut self, bar: Bar0<'_>, command: M, rpc_seq:
> u32) -> dst.contents.1, ])));
>  
> -        dev_dbg!(
> -            &self.dev,
> -            "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
> -            rpc_seq,
> -            M::FUNCTION,
> -            dst.header.length(),
> -        );
> +        if M::IS_ASYNC {
> +            dev_dbg!(
> +                &self.dev,
> +                "GSP RPC: async send: seq# {}, function={:?},
> length=0x{:x}\n",
> +                self.tx_async_seq,
> +                M::FUNCTION,
> +                dst.header.length(),
> +            );
> +            self.tx_async_seq += 1;
> +        } else {
> +            dev_dbg!(
> +                &self.dev,
> +                "GSP RPC: send: seq# {}, function={:?},
> length=0x{:x}\n",
> +                rpc_seq,
> +                M::FUNCTION,
> +                dst.header.length(),
> +            );
> +        }
>  
>          // All set - update the write pointer and inform the GSP of
> the new command. let elem_count = dst.header.element_count();
> @@ -826,14 +851,6 @@ fn wait_for_msg(&self, timeout: Delta) ->
> Result<GspMessage<'_>> { return Err(EIO);
>          };
>  
> -        dev_dbg!(
> -            &self.dev,
> -            "GSP RPC: receive: seq# {}, function={:?},
> length=0x{:x}\n",
> -            header.sequence(),
> -            header.function(),
> -            header.length(),
> -        );
> -
>          let payload_length = header.payload_length();
>  
>          // Check that the driver read area is large enough for the
> message. @@ -877,6 +894,41 @@ fn wait_for_msg(&self, timeout: Delta)
> -> Result<GspMessage<'_>> { })
>      }
>  
> +    /// Writes the classified receive debug line for a decoded GSP
> message.
> +    ///
> +    /// Events print [`Self::rx_event_seq`]. Command replies print
> `seq`. An unrecognized function
> +    /// code prints nothing here.
> +    fn log_received(&self, function: Result<MsgFunction, u32>, seq:
> u32, length: usize) {
> +        match function {
> +            Ok(f) if f.is_event() => {
> +                dev_dbg!(
> +                    &self.dev,
> +                    "GSP RPC: async received: seq# {},
> function={:?}, length=0x{:x}\n",
> +                    self.rx_event_seq,
> +                    f,
> +                    length,
> +                );
> +            }
> +            Ok(f) => {
> +                dev_dbg!(
> +                    &self.dev,
> +                    "GSP RPC: response received: seq# {},
> function={:?}, length=0x{:x}\n",
> +                    seq,
> +                    f,
> +                    length,
> +                );
> +            }
> +            Err(_) => {}
> +        }
> +    }
> +
> +    /// Advances [`Self::rx_event_seq`] after a GSP-initiated event
> has been consumed.
> +    fn advance_rx_event_seq(&mut self, function: Result<MsgFunction,
> u32>) {
> +        if matches!(function, Ok(f) if f.is_event()) {
> +            self.rx_event_seq += 1;
> +        }
> +    }
> +
>      /// Receive a message from the GSP.
>      ///
>      /// The expected message type is given by the `M` generic
> parameter. With `expected_seq` set, @@ -910,9 +962,12 @@ fn
> receive_msg<M: MessageFromGsp>( let message =
> self.wait_for_msg(timeout)?; let function = message.header.function();
>          let seq = message.header.sequence();
> +        let length = message.header.length();
>          let func_matches = matches!(function, Ok(f) if f ==
> M::FUNCTION); let matched = func_matches &&
> expected_seq.is_none_or(|expected| seq == expected); 
> +        self.log_received(function, seq, length);
> +
>          // Every path must advance the read pointer past this
> message, including a failed decode. let result = if matched {
>              match M::Message::from_bytes_prefix(message.contents.0) {
> @@ -938,9 +993,10 @@ fn receive_msg<M: MessageFromGsp>(
>          };
>  
>          // Advance the read pointer past this message.
> -        self.gsp_mem.advance_cpu_read_ptr(u32::try_from(
> -            message.header.length().div_ceil(GSP_PAGE_SIZE),
> -        )?);
> +        self.gsp_mem
> +
> .advance_cpu_read_ptr(u32::try_from(length.div_ceil(GSP_PAGE_SIZE))?);
> +
> +        self.advance_rx_event_seq(function);
>  
>          if !matched {
>              if func_matches {
> @@ -962,8 +1018,8 @@ fn receive_msg<M: MessageFromGsp>(
>      /// Routes a GSP message that is not the reply a caller is
> waiting for. ///
>      /// GSP-reported errors are logged at error level and
> unrecognized function codes at warning
> -    /// level. Every other known function code is consumed without a
> log line, because the RPC
> -    /// receive trace in [`Self::wait_for_msg`] already records its
> arrival.
> +    /// level. Every other known function code is consumed without a
> log line, because
> +    /// [`Self::log_received`] already records its arrival.
>      fn dispatch_event(&self, function: Result<MsgFunction, u32>,
> seq: u32) { match function {
>              Ok(MsgFunction::OsErrorLog) => {
> @@ -1004,16 +1060,19 @@ 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();
> +            let length = msg.header.length();
> +
> +            self.log_received(function, seq, length);
> +
> +            let pages =
> u32::try_from(length.div_ceil(GSP_PAGE_SIZE)).map_err(|_| {
> +                dev_err!(&self.dev, "GSP drain: message length
> overflow\n");
> +                EIO
> +            })?;
>  
>              self.gsp_mem.advance_cpu_read_ptr(pages);
> +            self.advance_rx_event_seq(function);
>              self.dispatch_event(function, seq);
>          }
>  
> diff --git a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
> b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs index
> 05e904f18097..a606c7be52a7 100644 ---
> a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs +++
> b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs @@ -147,6 +147,7 @@
> fn new(command: C, payload: KVVec<u8>) -> Self { 
>  impl<C: CommandToGsp> CommandToGsp for SplitCommand<C> {
>      const FUNCTION: MsgFunction = C::FUNCTION;
> +    const IS_ASYNC: bool = C::IS_ASYNC;
>      type Command = C::Command;
>      type Reply = C::Reply;
>      type InitError = C::InitError;
> diff --git a/drivers/gpu/nova-core/gsp/commands.rs
> b/drivers/gpu/nova-core/gsp/commands.rs index
> 61fe93db9e7e..d5575c036eb9 100644 ---
> a/drivers/gpu/nova-core/gsp/commands.rs +++
> b/drivers/gpu/nova-core/gsp/commands.rs @@ -52,6 +52,7 @@ pub(crate)
> fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Sel 
>  impl<'a> CommandToGsp for SetSystemInfo<'a> {
>      const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
> +    const IS_ASYNC: bool = true;
>      type Command = fw::commands::GspSetSystemInfo;
>      type Reply = NoReply;
>      type InitError = Error;
> @@ -122,6 +123,7 @@ pub(crate) fn new(vgpu_state: VgpuState) ->
> Result<Self> { 
>  impl CommandToGsp for SetRegistry {
>      const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
> +    const IS_ASYNC: bool = true;
>      type Command = fw::commands::PackedRegistryTable;
>      type Reply = NoReply;
>      type InitError = Infallible;
> diff --git a/drivers/gpu/nova-core/gsp/fw.rs
> b/drivers/gpu/nova-core/gsp/fw.rs index d3678f16750a..857d53c77297
> 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs
> +++ b/drivers/gpu/nova-core/gsp/fw.rs
> @@ -359,6 +359,25 @@ fn try_from(value: u32) -> Result<MsgFunction> {
>      }
>  }
>  
> +impl MsgFunction {
> +    /// Returns true if this is a GSP-initiated async event
> (`NV_VGPU_MSG_EVENT_*`), as opposed to
> +    /// a command response (`NV_VGPU_MSG_FUNCTION_*`).
> +    pub(crate) fn is_event(&self) -> bool {
> +        matches!(
> +            self,
> +            Self::GspInitDone
> +                | Self::GspRunCpuSequencer
> +                | Self::PostEvent
> +                | Self::RcTriggered
> +                | Self::MmuFaultQueued
> +                | Self::OsErrorLog
> +                | Self::GspPostNoCat
> +                | Self::GspLockdownNotice
> +                | Self::UcodeLibOsPrint //
> +        )
> +    }
> +}
> +
>  impl From<MsgFunction> for u32 {
>      fn from(value: MsgFunction) -> Self {
>          // CAST: `MsgFunction` is `repr(u32)` and can thus be cast
> losslessly.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.