* [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq
@ 2026-02-26 14:50 Eliot Courtney
2026-02-26 14:50 ` [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Eliot Courtney @ 2026-02-26 14:50 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
Simona Vetter, Benno Lossin, Gary Guo
Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney,
Zhi Wang
Add locking to Cmdq. This is required e.g. for unloading the driver,
which needs to send the UnloadingGuestDriver via the command queue
on unbind which may be on a different thread.
We have commands that need a reply ("sync") and commands that don't
("async"). For sync commands we want to make sure that they don't get
the reply of a different command back. The approach this patch series
takes is by making sync commands block until they get a response. For
now this should be ok, and we expect GSP to be fast anyway.
To do this, we need to know which commands expect a reply and which
don't. John's existing series[1] adds IS_ASYNC which solves part of the
problem, but we need to know a bit more. So instead, add an
associated type called Reply which tells us what the reply is.
An alternative would be to define traits inheriting CommandToGsp, e.g.
SyncCommand and AsyncCommand, instead of using the associated type. I
implemented the associated type version because it feels more
compositional rather than inherity so seemed a bit better to me. But
both of these approaches work and are fine, IMO.
In summary, this patch series has three steps:
1. Add the type infrastructure to know what replies are expected for a
command and update each caller to explicitly send a sync or async
command.
2. Make Cmdq pinned so we can use Mutex
3. Add a Mutex to protect Cmdq by moving the relevant state to an
inner struct.
[1]: https://lore.kernel.org/all/20260211000451.192109-1-jhubbard@nvidia.com/
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Changes in v2:
- Rebase on drm-rust-next
- Link to v1: https://lore.kernel.org/r/20260225-cmdq-locking-v1-0-bbf6b4156706@nvidia.com
---
Eliot Courtney (4):
gpu: nova-core: gsp: fix stale doc comments on command queue methods
gpu: nova-core: gsp: add sync and async command queue API to `Cmdq`
gpu: nova-core: gsp: make `Cmdq` a pinned type
gpu: nova-core: gsp: add mutex locking to Cmdq
drivers/gpu/nova-core/gsp.rs | 5 +-
drivers/gpu/nova-core/gsp/boot.rs | 13 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 246 +++++++++++++++++++++------------
drivers/gpu/nova-core/gsp/commands.rs | 23 ++-
drivers/gpu/nova-core/gsp/sequencer.rs | 2 +-
5 files changed, 183 insertions(+), 106 deletions(-)
---
base-commit: 4a49fe23e357b48845e31fe9c28a802c05458198
change-id: 20260225-cmdq-locking-d32928a2c2cf
prerequisite-message-id: <20260226-cmdq-continuation-v3-0-572ab9916766@nvidia.com>
prerequisite-patch-id: fd45bc5b8eda5e2b54a052dddb1a1c363107f0a7
prerequisite-patch-id: d0f59ef489346e978a222755f9fb42dfe7af19e5
prerequisite-patch-id: 8844970d0e387488c70979a73732579ba174b46c
prerequisite-patch-id: e138a94ed48fa8d50d5ed1eb36524f98923ed478
prerequisite-patch-id: 4599a5e90d6665fa3acb7d4045de5d378ac28b4d
prerequisite-patch-id: 30ed64c398e541d6efbcb2e46ed9a9e6cf953f4f
prerequisite-patch-id: 9a965e9f29c8680c0b554e656ff8e9a1bfc67280
prerequisite-patch-id: d8cccc3dfb070f304805fc7e0f24121809b4b300
prerequisite-patch-id: c0a73dfd1fb630ab02486f0180b90f8fe850b4dc
Best regards,
--
Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney @ 2026-02-26 14:50 ` Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney ` (4 subsequent siblings) 5 siblings, 1 reply; 12+ messages in thread From: Eliot Courtney @ 2026-02-26 14:50 UTC (permalink / raw) To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney, Zhi Wang Fix some inaccuracies / old doc comments. Reviewed-by: Zhi Wang <zhiw@nvidia.com> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> --- drivers/gpu/nova-core/gsp/cmdq.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index d68b93ddf7cc..a3e039117120 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -528,6 +528,7 @@ fn notify_gsp(bar: &Bar0) { /// /// # 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. @@ -710,22 +711,20 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { /// Receive a message from the GSP. /// - /// `init` is a closure tasked with processing the message. It receives a reference to the - /// message in the message queue, and a [`SBufferIter`] pointing to its variable-length - /// payload, if any. + /// The expected message type is specified using the `M` generic parameter. If the pending + /// message has a different function code, `ERANGE` is returned and the message is consumed. /// - /// The expected message is specified using the `M` generic parameter. If the pending message - /// is different, `EAGAIN` is returned and the unexpected message is dropped. - /// - /// This design is by no means final, but it is simple and will let us go through GSP - /// initialization. + /// The read pointer is always advanced past the message, regardless of whether it matched. /// /// # Errors /// /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available. /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the /// message queue. - /// - `EINVAL` if the function of the message was unrecognized. + /// - `EINVAL` if the function code of the message was not recognized. + /// - `ERANGE` if the message had a recognized but non-matching function code. + /// + /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is. pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M> where // This allows all error types, including `Infallible`, to be used for `M::InitError`. -- 2.53.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: fix stale doc comments on command queue methods 2026-02-26 14:50 ` [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney @ 2026-02-27 2:04 ` Claude Code Review Bot 0 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 2:04 UTC (permalink / raw) To: dri-devel-reviews Patch Review **Status: Looks good** A straightforward documentation cleanup. The changes are: 1. Adding missing `EMSGSIZE` error doc: ``` + /// - `EMSGSIZE` if the command exceeds the maximum queue element size. ``` This was presumably missing from the original docs — good to add. 2. Updating `receive_msg` docs to accurately describe current behavior: ``` - /// `init` is a closure tasked with processing the message... + /// The expected message type is specified using the `M` generic parameter. If the pending + /// message has a different function code, `ERANGE` is returned and the message is consumed. ``` The old docs referenced a closure-based API that no longer exists. The replacement accurately describes the current generic-parameter approach. 3. Updating error code documentation: ``` - /// - `EINVAL` if the function of the message was unrecognized. + /// - `EINVAL` if the function code of the message was not recognized. + /// - `ERANGE` if the message had a recognized but non-matching function code. ``` The `EAGAIN` → `ERANGE` distinction and the addition of `ERANGE` docs are correct. 4. Adding note about error propagation: ``` + /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is. ``` 5. Removing the "this design is by no means final" comment — appropriate for a maturing codebase. No issues. Already has Reviewed-by from Zhi Wang. --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney 2026-02-26 14:50 ` [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney @ 2026-02-26 14:50 ` Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 3/4] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney ` (3 subsequent siblings) 5 siblings, 1 reply; 12+ messages in thread From: Eliot Courtney @ 2026-02-26 14:50 UTC (permalink / raw) To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney Add sync and async command queue API and the type infrastructure to know what reply is expected from each `CommandToGsp`. Use a marker type `NoReply` which does not implement `MessageFromGsp` to mark async commands which don't expect a response. This prepares for adding locking to the queue. Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> --- drivers/gpu/nova-core/gsp/boot.rs | 5 ++-- drivers/gpu/nova-core/gsp/cmdq.rs | 54 ++++++++++++++++++++++++++++++++++- drivers/gpu/nova-core/gsp/commands.rs | 19 ++++++------ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index c56029f444cb..55899eba75db 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -160,8 +160,9 @@ pub(crate) fn boot( dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; self.cmdq - .send_command(bar, commands::SetSystemInfo::new(pdev))?; - self.cmdq.send_command(bar, commands::SetRegistry::new())?; + .send_async_command(bar, commands::SetSystemInfo::new(pdev))?; + self.cmdq + .send_async_command(bar, commands::SetRegistry::new())?; gsp_falcon.reset(bar)?; let libos_handle = self.libos.dma_handle(); diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index a3e039117120..daf3e1d153d4 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -48,6 +48,10 @@ sbuffer::SBufferIter, // }; +/// Marker type representing the absence of a reply for a command. This does not implement +/// `MessageFromGsp`. +pub(crate) struct NoReply; + /// Trait implemented by types representing a command to send to the GSP. /// /// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it @@ -66,6 +70,9 @@ pub(crate) trait CommandToGsp { /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer. type Command: FromBytes + AsBytes; + /// Type of the reply expected from the GSP, or [`NoReply`] for async commands. + type Reply; + /// Error type returned by [`CommandToGsp::init`]. type InitError; @@ -604,7 +611,7 @@ fn send_single_command<M>(&mut self, bar: &Bar0, command: M) -> Result /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result + fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp, Error: From<M::InitError>, @@ -626,6 +633,51 @@ pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result Ok(()) } + /// Sends `command` to the GSP and waits for the reply. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is + /// not received 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 and reply initializers are propagated as-is. + pub(crate) fn send_sync_command<M>(&mut self, bar: &Bar0, command: M) -> Result<M::Reply> + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + Error: From<M::InitError>, + Error: From<<M::Reply as MessageFromGsp>::InitError>, + { + self.send_command(bar, command)?; + + loop { + match self.receive_msg::<M::Reply>(Delta::from_secs(10)) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } + + /// Sends `command` to the GSP without waiting for a reply. + /// + /// # Errors + /// + /// - `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. + pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp<Reply = NoReply>, + Error: From<M::InitError>, + { + self.send_command(bar, command) + } + /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 74f875755e53..47ca31611927 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -26,7 +26,8 @@ command_size, Cmdq, CommandToGsp, - MessageFromGsp, // + MessageFromGsp, + NoReply, // }, fw::{ commands::*, @@ -53,6 +54,7 @@ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self { impl<'a> CommandToGsp for SetSystemInfo<'a> { const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo; type Command = GspSetSystemInfo; + type Reply = NoReply; type InitError = Error; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -104,6 +106,7 @@ pub(crate) fn new() -> Self { impl CommandToGsp for SetRegistry { const FUNCTION: MsgFunction = MsgFunction::SetRegistry; type Command = PackedRegistryTable; + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -183,6 +186,7 @@ pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { impl CommandToGsp for GetGspStaticInfo { const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; type Command = GspStaticConfigInfo; + type Reply = GetGspStaticInfoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -236,15 +240,7 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { /// Send the [`GetGspInfo`] command and awaits for its reply. pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> { - cmdq.send_command(bar, GetGspStaticInfo)?; - - loop { - match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) { - Ok(info) => return Ok(info), - Err(ERANGE) => continue, - Err(e) => return Err(e), - } - } + cmdq.send_sync_command(bar, GetGspStaticInfo) } /// The `ContinuationRecord` command. @@ -262,6 +258,7 @@ pub(crate) fn new(data: &'a [u8]) -> Self { impl<'a> CommandToGsp for ContinuationRecord<'a> { const FUNCTION: MsgFunction = MsgFunction::ContinuationRecord; type Command = (); + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -356,6 +353,7 @@ pub(crate) enum SplitCommand<'a, C: CommandToGsp> { impl<'a, C: CommandToGsp> CommandToGsp for SplitCommand<'a, C> { const FUNCTION: MsgFunction = C::FUNCTION; type Command = C::Command; + type Reply = C::Reply; type InitError = C::InitError; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -412,6 +410,7 @@ fn new(len: usize) -> Result<Self> { impl CommandToGsp for TestPayload { const FUNCTION: MsgFunction = MsgFunction::Nop; type Command = (); + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { -- 2.53.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` 2026-02-26 14:50 ` [PATCH v2 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney @ 2026-02-27 2:04 ` Claude Code Review Bot 0 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 2:04 UTC (permalink / raw) To: dri-devel-reviews Patch Review **Status: Looks good, minor observations** This patch adds the type infrastructure for distinguishing sync vs async commands. The `NoReply` marker type is simple and effective: ```rust +pub(crate) struct NoReply; ``` Not implementing `MessageFromGsp` means `send_sync_command` cannot accidentally be called with an async command — this is enforced at compile time via the `M::Reply: MessageFromGsp` bound. The `Reply` associated type on `CommandToGsp`: ```rust + /// Type of the reply expected from the GSP, or [`NoReply`] for async commands. + type Reply; ``` This is cleaner than a boolean `IS_ASYNC` constant since it carries the reply type information that `send_sync_command` needs. The `send_sync_command` implementation: ```rust + pub(crate) fn send_sync_command<M>(&mut self, bar: &Bar0, command: M) -> Result<M::Reply> + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + ... + { + self.send_command(bar, command)?; + loop { + match self.receive_msg::<M::Reply>(Delta::from_secs(10)) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } ``` **Observation:** The `ERANGE` loop retries with a fresh 10-second timeout each iteration. If the GSP keeps sending non-matching messages (e.g. asynchronous notifications), this could loop for a very long time. Currently this is likely fine since the cover letter says "we expect GSP to be fast," but a comment noting this assumption or an overall retry limit would be defensive. This is a pre-existing pattern from `get_gsp_info()` which this patch consolidates — so it's not a regression. The `send_async_command` properly constrains `Reply = NoReply`: ```rust + pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp<Reply = NoReply>, ``` Making `send_command` private (`fn` instead of `pub(crate) fn`) is correct — callers should now go through the sync/async variants. The migration of `get_gsp_info` from a manual send+receive loop to a single `send_sync_command` call is a nice cleanup: ```rust - cmdq.send_command(bar, GetGspStaticInfo)?; - loop { - match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) { - ... - } - } + cmdq.send_sync_command(bar, GetGspStaticInfo) ``` **Note:** The old code used a 5-second timeout; the new `send_sync_command` uses 10 seconds. This is a behavior change (doubled timeout) though likely intentional. The `SplitCommand` correctly propagates the `Reply` type: ```rust + type Reply = C::Reply; ``` All command types are properly annotated. This patch is solid. --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 3/4] gpu: nova-core: gsp: make `Cmdq` a pinned type 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney 2026-02-26 14:50 ` [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney 2026-02-26 14:50 ` [PATCH v2 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney @ 2026-02-26 14:50 ` Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 4/4] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney ` (2 subsequent siblings) 5 siblings, 1 reply; 12+ messages in thread From: Eliot Courtney @ 2026-02-26 14:50 UTC (permalink / raw) To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney, Zhi Wang Make `Cmdq` a pinned type. This is needed to use Mutex, which is needed to add locking to `Cmdq`. Reviewed-by: Zhi Wang <zhiw@nvidia.com> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> --- drivers/gpu/nova-core/gsp.rs | 5 +++-- drivers/gpu/nova-core/gsp/cmdq.rs | 9 ++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 174feaca0a6b..a6f3918c20b1 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -112,6 +112,7 @@ pub(crate) struct Gsp { /// RM log buffer. logrm: LogBuffer, /// Command queue. + #[pin] pub(crate) cmdq: Cmdq, /// RM arguments. rmargs: CoherentAllocation<GspArgumentsPadded>, @@ -132,7 +133,7 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error loginit: LogBuffer::new(dev)?, logintr: LogBuffer::new(dev)?, logrm: LogBuffer::new(dev)?, - cmdq: Cmdq::new(dev)?, + cmdq <- Cmdq::new(dev), rmargs: CoherentAllocation::<GspArgumentsPadded>::alloc_coherent( dev, 1, @@ -149,7 +150,7 @@ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0) )?; dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; - dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?; + dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(&cmdq))?; dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; }, })) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index daf3e1d153d4..6bb1decd2af5 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -474,6 +474,7 @@ pub(crate) fn command_size<M>(command: &M) -> usize /// /// Provides the ability to send commands and receive messages from the GSP using a shared memory /// area. +#[pin_data] pub(crate) struct Cmdq { /// Device this command queue belongs to. dev: ARef<device::Device>, @@ -501,13 +502,11 @@ impl Cmdq { pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT; /// Creates a new command queue for `dev`. - pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> { - let gsp_mem = DmaGspMem::new(dev)?; - - Ok(Cmdq { + pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { + try_pin_init!(Self { + gsp_mem: DmaGspMem::new(dev)?, dev: dev.into(), seq: 0, - gsp_mem, }) } -- 2.53.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: make `Cmdq` a pinned type 2026-02-26 14:50 ` [PATCH v2 3/4] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney @ 2026-02-27 2:04 ` Claude Code Review Bot 0 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 2:04 UTC (permalink / raw) To: dri-devel-reviews Patch Review **Status: Looks good** A clean mechanical change to make `Cmdq` pin-initialized, required for the `Mutex` in patch 4. Adding `#[pin_data]` and `#[pin]`: ```rust +#[pin_data] pub(crate) struct Cmdq { ... ``` Changing the constructor from `Result<Cmdq>` to `impl PinInit<Self, Error>`: ```rust - pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> { - let gsp_mem = DmaGspMem::new(dev)?; - Ok(Cmdq { + pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { + try_pin_init!(Self { + gsp_mem: DmaGspMem::new(dev)?, dev: dev.into(), seq: 0, - gsp_mem, }) ``` Updating the caller in `gsp.rs` to use pin-init syntax: ```rust - cmdq: Cmdq::new(dev)?, + cmdq <- Cmdq::new(dev), ``` And the reference change since `cmdq` is now behind a pin: ```rust - dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?; + dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(&cmdq))?; ``` No issues. Already has Reviewed-by from Zhi Wang. --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 4/4] gpu: nova-core: gsp: add mutex locking to Cmdq 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney ` (2 preceding siblings ...) 2026-02-26 14:50 ` [PATCH v2 3/4] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney @ 2026-02-26 14:50 ` Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 18:48 ` [PATCH v2 0/4] gpu: nova-core: gsp: add " Zhi Wang 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 5 siblings, 1 reply; 12+ messages in thread From: Eliot Courtney @ 2026-02-26 14:50 UTC (permalink / raw) To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney, Zhi Wang Wrap `Cmdq`'s mutable state in a new struct `CmdqInner` and wrap that in a Mutex. This lets `Cmdq` methods take &self instead of &mut self, which lets required commands be sent e.g. while unloading the driver. The mutex is held over both send and receive in `send_sync_command` to make sure that it doesn't get the reply of some other command that could have been sent just beforehand. Reviewed-by: Zhi Wang <zhiw@nvidia.com> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> --- drivers/gpu/nova-core/gsp/boot.rs | 8 +- drivers/gpu/nova-core/gsp/cmdq.rs | 266 ++++++++++++++++++--------------- drivers/gpu/nova-core/gsp/commands.rs | 4 +- drivers/gpu/nova-core/gsp/sequencer.rs | 2 +- 4 files changed, 153 insertions(+), 127 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 55899eba75db..d12ad1bd2cd8 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -128,7 +128,7 @@ fn run_fwsec_frts( /// /// Upon return, the GSP is up and running, and its runtime object given as return value. pub(crate) fn boot( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, pdev: &pci::Device<device::Bound>, bar: &Bar0, chipset: Chipset, @@ -214,13 +214,13 @@ pub(crate) fn boot( dev: pdev.as_ref().into(), bar, }; - GspSequencer::run(&mut self.cmdq, seq_params)?; + GspSequencer::run(&self.cmdq, seq_params)?; // Wait until GSP is fully initialized. - commands::wait_gsp_init_done(&mut self.cmdq)?; + commands::wait_gsp_init_done(&self.cmdq)?; // Obtain and display basic GPU information. - let info = commands::get_gsp_info(&mut self.cmdq, bar)?; + let info = commands::get_gsp_info(&self.cmdq, bar)?; match info.gpu_name() { Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 6bb1decd2af5..5010587c96f9 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -16,8 +16,12 @@ }, dma_write, io::poll::read_poll_timeout, + new_mutex, prelude::*, - sync::aref::ARef, + sync::{ + aref::ARef, + Mutex, // + }, time::Delta, transmute::{ AsBytes, @@ -54,8 +58,8 @@ /// Trait implemented by types representing a command to send to the GSP. /// -/// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it -/// needs to send a given command. +/// 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. @@ -470,66 +474,15 @@ pub(crate) fn command_size<M>(command: &M) -> usize size_of::<M::Command>() + command.variable_payload_len() } -/// GSP command queue. -/// -/// Provides the ability to send commands and receive messages from the GSP using a shared memory -/// area. -#[pin_data] -pub(crate) struct Cmdq { - /// Device this command queue belongs to. - dev: ARef<device::Device>, +/// Inner mutex protected state of [`Cmdq`]. +struct CmdqInner { /// Current command sequence number. seq: u32, /// Memory area shared with the GSP for communicating commands and messages. gsp_mem: DmaGspMem, } -impl Cmdq { - /// Offset of the data after the PTEs. - const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq); - - /// Offset of command queue ring buffer. - pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq) - + core::mem::offset_of!(Msgq, msgq) - - Self::POST_PTE_OFFSET; - - /// Offset of message queue ring buffer. - pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq) - + core::mem::offset_of!(Msgq, msgq) - - Self::POST_PTE_OFFSET; - - /// Number of page table entries for the GSP shared region. - pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT; - - /// Creates a new command queue for `dev`. - pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { - try_pin_init!(Self { - gsp_mem: DmaGspMem::new(dev)?, - dev: dev.into(), - seq: 0, - }) - } - - /// Computes the checksum for the message pointed to by `it`. - /// - /// A message is made of several parts, so `it` is an iterator over byte slices representing - /// these parts. - fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 { - let sum64 = it - .enumerate() - .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte)) - .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol)); - - ((sum64 >> 32) as u32) ^ (sum64 as u32) - } - - /// Notifies the GSP that we have updated the command queue pointers. - fn notify_gsp(bar: &Bar0) { - regs::NV_PGSP_QUEUE_HEAD::default() - .set_address(0) - .write(bar); - } - +impl CmdqInner { /// Sends `command` to the GSP, without splitting it. /// /// # Errors @@ -540,7 +493,7 @@ fn notify_gsp(bar: &Bar0) { /// 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, bar: &Bar0, command: M) -> Result + fn send_single_command<M>(&mut self, dev: &device::Device, bar: &Bar0, command: M) -> Result where M: CommandToGsp, // This allows all error types, including `Infallible`, to be used for `M::InitError`. @@ -583,7 +536,7 @@ fn send_single_command<M>(&mut self, bar: &Bar0, command: M) -> Result ]))); dev_dbg!( - &self.dev, + dev, "GSP RPC: send: seq# {}, function={:?}, length=0x{:x}\n", self.seq, M::FUNCTION, @@ -610,73 +563,27 @@ fn send_single_command<M>(&mut self, bar: &Bar0, command: M) -> Result /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result + fn send_command<M>(&mut self, dev: &device::Device, bar: &Bar0, command: M) -> Result where M: CommandToGsp, Error: From<M::InitError>, { let mut state = SplitState::new(&command)?; - - self.send_single_command(bar, state.command(command))?; + self.send_single_command(dev, bar, state.command(command))?; while let Some(continuation) = state.next_continuation_record() { dev_dbg!( - &self.dev, + dev, "GSP RPC: send continuation: size=0x{:x}\n", command_size(&continuation), ); // Turbofish needed because the compiler cannot infer M here. - self.send_single_command::<ContinuationRecord<'_>>(bar, continuation)?; + self.send_single_command::<ContinuationRecord<'_>>(dev, bar, continuation)?; } Ok(()) } - /// Sends `command` to the GSP and waits for the reply. - /// - /// # Errors - /// - /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is - /// not received 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 and reply initializers are propagated as-is. - pub(crate) fn send_sync_command<M>(&mut self, bar: &Bar0, command: M) -> Result<M::Reply> - where - M: CommandToGsp, - M::Reply: MessageFromGsp, - Error: From<M::InitError>, - Error: From<<M::Reply as MessageFromGsp>::InitError>, - { - self.send_command(bar, command)?; - - loop { - match self.receive_msg::<M::Reply>(Delta::from_secs(10)) { - Ok(reply) => break Ok(reply), - Err(ERANGE) => continue, - Err(e) => break Err(e), - } - } - } - - /// Sends `command` to the GSP without waiting for a reply. - /// - /// # Errors - /// - /// - `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. - pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result - where - M: CommandToGsp<Reply = NoReply>, - Error: From<M::InitError>, - { - self.send_command(bar, command) - } - /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message @@ -695,7 +602,7 @@ pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result /// message queue. /// /// Error codes returned by the message constructor are propagated as-is. - fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { + fn wait_for_msg(&self, dev: &device::Device, timeout: Delta) -> Result<GspMessage<'_>> { // Wait for a message to arrive from the GSP. let (slice_1, slice_2) = read_poll_timeout( || Ok(self.gsp_mem.driver_read_area()), @@ -712,7 +619,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?; dev_dbg!( - self.dev, + dev, "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n", header.sequence(), header.function(), @@ -747,7 +654,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { ])) != 0 { dev_err!( - self.dev, + dev, "GSP RPC: receive: Call {} - bad checksum\n", header.sequence() ); @@ -776,12 +683,12 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { /// - `ERANGE` if the message had a recognized but non-matching function code. /// /// Error codes returned by [`MessageFromGsp::read`] are propagated as-is. - pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M> + fn receive_msg<M: MessageFromGsp>(&mut self, dev: &device::Device, timeout: Delta) -> Result<M> where // This allows all error types, including `Infallible`, to be used for `M::InitError`. Error: From<M::InitError>, { - let message = self.wait_for_msg(timeout)?; + let message = self.wait_for_msg(dev, timeout)?; let function = message.header.function().map_err(|_| EINVAL)?; // Extract the message. Store the result as we want to advance the read pointer even in @@ -794,11 +701,7 @@ pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Resul .map_err(|e| e.into()) .inspect(|_| { if !sbuffer.is_empty() { - dev_warn!( - &self.dev, - "GSP message {:?} has unprocessed data\n", - function - ); + dev_warn!(dev, "GSP message {:?} has unprocessed data\n", function); } }) } else { @@ -812,9 +715,132 @@ pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Resul result } +} + +/// GSP command queue. +/// +/// Provides the ability to send commands and receive messages from the GSP using a shared memory +/// area. +#[pin_data] +pub(crate) struct Cmdq { + /// Device this command queue belongs to. + dev: ARef<device::Device>, + /// Inner mutex-protected state. + #[pin] + inner: Mutex<CmdqInner>, +} + +impl Cmdq { + /// Offset of the data after the PTEs. + const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq); + + /// Offset of command queue ring buffer. + pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq) + + core::mem::offset_of!(Msgq, msgq) + - Self::POST_PTE_OFFSET; + + /// Offset of message queue ring buffer. + pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq) + + core::mem::offset_of!(Msgq, msgq) + - Self::POST_PTE_OFFSET; + + /// Number of page table entries for the GSP shared region. + pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT; + + /// Creates a new command queue for `dev`. + pub(crate) fn new(dev: &device::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { + try_pin_init!(Self { + inner <- new_mutex!(CmdqInner { + gsp_mem: DmaGspMem::new(dev)?, + seq: 0, + }), + dev: dev.into(), + }) + } + + /// Computes the checksum for the message pointed to by `it`. + /// + /// A message is made of several parts, so `it` is an iterator over byte slices representing + /// these parts. + fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 { + let sum64 = it + .enumerate() + .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte)) + .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol)); + + ((sum64 >> 32) as u32) ^ (sum64 as u32) + } + + /// Notifies the GSP that we have updated the command queue pointers. + fn notify_gsp(bar: &Bar0) { + regs::NV_PGSP_QUEUE_HEAD::default() + .set_address(0) + .write(bar); + } + + /// Sends `command` to the GSP and waits for the reply. + /// + /// The mutex is held for the entire send+receive cycle to ensure that no other command can + /// be interleaved. Messages with non-matching function codes are silently consumed until the + /// expected reply arrives. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is + /// not received 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 and reply initializers are propagated as-is. + pub(crate) fn send_sync_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply> + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + Error: From<M::InitError>, + Error: From<<M::Reply as MessageFromGsp>::InitError>, + { + let mut inner = self.inner.lock(); + inner.send_command(&self.dev, bar, command)?; + + loop { + match inner.receive_msg::<M::Reply>(&self.dev, Delta::from_secs(10)) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } + + /// Sends `command` to the GSP without waiting for a reply. + /// + /// # Errors + /// + /// - `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. + pub(crate) fn send_async_command<M>(&self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp<Reply = NoReply>, + Error: From<M::InitError>, + { + self.inner.lock().send_command(&self.dev, bar, command) + } + + /// Receive a message from the GSP. + /// + /// See [`CmdqInner::receive_msg`] for details. + pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M> + where + // This allows all error types, including `Infallible`, to be used for `M::InitError`. + Error: From<M::InitError>, + { + self.inner.lock().receive_msg(&self.dev, timeout) + } /// Returns the DMA handle of the command queue's shared memory region. pub(crate) fn dma_handle(&self) -> DmaAddress { - self.gsp_mem.0.dma_handle() + self.inner.lock().gsp_mem.0.dma_handle() } } diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 47ca31611927..4740cda0b51c 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -170,7 +170,7 @@ fn read( } /// Waits for GSP initialization to complete. -pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { +pub(crate) fn wait_gsp_init_done(cmdq: &Cmdq) -> Result { loop { match cmdq.receive_msg::<GspInitDone>(Delta::from_secs(10)) { Ok(_) => break Ok(()), @@ -239,7 +239,7 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { } /// Send the [`GetGspInfo`] command and awaits for its reply. -pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> { +pub(crate) fn get_gsp_info(cmdq: &Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> { cmdq.send_sync_command(bar, GetGspStaticInfo) } diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs index 0cfbedc47fcf..f99f4fe652ba 100644 --- a/drivers/gpu/nova-core/gsp/sequencer.rs +++ b/drivers/gpu/nova-core/gsp/sequencer.rs @@ -356,7 +356,7 @@ pub(crate) struct GspSequencerParams<'a> { } impl<'a> GspSequencer<'a> { - pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { + pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result { let seq_info = loop { match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) { Ok(seq_info) => break seq_info, -- 2.53.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: add mutex locking to Cmdq 2026-02-26 14:50 ` [PATCH v2 4/4] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney @ 2026-02-27 2:04 ` Claude Code Review Bot 0 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 2:04 UTC (permalink / raw) To: dri-devel-reviews Patch Review **Status: Looks good, a few observations** This is the main patch, splitting `Cmdq` into an outer struct (holding `dev` and the `Mutex`) and `CmdqInner` (holding mutable state: `seq` and `gsp_mem`). The split is well-motivated: ```rust +struct CmdqInner { + seq: u32, + gsp_mem: DmaGspMem, +} + +pub(crate) struct Cmdq { + dev: ARef<device::Device>, + #[pin] + inner: Mutex<CmdqInner>, +} ``` Moving `dev` outside the mutex is correct — it's `ARef` (immutable, reference-counted) and doesn't need mutex protection. The `send_sync_command` correctly holds the lock across send+receive: ```rust + pub(crate) fn send_sync_command<M>(&self, bar: &Bar0, command: M) -> Result<M::Reply> + ... + { + let mut inner = self.inner.lock(); + inner.send_command(&self.dev, bar, command)?; + loop { + match inner.receive_msg::<M::Reply>(&self.dev, Delta::from_secs(10)) { ``` This ensures no interleaving of commands and their replies. Methods now take `&self` instead of `&mut self`, which is the whole point. Moving `calculate_checksum` and `notify_gsp` to `Cmdq` (rather than `CmdqInner`) makes sense since they don't use any mutable state — they're effectively static methods. **Observations:** 1. **`dma_handle()` takes the lock unnecessarily:** ```rust + pub(crate) fn dma_handle(&self) -> DmaAddress { + self.inner.lock().gsp_mem.0.dma_handle() + } ``` The DMA handle is set once during allocation and never changes. This could instead store the DMA handle in the outer `Cmdq` struct at construction time, avoiding the lock. However, this is a minor performance point and correctness is not affected. 2. **`receive_msg` is exposed as `pub(crate)` on `Cmdq`:** ```rust + pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M> + ... + { + self.inner.lock().receive_msg(&self.dev, timeout) + } ``` This is used by `wait_gsp_init_done` and `GspSequencer::run` which receive messages without a preceding send. The lock is acquired and released per call, which is correct for these use cases. However, if someone were to call this in a loop (as `wait_gsp_init_done` does), each iteration re-acquires the lock — this is fine for correctness and allows other operations to interleave. 3. **Thread passing `dev` through method parameters:** The `CmdqInner` methods now take `dev: &device::Device` as a parameter instead of accessing `self.dev`, since `dev` lives outside the inner struct. This is a clean pattern that avoids the need for the inner struct to hold a reference back to the outer struct. 4. **Formatting nit** in the constructor: ```rust + try_pin_init!(Self { + inner <- new_mutex!(CmdqInner { + gsp_mem: DmaGspMem::new(dev)?, + seq: 0, + }), + dev: dev.into(), + }) ``` The closing `})` for `new_mutex!` has unusual indentation — the `)` is at 16 spaces indent while the opening `new_mutex!` is at 24. This is a very minor style nit. 5. The `boot()` signature change from `mut self: Pin<&mut Self>` to `self: Pin<&mut Self>` reflects that `Cmdq` methods no longer require `&mut self`: ```rust - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, ``` Overall the locking design is correct and well-documented. Already has Reviewed-by from Zhi Wang. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney ` (3 preceding siblings ...) 2026-02-26 14:50 ` [PATCH v2 4/4] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney @ 2026-02-26 18:48 ` Zhi Wang 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 5 siblings, 0 replies; 12+ messages in thread From: Zhi Wang @ 2026-02-26 18:48 UTC (permalink / raw) To: Eliot Courtney Cc: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo, nouveau, dri-devel, linux-kernel, rust-for-linux On Thu, 26 Feb 2026 23:50:22 +0900 Eliot Courtney <ecourtney@nvidia.com> wrote: Tested-by: Zhi Wang <zhiw@nvidia.com> > Add locking to Cmdq. This is required e.g. for unloading the driver, > which needs to send the UnloadingGuestDriver via the command queue > on unbind which may be on a different thread. > > We have commands that need a reply ("sync") and commands that don't > ("async"). For sync commands we want to make sure that they don't get > the reply of a different command back. The approach this patch series > takes is by making sync commands block until they get a response. For > now this should be ok, and we expect GSP to be fast anyway. > > To do this, we need to know which commands expect a reply and which > don't. John's existing series[1] adds IS_ASYNC which solves part of > the problem, but we need to know a bit more. So instead, add an > associated type called Reply which tells us what the reply is. > > An alternative would be to define traits inheriting CommandToGsp, e.g. > SyncCommand and AsyncCommand, instead of using the associated type. I > implemented the associated type version because it feels more > compositional rather than inherity so seemed a bit better to me. But > both of these approaches work and are fine, IMO. > > In summary, this patch series has three steps: > 1. Add the type infrastructure to know what replies are expected for a > command and update each caller to explicitly send a sync or async > command. > 2. Make Cmdq pinned so we can use Mutex > 3. Add a Mutex to protect Cmdq by moving the relevant state to an > inner struct. > > [1]: > https://lore.kernel.org/all/20260211000451.192109-1-jhubbard@nvidia.com/ > > Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> > --- > Changes in v2: > - Rebase on drm-rust-next > - Link to v1: > https://lore.kernel.org/r/20260225-cmdq-locking-v1-0-bbf6b4156706@nvidia.com > > --- > Eliot Courtney (4): > gpu: nova-core: gsp: fix stale doc comments on command queue > methods gpu: nova-core: gsp: add sync and async command queue API to > `Cmdq` gpu: nova-core: gsp: make `Cmdq` a pinned type > gpu: nova-core: gsp: add mutex locking to Cmdq > > drivers/gpu/nova-core/gsp.rs | 5 +- > drivers/gpu/nova-core/gsp/boot.rs | 13 +- > drivers/gpu/nova-core/gsp/cmdq.rs | 246 > +++++++++++++++++++++------------ > drivers/gpu/nova-core/gsp/commands.rs | 23 ++- > drivers/gpu/nova-core/gsp/sequencer.rs | 2 +- 5 files changed, 183 > insertions(+), 106 deletions(-) --- > base-commit: 4a49fe23e357b48845e31fe9c28a802c05458198 > change-id: 20260225-cmdq-locking-d32928a2c2cf > prerequisite-message-id: > <20260226-cmdq-continuation-v3-0-572ab9916766@nvidia.com> > prerequisite-patch-id: fd45bc5b8eda5e2b54a052dddb1a1c363107f0a7 > prerequisite-patch-id: d0f59ef489346e978a222755f9fb42dfe7af19e5 > prerequisite-patch-id: 8844970d0e387488c70979a73732579ba174b46c > prerequisite-patch-id: e138a94ed48fa8d50d5ed1eb36524f98923ed478 > prerequisite-patch-id: 4599a5e90d6665fa3acb7d4045de5d378ac28b4d > prerequisite-patch-id: 30ed64c398e541d6efbcb2e46ed9a9e6cf953f4f > prerequisite-patch-id: 9a965e9f29c8680c0b554e656ff8e9a1bfc67280 > prerequisite-patch-id: d8cccc3dfb070f304805fc7e0f24121809b4b300 > prerequisite-patch-id: c0a73dfd1fb630ab02486f0180b90f8fe850b4dc > > Best regards, ^ permalink raw reply [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: add locking to Cmdq 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney ` (4 preceding siblings ...) 2026-02-26 18:48 ` [PATCH v2 0/4] gpu: nova-core: gsp: add " Zhi Wang @ 2026-02-27 2:04 ` Claude Code Review Bot 5 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 2:04 UTC (permalink / raw) To: dri-devel-reviews Overall Series Review Subject: gpu: nova-core: gsp: add locking to Cmdq Author: Eliot Courtney <ecourtney@nvidia.com> Patches: 6 Reviewed: 2026-02-27T12:04:08.349113 --- This is a well-structured 4-patch series by Eliot Courtney that adds mutex locking to the `Cmdq` (command queue) in the nova-core GSP driver. The series is motivated by the need to send commands (e.g., `UnloadingGuestDriver`) from different threads during driver unloading. The approach is sound: introduce a type-level distinction between sync and async commands via an associated `Reply` type, convert `Cmdq` to a pinned type (required for kernel `Mutex`), and then wrap mutable state in a `Mutex<CmdqInner>`. The decomposition into four incremental patches is clean and logical — each builds on the previous in a natural progression. **Key design decisions:** - Using an associated `Reply` type rather than separate `SyncCommand`/`AsyncCommand` traits is a good compositional choice - Holding the mutex across send+receive in `send_sync_command` is the right approach to prevent reply mismatches between concurrent callers - The `NoReply` marker type that intentionally does *not* implement `MessageFromGsp` gives compile-time enforcement **Concerns:** - The `send_sync_command` receive loop with a 10-second per-iteration timeout and no overall deadline could block indefinitely if the GSP keeps sending non-matching messages (ERANGE) - `dma_handle()` takes the mutex just to read a DMA address that never changes after init — this is unnecessary overhead (though harmless) - The series depends on prerequisite patches that should be stated clearly for merge order Overall this is clean, well-documented code that follows Rust/kernel conventions properly. The series is in good shape. --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 0/4] gpu: nova-core: gsp: add locking to Cmdq
@ 2026-02-25 13:41 Eliot Courtney
2026-02-25 13:41 ` [PATCH 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney
0 siblings, 1 reply; 12+ messages in thread
From: Eliot Courtney @ 2026-02-25 13:41 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
Simona Vetter, Benno Lossin, Gary Guo
Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney
Add locking to Cmdq. This is required e.g. for unloading the driver,
which needs to send the UnloadingGuestDriver via the command queue
on unbind which may be on a different thread.
We have commands that need a reply ("sync") and commands that don't
("async"). For sync commands we want to make sure that they don't get
the reply of a different command back. The approach this patch series
takes is by making sync commands block until they get a response. For
now this should be ok, and we expect GSP to be fast anyway.
To do this, we need to know which commands expect a reply and which
don't. John's existing series[1] adds IS_ASYNC which solves part of the
problem, but we need to know a bit more. So instead, add an
associated type called Reply which tells us what the reply is.
An alternative would be to define traits inheriting CommandToGsp, e.g.
SyncCommand and AsyncCommand, instead of using the associated type. I
implemented the associated type version because it feels more
compositional rather than inherity so seemed a bit better to me. But
both of these approaches work and are fine, IMO.
In summary, this patch series has three steps:
1. Add the type infrastructure to know what replies are expected for a
command and update each caller to explicitly send a sync or async
command.
2. Make Cmdq pinned so we can use Mutex
3. Add a Mutex to protect Cmdq by moving the relevant state to an
inner struct.
[1]: https://lore.kernel.org/all/20260211000451.192109-1-jhubbard@nvidia.com/
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Eliot Courtney (4):
gpu: nova-core: gsp: fix stale doc comments on command queue methods
gpu: nova-core: gsp: add sync and async command queue API to `Cmdq`
gpu: nova-core: gsp: make `Cmdq` a pinned type
gpu: nova-core: gsp: add mutex locking to Cmdq
drivers/gpu/nova-core/gsp.rs | 5 +-
drivers/gpu/nova-core/gsp/boot.rs | 13 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 240 ++++++++++++++++++++++-----------
drivers/gpu/nova-core/gsp/commands.rs | 23 ++--
drivers/gpu/nova-core/gsp/sequencer.rs | 2 +-
5 files changed, 182 insertions(+), 101 deletions(-)
---
base-commit: 4f938c7d3b25d87b356af4106c2682caf8c835a2
change-id: 20260225-cmdq-locking-d32928a2c2cf
prerequisite-message-id: <20260129-nova-core-cmdq1-v3-0-2ede85493a27@nvidia.com>
prerequisite-patch-id: bd2da0d580038dbab7ef06c0508d157873796ff4
prerequisite-patch-id: f834078971a3a46babe131251353184ffd7ecc38
prerequisite-patch-id: db5049ec7fe4348eed9e54dd5a2c07c697a54c81
prerequisite-patch-id: bafdd38cb9aba355a796232e07d5bbcd6fc59ba5
prerequisite-patch-id: 72a8d4626458707ae4ed632c629ffb6728cee032
prerequisite-message-id: <20260219-cmdq-continuation-v2-0-2e8b7615536f@nvidia.com>
prerequisite-patch-id: 20003099ca19d81b6255c5e95148f6584c108a29
prerequisite-patch-id: d0f59ef489346e978a222755f9fb42dfe7af19e5
prerequisite-patch-id: 8844970d0e387488c70979a73732579ba174b46c
prerequisite-patch-id: e138a94ed48fa8d50d5ed1eb36524f98923ed478
prerequisite-patch-id: 4599a5e90d6665fa3acb7d4045de5d378ac28b4d
prerequisite-patch-id: 7975f5bcaf28a99202d86d755fc256cd25be9cc4
prerequisite-patch-id: ee694b609c76628508d12fbe9b8b5a8a9354bfc4
prerequisite-patch-id: 1bb2f05c6a409a39421586e8148f82fa62ce6875
prerequisite-patch-id: a932945cb83fb631ebff3ac76ffff4319cacaabe
Best regards,
--
Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` 2026-02-25 13:41 [PATCH 0/4] " Eliot Courtney @ 2026-02-25 13:41 ` Eliot Courtney 2026-02-27 3:18 ` Claude review: " Claude Code Review Bot 0 siblings, 1 reply; 12+ messages in thread From: Eliot Courtney @ 2026-02-25 13:41 UTC (permalink / raw) To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie, Simona Vetter, Benno Lossin, Gary Guo Cc: nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney Add sync and async command queue API and the type infrastructure to know what reply is expected from each `CommandToGsp`. Use a marker type `NoReply` which does not implement `MessageFromGsp` to mark async commands which don't expect a response. This prepares for adding locking to the queue. Signed-off-by: Eliot Courtney <ecourtney@nvidia.com> --- drivers/gpu/nova-core/gsp/boot.rs | 5 ++-- drivers/gpu/nova-core/gsp/cmdq.rs | 54 ++++++++++++++++++++++++++++++++++- drivers/gpu/nova-core/gsp/commands.rs | 19 ++++++------ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index be427fe26a58..1cb21da855b9 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -160,8 +160,9 @@ pub(crate) fn boot( dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; self.cmdq - .send_command(bar, commands::SetSystemInfo::new(pdev))?; - self.cmdq.send_command(bar, commands::SetRegistry::new())?; + .send_async_command(bar, commands::SetSystemInfo::new(pdev))?; + self.cmdq + .send_async_command(bar, commands::SetRegistry::new())?; gsp_falcon.reset(bar)?; let libos_handle = self.libos.dma_handle(); diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index cfae5b35adec..e1ca1bb9e07d 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -48,6 +48,10 @@ sbuffer::SBufferIter, // }; +/// Marker type representing the absence of a reply for a command. This does not implement +/// `MessageFromGsp`. +pub(crate) struct NoReply; + /// Trait implemented by types representing a command to send to the GSP. /// /// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it @@ -66,6 +70,9 @@ pub(crate) trait CommandToGsp { /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer. type Command: FromBytes + AsBytes; + /// Type of the reply expected from the GSP, or [`NoReply`] for async commands. + type Reply; + /// Error type returned by [`CommandToGsp::init`]. type InitError; @@ -604,7 +611,7 @@ fn send_single_command<M>(&mut self, bar: &Bar0, command: M) -> Result /// written to by its [`CommandToGsp::init_variable_payload`] method. /// /// Error codes returned by the command initializers are propagated as-is. - pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result + fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp, Error: From<M::InitError>, @@ -626,6 +633,51 @@ pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result Ok(()) } + /// Sends `command` to the GSP and waits for the reply. + /// + /// # Errors + /// + /// - `ETIMEDOUT` if space does not become available to send the command, or if the reply is + /// not received 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 and reply initializers are propagated as-is. + pub(crate) fn send_sync_command<M>(&mut self, bar: &Bar0, command: M) -> Result<M::Reply> + where + M: CommandToGsp, + M::Reply: MessageFromGsp, + Error: From<M::InitError>, + Error: From<<M::Reply as MessageFromGsp>::InitError>, + { + self.send_command(bar, command)?; + + loop { + match self.receive_msg::<M::Reply>(Delta::from_secs(10)) { + Ok(reply) => break Ok(reply), + Err(ERANGE) => continue, + Err(e) => break Err(e), + } + } + } + + /// Sends `command` to the GSP without waiting for a reply. + /// + /// # Errors + /// + /// - `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. + pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result + where + M: CommandToGsp<Reply = NoReply>, + Error: From<M::InitError>, + { + self.send_command(bar, command) + } + /// Wait for a message to become available on the message queue. /// /// This works purely at the transport layer and does not interpret or validate the message diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs index 1683ebb4c685..b42e32dcc55c 100644 --- a/drivers/gpu/nova-core/gsp/commands.rs +++ b/drivers/gpu/nova-core/gsp/commands.rs @@ -26,7 +26,8 @@ command_size, Cmdq, CommandToGsp, - MessageFromGsp, // + MessageFromGsp, + NoReply, // }, fw::{ commands::*, @@ -53,6 +54,7 @@ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self { impl<'a> CommandToGsp for SetSystemInfo<'a> { const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo; type Command = GspSetSystemInfo; + type Reply = NoReply; type InitError = Error; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -104,6 +106,7 @@ pub(crate) fn new() -> Self { impl CommandToGsp for SetRegistry { const FUNCTION: MsgFunction = MsgFunction::SetRegistry; type Command = PackedRegistryTable; + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -183,6 +186,7 @@ pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { impl CommandToGsp for GetGspStaticInfo { const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; type Command = GspStaticConfigInfo; + type Reply = GetGspStaticInfoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -236,15 +240,7 @@ pub(crate) fn gpu_name(&self) -> core::result::Result<&str, GpuNameError> { /// Send the [`GetGspInfo`] command and awaits for its reply. pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> { - cmdq.send_command(bar, GetGspStaticInfo)?; - - loop { - match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) { - Ok(info) => return Ok(info), - Err(ERANGE) => continue, - Err(e) => return Err(e), - } - } + cmdq.send_sync_command(bar, GetGspStaticInfo) } /// The `ContinuationRecord` command. @@ -262,6 +258,7 @@ pub(crate) fn new(data: &'a [u8]) -> Self { impl<'a> CommandToGsp for ContinuationRecord<'a> { const FUNCTION: MsgFunction = MsgFunction::ContinuationRecord; type Command = (); + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -354,6 +351,7 @@ pub(crate) enum SplitCommand<'a, C: CommandToGsp> { impl<'a, C: CommandToGsp> CommandToGsp for SplitCommand<'a, C> { const FUNCTION: MsgFunction = C::FUNCTION; type Command = C::Command; + type Reply = C::Reply; type InitError = C::InitError; fn init(&self) -> impl Init<Self::Command, Self::InitError> { @@ -410,6 +408,7 @@ fn new(len: usize) -> Result<Self> { impl CommandToGsp for TestPayload { const FUNCTION: MsgFunction = MsgFunction::Nop; type Command = (); + type Reply = NoReply; type InitError = Infallible; fn init(&self) -> impl Init<Self::Command, Self::InitError> { -- 2.53.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Claude review: gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` 2026-02-25 13:41 ` [PATCH 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney @ 2026-02-27 3:18 ` Claude Code Review Bot 0 siblings, 0 replies; 12+ messages in thread From: Claude Code Review Bot @ 2026-02-27 3:18 UTC (permalink / raw) To: dri-devel-reviews Patch Review This is the core type infrastructure patch. Key observations: **The `NoReply` marker type is well-designed:** ```rust pub(crate) struct NoReply; ``` Not implementing `MessageFromGsp` means `send_sync_command` can't be called with async commands — the compiler enforces it via `M::Reply: MessageFromGsp`. **Associated type on `CommandToGsp`:** ```rust type Reply; ``` Clean addition. The cover letter discusses the alternative of separate `SyncCommand`/`AsyncCommand` traits, and I agree the associated type approach is more composable. **`send_sync_command` implementation:** ```rust pub(crate) fn send_sync_command<M>(&mut self, bar: &Bar0, command: M) -> Result<M::Reply> { self.send_command(bar, command)?; loop { match self.receive_msg::<M::Reply>(Delta::from_secs(10)) { Ok(reply) => break Ok(reply), Err(ERANGE) => continue, Err(e) => break Err(e), } } } ``` This correctly extracts the existing pattern from `get_gsp_info`. Note the timeout change from 5s to 10s as mentioned above — this is fine but worth acknowledging. **`send_async_command` is a thin wrapper:** ```rust pub(crate) fn send_async_command<M>(&mut self, bar: &Bar0, command: M) -> Result where M: CommandToGsp<Reply = NoReply>, { self.send_command(bar, command) } ``` The `Reply = NoReply` constraint is the enforcement mechanism. Clean. **Making `send_command` private (`fn` instead of `pub(crate) fn`):** ```rust - pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result + fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result ``` Good — callers should now go through the sync/async API. **`SplitCommand` propagates `Reply`:** ```rust + type Reply = C::Reply; ``` Correct — a split command should have the same reply type as the underlying command. **Caller updates in boot.rs:** ```rust - .send_command(bar, commands::SetSystemInfo::new(pdev))?; + .send_async_command(bar, commands::SetSystemInfo::new(pdev))?; ``` Correct — these pre-boot commands are fire-and-forget. **Refactoring of `get_gsp_info`:** ```rust - cmdq.send_command(bar, GetGspStaticInfo)?; - loop { - match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) { - Ok(info) => return Ok(info), - Err(ERANGE) => continue, - Err(e) => return Err(e), - } - } + cmdq.send_sync_command(bar, GetGspStaticInfo) ``` Nice simplification. No issues. **Reviewed-by worthy.** --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-02-27 3:18 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-02-26 14:50 [PATCH v2 0/4] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney 2026-02-26 14:50 ` [PATCH v2 1/4] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 3/4] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 14:50 ` [PATCH v2 4/4] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot 2026-02-26 18:48 ` [PATCH v2 0/4] gpu: nova-core: gsp: add " Zhi Wang 2026-02-27 2:04 ` Claude review: " Claude Code Review Bot -- strict thread matches above, loose matches on Subject: below -- 2026-02-25 13:41 [PATCH 0/4] " Eliot Courtney 2026-02-25 13:41 ` [PATCH 2/4] gpu: nova-core: gsp: add sync and async command queue API to `Cmdq` Eliot Courtney 2026-02-27 3:18 ` Claude review: " Claude Code Review Bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox