From: Alexandre Courbot <acourbot@nvidia.com>
To: Danilo Krummrich <dakr@kernel.org>,
Alice Ryhl <aliceryhl@google.com>,
David Airlie <airlied@gmail.com>,
Simona Vetter <simona@ffwll.ch>,
Benno Lossin <lossin@kernel.org>, Gary Guo <gary@garyguo.net>
Cc: John Hubbard <jhubbard@nvidia.com>,
Alistair Popple <apopple@nvidia.com>,
Timur Tabi <ttabi@nvidia.com>,
Eliot Courtney <ecourtney@nvidia.com>,
Zhi Wang <zhiw@nvidia.com>,
linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev,
rust-for-linux@vger.kernel.org,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH v2 3/6] gpu: nova-core: gsp: cmdq: add the GMC send path
Date: Fri, 06 Nov 2026 16:06:11 +0900 [thread overview]
Message-ID: <20261106-r000_rebase_on_cmdq-rpc-v2-3-e481ea2168ef@nvidia.com> (raw)
In-Reply-To: <20261106-r000_rebase_on_cmdq-rpc-v2-0-e481ea2168ef@nvidia.com>
From: John Hubbard <jhubbard@nvidia.com>
The r000 boot protocol has the driver send GSP_INIT, the request that
carries the driver's configuration to GSP-RM, as a GMC (GPU Management
Controller) API command. GMC and RPC commands share the command queue
and its sequence counter. A GMC command always fits in one element, as
the GMC API has no continuation records.
Add the gmc sub-module of the command queue, sibling of rpc, with trait
and type definitions mirroring those of rpc to support sending GMC
messages.
The module has no user yet, and is built as dead code until the switch
to r000.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 4 +
drivers/gpu/nova-core/gsp/cmdq/gmc.rs | 202 ++++++++++++++++++++++++++++++++++
2 files changed, 206 insertions(+)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 76f5dbf2561b..9dd11ac8952b 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// The transport header of the r570 firmware carries no message type, so nothing can produce or
+// consume a GMC message before the switch to r000. Until then the module is built as dead code.
+#[expect(dead_code)]
+pub(super) mod gmc;
pub(super) mod rpc;
use core::mem;
diff --git a/drivers/gpu/nova-core/gsp/cmdq/gmc.rs b/drivers/gpu/nova-core/gsp/cmdq/gmc.rs
new file mode 100644
index 000000000000..f741de8288c5
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/cmdq/gmc.rs
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Support for the GMC (GPU Management Controller) API message type on the GSP command queue.
+
+use kernel::{
+ device,
+ prelude::*,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ gsp::{
+ cmdq::{
+ Cmdq,
+ CmdqInner,
+ CommandElement,
+ GspCommand,
+ NoReply, //
+ },
+ fw::{
+ GmcApiHeader,
+ GmcCommandId,
+ GspMsgElement, //
+ },
+ GSP_PAGE_SIZE,
+ },
+ sbuffer::SBufferIter,
+};
+
+/// Trait implemented by types representing a GMC command to send to the GSP.
+///
+/// The main purpose of this trait is to provide [`Cmdq`] with the information it needs to send
+/// a given command.
+///
+/// [`CommandToGsp::init`] in particular is responsible for initializing the command directly
+/// into the space reserved for it in the command queue buffer.
+///
+/// Some commands may be followed by a variable-length payload. For these, the
+/// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be
+/// defined as well.
+pub(crate) trait CommandToGsp {
+ /// Id identifying this command to the GSP.
+ const COMMAND: GmcCommandId;
+
+ /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer right
+ /// after the GMC API header.
+ type Command: FromBytes + AsBytes;
+
+ /// Type of the reply expected from the GSP, or [`NoReply`] for commands that don't have a
+ /// reply.
+ type Reply;
+
+ /// Largest reply this command accepts, in bytes. Zero for commands that the GSP does not
+ /// answer, or for commands that expect a reply with an empty payload (i.e. just a status
+ /// value).
+ const MAX_REPLY_SIZE: u32;
+
+ /// Error type returned by [`CommandToGsp::init`].
+ type InitError;
+
+ /// In-place command initializer responsible for filling the command in the command queue
+ /// buffer.
+ fn init(&self) -> impl Init<Self::Command, Self::InitError>;
+
+ /// Size of the variable-length payload following the command structure generated by
+ /// [`CommandToGsp::init`].
+ ///
+ /// Most commands don't have a variable-length payload, so this is zero by default.
+ fn variable_payload_len(&self) -> usize {
+ 0
+ }
+
+ /// Method initializing the variable-length payload.
+ ///
+ /// The command buffer is circular, which means that we may need to jump back to its beginning
+ /// while in the middle of a command. For this reason, the variable-length payload is
+ /// initialized using a [`SBufferIter`].
+ ///
+ /// This method will receive a buffer of the length returned by
+ /// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving
+ /// unwritten space will lead to an error.
+ ///
+ /// Most commands don't have a variable-length payload, so this does nothing by default.
+ fn init_variable_payload(
+ &self,
+ _dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
+ ) -> Result {
+ Ok(())
+ }
+
+ /// Total size of the command (including its variable-length payload) without the GMC API
+ /// header, i.e. the payload size advertised by that header.
+ fn size(&self) -> usize {
+ size_of::<Self::Command>() + self.variable_payload_len()
+ }
+}
+
+/// Wrapper type for sending a GMC command as a command queue element.
+struct GmcCommandElement<C>(C);
+
+impl<C> CommandElement for GmcCommandElement<C>
+where
+ C: CommandToGsp,
+ Error: From<C::InitError>,
+{
+ fn size(&self) -> usize {
+ size_of::<GmcApiHeader>() + self.0.size()
+ }
+
+ fn write(&self, dev: &device::Device, seq: u32, dst: &mut GspCommand<'_>) -> Result {
+ // Ensure that the command and its headers are not larger than one page, which is part of
+ // the GMC API contract.
+ const_assert!(
+ size_of::<GspMsgElement>() + size_of::<GmcApiHeader>() + size_of::<C::Command>()
+ <= GSP_PAGE_SIZE
+ );
+
+ let command = &self.0;
+ let payload_size = command.size();
+ // Extract the areas for the GMC API header and the command itself. Per the `const_assert!`
+ // above, the GSP message header, the GMC API header and the command header together are
+ // guaranteed to fit entirely into a single page, so it's ok to only look at
+ // `dst.contents.0` here.
+ let (header, contents) = GmcApiHeader::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
+ let (cmd, payload_1) = C::Command::from_bytes_mut_prefix(contents).ok_or(EIO)?;
+ let header_init =
+ GmcApiHeader::init(C::COMMAND, u64::from(seq), payload_size, C::MAX_REPLY_SIZE);
+ // SAFETY: `header` is a valid reference, and is not touched if the initializer fails.
+ unsafe {
+ pin_init::raw_try_init(core::ptr::from_mut(header), header_init)?;
+ }
+ // SAFETY: `cmd` is a valid reference, and is not touched if the initializer fails.
+ unsafe {
+ pin_init::raw_try_init(core::ptr::from_mut(cmd), command.init())?;
+ }
+
+ // Fill the variable-length payload, which may be empty.
+ let mut sbuffer = SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]);
+ command.init_variable_payload(&mut sbuffer)?;
+
+ if !sbuffer.is_empty() {
+ return Err(EIO);
+ }
+
+ dev_dbg!(
+ dev,
+ "GSP GMC: send: seq# {}, command={:?}, length=0x{:x}\n",
+ seq,
+ C::COMMAND,
+ payload_size,
+ );
+
+ Ok(())
+ }
+}
+
+impl Cmdq<'_> {
+ /// Sends `command` to the GSP without waiting for a reply.
+ ///
+ /// # Errors
+ ///
+ /// - `EMSGSIZE` if the command does not fit in a single command queue element.
+ /// - `ETIMEDOUT` if space does not become available within the timeout.
+ /// - `EIO` if the variable payload requested by the command has not been entirely
+ /// written to by its [`CommandToGsp::init_variable_payload`] method.
+ ///
+ /// Error codes returned by the command initializer are propagated as-is.
+ pub(crate) fn send_gmc_command_no_wait<C>(&self, command: C) -> Result
+ where
+ C: CommandToGsp<Reply = NoReply>,
+ Error: From<C::InitError>,
+ {
+ self.inner.lock().send_gmc_command(command).map(|_| ())
+ }
+}
+
+impl CmdqInner<'_> {
+ /// Sends `command` to the GSP, and returns the sequence number of its element.
+ ///
+ /// Unlike RPC commands, GMC commands are never split: a command that does not fit in a single
+ /// element is an error.
+ ///
+ /// # Errors
+ ///
+ /// - `EMSGSIZE` if the command does not fit in a single command queue element.
+ /// - `ETIMEDOUT` if space does not become available within the timeout.
+ /// - `EIO` if the variable payload requested by the command has not been entirely
+ /// written to by its [`CommandToGsp::init_variable_payload`] method.
+ ///
+ /// Error codes returned by the command initializer are propagated as-is.
+ fn send_gmc_command<C>(&mut self, command: C) -> Result<u32>
+ where
+ C: CommandToGsp,
+ Error: From<C::InitError>,
+ {
+ self.send_command_element(GmcCommandElement(command))
+ }
+}
--
2.56.0
next prev parent reply other threads:[~2026-11-06 7:06 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 7:06 [PATCH v2 0/6] gpu: nova-core: gsp: add support for GMC messages Alexandre Courbot
2026-11-06 7:06 ` [PATCH v2 1/6] gpu: nova-core: gsp: fw: add the r000 bindings for the GMC API header Alexandre Courbot
2026-11-06 7:06 ` [PATCH v2 2/6] gpu: nova-core: gsp: add " Alexandre Courbot
2026-11-06 7:06 ` Alexandre Courbot [this message]
2026-11-06 7:06 ` [PATCH v2 4/6] gpu: nova-core: gsp: cmdq: move message trimming into its own function Alexandre Courbot
2026-11-06 9:08 ` Eliot Courtney
2026-11-06 7:06 ` [PATCH v2 5/6] gpu: nova-core: gsp: cmdq: do not use ENOMSG to skip messages Alexandre Courbot
2026-11-06 9:09 ` Eliot Courtney
2026-11-06 7:06 ` [PATCH v2 6/6] gpu: nova-core: gsp: cmdq: add the GMC receive path Alexandre Courbot
2026-11-06 9:38 ` Eliot Courtney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106-r000_rebase_on_cmdq-rpc-v2-3-e481ea2168ef@nvidia.com \
--to=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=apopple@nvidia.com \
--cc=dakr@kernel.org \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=jhubbard@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nova-gpu@lists.linux.dev \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=ttabi@nvidia.com \
--cc=zhiw@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®