From: Alexandre Courbot <acourbot@nvidia.com>
To: John Hubbard <jhubbard@nvidia.com>,
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: Alistair Popple <apopple@nvidia.com>,
Timur Tabi <ttabi@nvidia.com>,
Eliot Courtney <ecourtney@nvidia.com>,
Zhi Wang <zhiw@nvidia.com>,
nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH v5 06/14] gpu: nova-core: gsp: cmdq: split the transport part of the send path
Date: Tue, 20 Oct 2026 19:08:27 +0900 [thread overview]
Message-ID: <20261020-cmdq-rpc-v5-6-dceeb8f360ec@nvidia.com> (raw)
In-Reply-To: <20261020-cmdq-rpc-v5-0-dceeb8f360ec@nvidia.com>
Introduce the `CommandElement` trait and `RpcCommandElement` wrapper
type to define how messages (independently of their type) are sent
through the transport layer. `send_single_command` becomes
`send_command_element`, which allocates the queue slots, writes the
element header, and delegates the writing of the message itself to the
implementation of `CommandElement` before computing the checksum,
advancing the write pointer and ringing the doorbell.
The RPC part of `send_single_command` (writing the RPC header and the
command payload) is now part of the `CommandElement` implementation.
This sets things up for moving the RPC code into its own sub-module,
leaving the transport agnostic of the message type.
No functional change intended.
Suggested-by: Eliot Courtney <ecourtney@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 133 +++++++++++++++++++++++---------------
1 file changed, 80 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 88d81a4e37ef..ef59f541f7e6 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -39,10 +39,7 @@
},
};
-use continuation::{
- ContinuationRecord,
- SplitState, //
-};
+use continuation::SplitState;
use pin_init::pin_init_scope;
@@ -71,6 +68,19 @@
/// reply type are sent using [`Cmdq::send_command_no_wait`].
pub(crate) struct NoReply;
+/// Trait implemented by types that can be sent as a single command queue element.
+///
+/// The command queue allocates `size()` bytes after the `GspMsgElement` header and calls `write()`
+/// to fill them.
+trait CommandElement {
+ /// Size in bytes of the element, not including the `GspMsgElement` header.
+ fn size(&self) -> usize;
+
+ /// Writes the contents of the command into `dst`. `dev` is the queue's device (to be used for
+ /// logging), `seq` is the sequence number of the element.
+ fn write(&self, dev: &device::Device, seq: u32, dst: &mut GspCommand<'_>) -> Result;
+}
+
/// Trait implemented by types representing a command to send to the GSP.
///
/// The main purpose of this trait is to provide [`Cmdq`] with the information it needs to send
@@ -133,6 +143,62 @@ fn size(&self) -> usize {
}
}
+/// Wrapper type for sending a RPC command as a command queue element.
+///
+/// [`CommandElement`] cannot be directly implemented for all [`CommandToGsp`] with a blanket
+/// implementation as it would conflict with other future command types.
+struct RpcCommandElement<M>(M);
+
+impl<M> CommandElement for RpcCommandElement<M>
+where
+ M: CommandToGsp,
+ Error: From<M::InitError>,
+{
+ fn size(&self) -> usize {
+ self.0.size()
+ }
+
+ fn write(&self, dev: &device::Device, seq: u32, dst: &mut GspCommand<'_>) -> Result {
+ let command = &self.0;
+ let size_in_bytes = command.size();
+ // Extract area for the command itself. The GSP message 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 (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
+ let rpc_header_init = RpcMessageHeader::init(size_in_bytes, M::FUNCTION);
+ // SAFETY: `dst.header.rpc_header_mut()` is a valid reference, and is not touched if the
+ // initializer fails.
+ unsafe {
+ pin_init::raw_try_init(
+ core::ptr::from_mut(dst.header.rpc_header_mut()),
+ rpc_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 RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
+ seq,
+ M::FUNCTION,
+ size_in_bytes,
+ );
+
+ Ok(())
+ }
+}
+
/// Trait representing messages received from the GSP.
///
/// A reply that [`Cmdq::send_command`] waits for, or an event that [`Cmdq::await_msg`] waits for.
@@ -662,24 +728,18 @@ impl CmdqInner<'_> {
/// Timeout for waiting for space on the command queue.
const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1);
- /// Sends `command` to the GSP, without splitting it.
+ /// Allocate enough space on the command queue to store `command`, initialize it with
+ /// [`CommandElement::write`], and send the command to the GSP.
///
/// # Errors
///
/// - `EMSGSIZE` if the command exceeds the maximum queue element size.
/// - `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 initializers are propagated as-is.
- fn send_single_command<M>(&mut self, command: M) -> Result
- where
- M: CommandToGsp,
- // This allows all error types, including `Infallible`, to be used for `M::InitError`.
- Error: From<M::InitError>,
- {
+ /// Error codes returned by [`CommandElement::write`] are returned as-is.
+ fn send_command_element<E: CommandElement>(&mut self, command: E) -> Result {
let size_in_bytes = command.size();
- let dst = self
+ let mut dst = self
.gsp_mem
.allocate_command(size_in_bytes, Self::ALLOCATE_TIMEOUT)?;
@@ -690,40 +750,8 @@ fn send_single_command<M>(&mut self, command: M) -> Result
pin_init::raw_try_init(core::ptr::from_mut(dst.header), msg_element_init)?;
}
- // Extract area for the command itself. The GSP message 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 (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
- let rpc_header_init = RpcMessageHeader::init(size_in_bytes, M::FUNCTION);
- // SAFETY: `dst.header.rpc_header_mut()` is a valid reference, and is not touched if the
- // initializer fails.
- unsafe {
- pin_init::raw_try_init(
- core::ptr::from_mut(dst.header.rpc_header_mut()),
- rpc_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);
- }
- drop(sbuffer);
-
- dev_dbg!(
- &self.dev,
- "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n",
- self.seq,
- M::FUNCTION,
- size_in_bytes,
- );
+ // Write the message payload.
+ command.write(self.dev, self.seq, &mut dst)?;
// Compute checksum now that the whole message is ready.
dst.header
@@ -759,13 +787,12 @@ fn send_command<M>(&mut self, command: M) -> Result
Error: From<M::InitError>,
{
match SplitState::new(command)? {
- SplitState::Single(command) => self.send_single_command(command),
+ SplitState::Single(command) => self.send_command_element(RpcCommandElement(command)),
SplitState::Split(command, mut continuations) => {
- self.send_single_command(command)?;
+ self.send_command_element(RpcCommandElement(command))?;
while let Some(continuation) = continuations.next() {
- // Turbofish needed because the compiler cannot infer M here.
- self.send_single_command::<ContinuationRecord<'_>>(continuation)?;
+ self.send_command_element(RpcCommandElement(continuation))?;
}
Ok(())
--
2.56.0
next prev parent reply other threads:[~2026-10-20 10:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 10:08 [PATCH v5 00/14] gpu: nova-core: gsp: prepare the command queue for r000 dual-message types Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 01/14] gpu: nova-core: gsp: cmdq: remove stale comment Alexandre Courbot
2026-10-20 11:43 ` Eliot Courtney
2026-10-20 10:08 ` [PATCH v5 02/14] gpu: nova-core: gsp: cmdq: simplify framing error description Alexandre Courbot
2026-10-20 11:43 ` Eliot Courtney
2026-10-20 10:08 ` [PATCH v5 03/14] gpu: nova-core: gsp: cmdq: validate checksum earlier on receive Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 04/14] gpu: nova-core: gsp: introduce and use proper RpcMessageHeader type Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 05/14] gpu: nova-core: gsp: cmdq: group the RPC-specific part of send_single_command Alexandre Courbot
2026-10-20 10:08 ` Alexandre Courbot [this message]
2026-10-20 10:08 ` [PATCH v5 07/14] gpu: nova-core: gsp: cmdq: split RPC parsing part of the receive path Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 08/14] gpu: nova-core: gsp: cmdq: move RPC message logging to RpcMessage Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 09/14] gpu: nova-core: gsp: cmdq: split the transport part of the receive path Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 10/14] gpu: nova-core: gsp: cmdq: extract await_msg_with from await_msg Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 11/14] gpu: nova-core: gsp: cmdq: use consume_element to implement drain Alexandre Courbot
2026-10-20 10:18 ` sashiko-bot
2026-10-20 14:27 ` Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 12/14] gpu: nova-core: gsp: cmdq: move the RPC code into a sub-module Alexandre Courbot
2026-10-20 10:15 ` sashiko-bot
2026-10-20 10:08 ` [PATCH v5 13/14] gpu: nova-core: gsp: move the RPC commands " Alexandre Courbot
2026-10-20 10:08 ` [PATCH v5 14/14] gpu: nova-core: gsp: add `rpc` to RPC message send/receive methods Alexandre Courbot
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=20261020-cmdq-rpc-v5-6-dceeb8f360ec@nvidia.com \
--to=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=apopple@nvidia.com \
--cc=dakr@kernel.org \
--cc=dri-devel@lists.freedesktop.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.