public inbox for drm-ai-reviews@public-inbox.freedesktop.org
 help / color / mirror / Atom feed
* Claude review: gpu: nova-core: gsp: add locking to Cmdq
  2026-02-26 14:50 [PATCH v2 0/4] " Eliot Courtney
@ 2026-02-27  2:04 ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ 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] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add locking to Cmdq
  2026-02-25 13:41 [PATCH 0/4] " Eliot Courtney
@ 2026-02-27  3:18 ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-02-27  3:18 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: 10
Reviewed: 2026-02-27T13:18:03.810448

---

This is a well-structured 4-patch series that adds mutex locking to the GSP command queue (`Cmdq`) in the nova-core Rust GPU driver. The series has clear prerequisites (John's cmdq split/continuation series and possibly others) which explains why it doesn't apply to drm-next.

The approach is sound: distinguish sync from async commands at the type level using an associated `Reply` type, make `Cmdq` a pinned type (required for kernel `Mutex`), then protect the mutable state with a `Mutex` so the API takes `&self` instead of `&mut self`. This enables concurrent access from different threads (e.g., during driver unbind).

**Key design decisions that look correct:**
- Holding the mutex across both send and receive in `send_sync_command` prevents reply mismatches between concurrent sync commands.
- Using `NoReply` as a marker type with the `Reply = NoReply` constraint on `send_async_command` provides compile-time enforcement.
- Moving `calculate_checksum` and `notify_gsp` (which don't need `&mut self`) to the outer `Cmdq` struct, while keeping actual state-mutating methods on `CmdqInner`, is a clean split.

**Concerns:**

1. **`dma_handle()` takes the lock**: After patch 4, `dma_handle()` acquires the mutex just to read the DMA address, which is immutable after construction. This is wasteful and could be a problem if the DMA address is needed while the lock is held elsewhere. The DMA handle could be stored as a separate field on `Cmdq` (outside the mutex), or `DmaGspMem` could be split so the handle is accessible without locking.

2. **`send_sync_command` busy-loops on `ERANGE` while holding the mutex**: If unexpected messages arrive (non-matching function codes), the loop silently consumes them while blocking all other command queue access. This means async commands cannot be sent while a sync command is waiting for its reply. The cover letter acknowledges this trade-off and says GSP is expected to be fast, which is acceptable for now, but should be documented as a known limitation.

3. **Timeout accumulation**: The `receive_msg` call inside the `send_sync_command` loop uses a 10-second timeout per attempt. Since `ERANGE` causes a `continue`, the effective timeout can be `N * 10` seconds if many non-matching messages arrive. This differs from the original `get_gsp_info` which used 5-second timeouts — the series changes this to 10 seconds.

Overall the series is clean and well-structured. The concerns above are mostly future considerations rather than blockers.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add locking to Cmdq
  2026-03-04  2:46 [PATCH v3 0/5] " Eliot Courtney
@ 2026-03-05  3:52 ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-05  3:52 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: 22
Reviewed: 2026-03-05T13:52:59.195127

---

This is a well-structured 5-patch series adding mutex locking to the nova-core GSP command queue (`Cmdq`). The motivation is clear: commands need to be sent from different threads (e.g. driver unbind), so the queue needs synchronization. The approach of distinguishing reply/no-reply commands via an associated type is clean and leverages Rust's type system well. The series is logically ordered: doc fixes → constant extraction → type infrastructure → pin requirement → mutex.

The series is generally solid. The design choice to hold the mutex across both send+receive for commands expecting a reply is correct and necessary to prevent reply mismatches. The code is well documented.

I have a few concerns, mainly around the `send_command` reply loop potentially blocking the mutex indefinitely, and the `dma_handle` accessor locking the mutex just to read an immutable field.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq
@ 2026-03-10  8:09 Eliot Courtney
  2026-03-10  8:09 ` [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	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 and commands that don't. For
commands with a reply 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 those 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.
CommandWithReply and CommandWithoutReply, 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 wait for the reply or
   not.
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 v4:
- Change RECEIVE_TIMEOUT from 10s to 5s
- Update NoReply doc comment
- Move CommandToGsp doc comment update
- Fix import formatting in continuation.rs
- Move CmdqInner after Cmdq to reduce diff size
- Link to v3: https://lore.kernel.org/r/20260304-cmdq-locking-v3-0-a6314b708850@nvidia.com

Changes in v3:
- Rename send_sync_command/send_async_command to
  send_command/send_command_no_wait.
- Move `dev` field into `CmdqInner` to avoid passing it through method
  parameters.
- Add `RECEIVE_TIMEOUT` constant for the 10s receive timeout.
- Link to v2: https://lore.kernel.org/r/20260226-cmdq-locking-v2-0-c7e16a6d5885@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 (5):
      gpu: nova-core: gsp: fix stale doc comments on command queue methods
      gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue
      gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp`
      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              | 157 +++++++++++++++++++------
 drivers/gpu/nova-core/gsp/cmdq/continuation.rs |   8 +-
 drivers/gpu/nova-core/gsp/commands.rs          |  23 ++--
 drivers/gpu/nova-core/gsp/sequencer.rs         |   4 +-
 6 files changed, 149 insertions(+), 61 deletions(-)
---
base-commit: 4a49fe23e357b48845e31fe9c28a802c05458198
change-id: 20260225-cmdq-locking-d32928a2c2cf
prerequisite-message-id: <20260304-cmdq-continuation-v5-0-3f19d759ed93@nvidia.com>
prerequisite-patch-id: fd45bc5b8eda5e2b54a052dddb1a1c363107f0a7
prerequisite-patch-id: 06fe65f900206c44b5ba52286ca4ce1ca42b55d5
prerequisite-patch-id: 8844970d0e387488c70979a73732579ba174b46c
prerequisite-patch-id: e138a94ed48fa8d50d5ed1eb36524f98923ed478
prerequisite-patch-id: dccf2b12b176947e89b44baafda9c5a0aa0a93bc
prerequisite-patch-id: 30ed64c398e541d6efbcb2e46ed9a9e6cf953f4f
prerequisite-patch-id: ba1c8da0cbdb4682b879633a94a172d1b2b6bc8e
prerequisite-patch-id: 081d4a4198a0bf09f3480cb8baf296db585decce
prerequisite-patch-id: 56c8c25e7362178cd019c8f03954a6bcdb72b1b5

Best regards,
-- 
Eliot Courtney <ecourtney@nvidia.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
@ 2026-03-10  8:09 ` Eliot Courtney
  2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
  2026-03-10  8:09 ` [PATCH v4 2/5] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue Eliot Courtney
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	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>
Tested-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 e0b096546d23..cbdc76a33a54 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -531,6 +531,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.
@@ -711,22 +712,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] 15+ messages in thread

* [PATCH v4 2/5] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
  2026-03-10  8:09 ` [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
@ 2026-03-10  8:09 ` Eliot Courtney
  2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
  2026-03-10  8:09 ` [PATCH v4 3/5] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp` Eliot Courtney
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney,
	Zhi Wang

Remove magic numbers and add a default timeout for callers to use.

Tested-by: Zhi Wang <zhiw@nvidia.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/cmdq.rs      | 3 +++
 drivers/gpu/nova-core/gsp/commands.rs  | 5 ++---
 drivers/gpu/nova-core/gsp/sequencer.rs | 2 +-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index cbdc76a33a54..08ac3067f1b5 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -496,6 +496,9 @@ impl Cmdq {
     /// Timeout for waiting for space on the command queue.
     const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1);
 
+    /// Default timeout for receiving a message from the GSP.
+    pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5);
+
     /// Creates a new command queue for `dev`.
     pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {
         let gsp_mem = DmaGspMem::new(dev)?;
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 8f270eca33be..88df117ba575 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -11,7 +11,6 @@
     device,
     pci,
     prelude::*,
-    time::Delta,
     transmute::{
         AsBytes,
         FromBytes, //
@@ -165,7 +164,7 @@ fn read(
 /// Waits for GSP initialization to complete.
 pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result {
     loop {
-        match cmdq.receive_msg::<GspInitDone>(Delta::from_secs(10)) {
+        match cmdq.receive_msg::<GspInitDone>(Cmdq::RECEIVE_TIMEOUT) {
             Ok(_) => break Ok(()),
             Err(ERANGE) => continue,
             Err(e) => break Err(e),
@@ -235,7 +234,7 @@ pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticIn
     cmdq.send_command(bar, GetGspStaticInfo)?;
 
     loop {
-        match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) {
+        match cmdq.receive_msg::<GetGspStaticInfoReply>(Cmdq::RECEIVE_TIMEOUT) {
             Ok(info) => return Ok(info),
             Err(ERANGE) => continue,
             Err(e) => return Err(e),
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index 0cfbedc47fcf..ce2b3bb05d22 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -358,7 +358,7 @@ pub(crate) struct GspSequencerParams<'a> {
 impl<'a> GspSequencer<'a> {
     pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result {
         let seq_info = loop {
-            match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) {
+            match cmdq.receive_msg::<GspSequence>(Cmdq::RECEIVE_TIMEOUT) {
                 Ok(seq_info) => break seq_info,
                 Err(ERANGE) => continue,
                 Err(e) => return Err(e),

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v4 3/5] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp`
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
  2026-03-10  8:09 ` [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
  2026-03-10  8:09 ` [PATCH v4 2/5] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue Eliot Courtney
@ 2026-03-10  8:09 ` Eliot Courtney
  2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
  2026-03-10  8:09 ` [PATCH v4 4/5] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	nouveau, dri-devel, linux-kernel, rust-for-linux, Eliot Courtney,
	Zhi Wang

Add type infrastructure to know what reply is expected from each
`CommandToGsp`. Uses a marker type `NoReply` which does not implement
`MessageFromGsp` to mark commands which don't expect a response.

Update `send_command` to wait for a reply and add `send_command_no_wait`
which sends a command that has no reply, without blocking.

This prepares for adding locking to the queue.

Tested-by: Zhi Wang <zhiw@nvidia.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
 drivers/gpu/nova-core/gsp/boot.rs              |  5 ++-
 drivers/gpu/nova-core/gsp/cmdq.rs              | 59 ++++++++++++++++++++++++--
 drivers/gpu/nova-core/gsp/cmdq/continuation.rs |  8 +++-
 drivers/gpu/nova-core/gsp/commands.rs          | 16 +++----
 4 files changed, 72 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index c56029f444cb..991eb5957e3d 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_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?;
+        self.cmdq
+            .send_command_no_wait(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 08ac3067f1b5..b631daae5642 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -51,10 +51,14 @@
     sbuffer::SBufferIter, //
 };
 
+/// Marker type representing the absence of a reply for a command. Commands using this as their
+/// reply type are sent using [`Cmdq::send_command_no_wait`].
+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
-/// 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.
@@ -69,6 +73,10 @@ 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 commands that don't
+    /// have a reply.
+    type Reply;
+
     /// Error type returned by [`CommandToGsp::init`].
     type InitError;
 
@@ -610,7 +618,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_internal<M>(&mut self, bar: &Bar0, command: M) -> Result
     where
         M: CommandToGsp,
         Error: From<M::InitError>,
@@ -630,6 +638,51 @@ pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result
         }
     }
 
+    /// 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_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_internal(bar, command)?;
+
+        loop {
+            match self.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
+                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_command_no_wait<M>(&mut self, bar: &Bar0, command: M) -> Result
+    where
+        M: CommandToGsp<Reply = NoReply>,
+        Error: From<M::InitError>,
+    {
+        self.send_command_internal(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/cmdq/continuation.rs b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
index 2aa17caac2e0..05e904f18097 100644
--- a/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq/continuation.rs
@@ -6,7 +6,10 @@
 
 use kernel::prelude::*;
 
-use super::CommandToGsp;
+use super::{
+    CommandToGsp,
+    NoReply, //
+};
 
 use crate::{
     gsp::fw::{
@@ -63,6 +66,7 @@ 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> {
@@ -144,6 +148,7 @@ fn new(command: C, payload: KVVec<u8>) -> Self {
 impl<C: CommandToGsp> CommandToGsp for SplitCommand<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> {
@@ -206,6 +211,7 @@ fn new(len: usize) -> Result<Self> {
     impl CommandToGsp for TestPayload {
         const FUNCTION: MsgFunction = MsgFunction::Nop;
         type Command = TestHeader;
+        type Reply = NoReply;
         type InitError = Infallible;
 
         fn init(&self) -> impl Init<Self::Command, Self::InitError> {
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 88df117ba575..77054c92fcc2 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -23,7 +23,8 @@
         cmdq::{
             Cmdq,
             CommandToGsp,
-            MessageFromGsp, //
+            MessageFromGsp,
+            NoReply, //
         },
         fw::{
             commands::*,
@@ -48,6 +49,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> {
@@ -99,6 +101,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> {
@@ -178,6 +181,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> {
@@ -231,13 +235,5 @@ 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>(Cmdq::RECEIVE_TIMEOUT) {
-            Ok(info) => return Ok(info),
-            Err(ERANGE) => continue,
-            Err(e) => return Err(e),
-        }
-    }
+    cmdq.send_command(bar, GetGspStaticInfo)
 }

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v4 4/5] gpu: nova-core: gsp: make `Cmdq` a pinned type
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
                   ` (2 preceding siblings ...)
  2026-03-10  8:09 ` [PATCH v4 3/5] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp` Eliot Courtney
@ 2026-03-10  8:09 ` Eliot Courtney
  2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
  2026-03-10  8:09 ` [PATCH v4 5/5] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney
  2026-03-11  3:32 ` Claude review: gpu: nova-core: gsp: add " Claude Code Review Bot
  5 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	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>
Tested-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 b631daae5642..90179256a929 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -475,6 +475,7 @@ struct GspMessage<'a> {
 ///
 /// 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>,
@@ -508,13 +509,11 @@ impl Cmdq {
     pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5);
 
     /// 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] 15+ messages in thread

* [PATCH v4 5/5] gpu: nova-core: gsp: add mutex locking to Cmdq
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
                   ` (3 preceding siblings ...)
  2026-03-10  8:09 ` [PATCH v4 4/5] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney
@ 2026-03-10  8:09 ` Eliot Courtney
  2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
  2026-03-11  3:32 ` Claude review: gpu: nova-core: gsp: add " Claude Code Review Bot
  5 siblings, 1 reply; 15+ messages in thread
From: Eliot Courtney @ 2026-03-10  8:09 UTC (permalink / raw)
  To: Danilo Krummrich, Alice Ryhl, Alexandre Courbot, David Airlie,
	Simona Vetter, Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
	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_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>
Tested-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      | 165 ++++++++++++++++++++-------------
 drivers/gpu/nova-core/gsp/commands.rs  |   4 +-
 drivers/gpu/nova-core/gsp/sequencer.rs |   2 +-
 4 files changed, 105 insertions(+), 74 deletions(-)

diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 991eb5957e3d..bc53e667cd9e 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 90179256a929..47406d494523 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -18,8 +18,12 @@
     },
     dma_write,
     io::poll::read_poll_timeout,
+    new_mutex,
     prelude::*,
-    sync::aref::ARef,
+    sync::{
+        aref::ARef,
+        Mutex, //
+    },
     time::Delta,
     transmute::{
         AsBytes,
@@ -477,12 +481,9 @@ struct GspMessage<'a> {
 /// area.
 #[pin_data]
 pub(crate) struct Cmdq {
-    /// Device this command queue belongs to.
-    dev: ARef<device::Device>,
-    /// Current command sequence number.
-    seq: u32,
-    /// Memory area shared with the GSP for communicating commands and messages.
-    gsp_mem: DmaGspMem,
+    /// Inner mutex-protected state.
+    #[pin]
+    inner: Mutex<CmdqInner>,
 }
 
 impl Cmdq {
@@ -502,18 +503,17 @@ impl Cmdq {
     /// Number of page table entries for the GSP shared region.
     pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
 
-    /// Timeout for waiting for space on the command queue.
-    const ALLOCATE_TIMEOUT: Delta = Delta::from_secs(1);
-
     /// Default timeout for receiving a message from the GSP.
     pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5);
 
     /// 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,
+            inner <- new_mutex!(CmdqInner {
+                dev: dev.into(),
+                gsp_mem: DmaGspMem::new(dev)?,
+                seq: 0,
+            }),
         })
     }
 
@@ -537,6 +537,87 @@ fn notify_gsp(bar: &Bar0) {
             .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_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(bar, command)?;
+
+        loop {
+            match inner.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
+                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_command_no_wait<M>(&self, bar: &Bar0, command: M) -> Result
+    where
+        M: CommandToGsp<Reply = NoReply>,
+        Error: From<M::InitError>,
+    {
+        self.inner.lock().send_command(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(timeout)
+    }
+
+    /// Returns the DMA handle of the command queue's shared memory region.
+    pub(crate) fn dma_handle(&self) -> DmaAddress {
+        self.inner.lock().gsp_mem.0.dma_handle()
+    }
+}
+
+/// Inner mutex protected state of [`Cmdq`].
+struct CmdqInner {
+    /// Device this command queue belongs to.
+    dev: ARef<device::Device>,
+    /// Current command sequence number.
+    seq: u32,
+    /// Memory area shared with the GSP for communicating commands and messages.
+    gsp_mem: DmaGspMem,
+}
+
+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.
     ///
     /// # Errors
@@ -617,7 +698,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.
-    fn send_command_internal<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>,
@@ -637,51 +718,6 @@ fn send_command_internal<M>(&mut self, bar: &Bar0, command: M) -> Result
         }
     }
 
-    /// 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_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_internal(bar, command)?;
-
-        loop {
-            match self.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
-                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_command_no_wait<M>(&mut self, bar: &Bar0, command: M) -> Result
-    where
-        M: CommandToGsp<Reply = NoReply>,
-        Error: From<M::InitError>,
-    {
-        self.send_command_internal(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
@@ -717,7 +753,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,
+            &self.dev,
             "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",
             header.sequence(),
             header.function(),
@@ -752,7 +788,7 @@ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
         ])) != 0
         {
             dev_err!(
-                self.dev,
+                &self.dev,
                 "GSP RPC: receive: Call {} - bad checksum\n",
                 header.sequence()
             );
@@ -781,7 +817,7 @@ 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, timeout: Delta) -> Result<M>
     where
         // This allows all error types, including `Infallible`, to be used for `M::InitError`.
         Error: From<M::InitError>,
@@ -817,9 +853,4 @@ pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Resul
 
         result
     }
-
-    /// 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()
-    }
 }
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index 77054c92fcc2..c89c7b57a751 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -165,7 +165,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>(Cmdq::RECEIVE_TIMEOUT) {
             Ok(_) => break Ok(()),
@@ -234,6 +234,6 @@ 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_command(bar, GetGspStaticInfo)
 }
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index ce2b3bb05d22..474e4c8021db 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>(Cmdq::RECEIVE_TIMEOUT) {
                 Ok(seq_info) => break seq_info,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add locking to Cmdq
  2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
                   ` (4 preceding siblings ...)
  2026-03-10  8:09 ` [PATCH v4 5/5] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney
@ 2026-03-11  3:32 ` Claude Code Review Bot
  5 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 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-03-11T13:32:09.489388

---

This is a well-structured 5-patch series that incrementally adds mutex locking to the `Cmdq` (command queue) in the nova-core GSP driver. The series follows a clean decomposition: (1) fix doc comments, (2) extract a timeout constant, (3) add reply/no-reply type infrastructure, (4) make `Cmdq` pinned, (5) add the mutex. The progression is logical, each patch builds on the prior ones, and the final result is clean.

The series is already at v4 and has Reviewed-by/Tested-by tags from Zhi Wang and Gary Guo on several patches. The approach of using an associated `Reply` type on `CommandToGsp` to distinguish reply-expecting vs fire-and-forget commands is a good Rust-idiomatic design choice. The locking strategy of holding the mutex over both send and receive in `send_command` is correct for ensuring reply ordering.

Overall this looks ready to merge with only minor observations below.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: fix stale doc comments on command queue methods
  2026-03-10  8:09 ` [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
@ 2026-03-11  3:32   ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

Clean documentation-only patch. Updates doc comments on `send_command` and `receive_msg` to match current behavior.

The change from `EAGAIN` to `ERANGE` in the doc comments for non-matching messages aligns with what appears to be an earlier refactoring:

```rust
+    /// - `ERANGE` if the message had a recognized but non-matching function code.
```

And the addition of the `EMSGSIZE` error documentation for `send_command`:
```rust
+    /// - `EMSGSIZE` if the command exceeds the maximum queue element size.
```

This is a good cleanup. Note that the existing tree's `send_command` doc mentions `EAGAIN` for insufficient space (line 487 of the current tree), while this patch's context shows `ETIMEDOUT` -- this implies the prerequisite series already changed the space-waiting behavior to use a polling timeout, which is consistent.

No issues.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue
  2026-03-10  8:09 ` [PATCH v4 2/5] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue Eliot Courtney
@ 2026-03-11  3:32   ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

Straightforward extraction of magic timeout values into a named constant:

```rust
+    pub(super) const RECEIVE_TIMEOUT: Delta = Delta::from_secs(5);
```

The previous code used `Delta::from_secs(10)` in two places and `Delta::from_secs(5)` in one place. This patch unifies all three to 5 seconds (per the v4 changelog). The `wait_gsp_init_done` and `GspSequencer::run` callers were previously using 10s timeouts; reducing them to 5s is a behavioral change. Given the cover letter mentions GSP is expected to be fast and v4 explicitly changed this from 10s to 5s, this seems intentional and reasonable.

The visibility `pub(super)` is appropriate since it's used across sibling modules within `gsp/`.

No issues.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp`
  2026-03-10  8:09 ` [PATCH v4 3/5] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp` Eliot Courtney
@ 2026-03-11  3:32   ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This is the key design patch. It introduces:

1. A `NoReply` marker type for commands with no expected response
2. An associated `type Reply` on `CommandToGsp`
3. Splits the public API into `send_command` (waits for reply) and `send_command_no_wait` (fire-and-forget)

The type-level enforcement is well done:

```rust
+    pub(crate) fn send_command_no_wait<M>(&mut self, bar: &Bar0, command: M) -> Result
+    where
+        M: CommandToGsp<Reply = NoReply>,
```

This constrains `send_command_no_wait` to only accept commands whose `Reply` is `NoReply`, which prevents accidentally forgetting to wait for a reply.

The `send_command` method absorbs the receive loop pattern that was previously duplicated in `get_gsp_info`:

```rust
+        loop {
+            match self.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
+                Ok(reply) => break Ok(reply),
+                Err(ERANGE) => continue,
+                Err(e) => break Err(e),
+            }
+        }
```

This simplifies `get_gsp_info` to a single line, which is nice. The `SplitCommand` wrapper correctly propagates `type Reply = C::Reply`, and `ContinuationRecord` correctly uses `NoReply`.

One observation: the `wait_gsp_init_done` function still has its own manual receive loop in `commands.rs` since `GspInitDone` isn't triggered by a specific command -- it's an unsolicited notification. This is correct; it cannot use `send_command`.

The `// ` trailing comment on the import formatting (e.g., `NoReply, //`) is the `rustfmt` skip trick to force vertical formatting -- standard practice in the kernel Rust code.

No issues.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: make `Cmdq` a pinned type
  2026-03-10  8:09 ` [PATCH v4 4/5] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney
@ 2026-03-11  3:32   ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

Mechanical conversion of `Cmdq` from a regular type to a pinned type using `#[pin_data]` and `try_pin_init!`:

```rust
+    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,
         })
```

The `Gsp` struct update correctly uses the `<-` pin-init operator:
```rust
-                cmdq: Cmdq::new(dev)?,
+                cmdq <- Cmdq::new(dev),
```

And the `GspArgumentsCached::new` call changes from passing `cmdq` (which was previously `Cmdq` by value-ish) to `&cmdq`:
```rust
-                    dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?;
+                    dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(&cmdq))?;
```

This is clean and correct preparation for the mutex in the next patch.

No issues.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Claude review: gpu: nova-core: gsp: add mutex locking to Cmdq
  2026-03-10  8:09 ` [PATCH v4 5/5] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney
@ 2026-03-11  3:32   ` Claude Code Review Bot
  0 siblings, 0 replies; 15+ messages in thread
From: Claude Code Review Bot @ 2026-03-11  3:32 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This is the main event. Key design decisions:

1. **All mutable state moved to `CmdqInner`** wrapped in `Mutex<CmdqInner>`:
```rust
pub(crate) struct Cmdq {
+    #[pin]
+    inner: Mutex<CmdqInner>,
}
```

2. **`Cmdq` methods now take `&self`** instead of `&mut self`, enabling shared access from multiple threads.

3. **The mutex is held across send+receive in `send_command`**:
```rust
+        let mut inner = self.inner.lock();
+        inner.send_command(bar, command)?;
+        loop {
+            match inner.receive_msg::<M::Reply>(Self::RECEIVE_TIMEOUT) {
```

This is correct for ensuring reply ordering -- no other command can slip in between send and receive.

4. **`dma_handle()` now acquires the mutex**:
```rust
+    pub(crate) fn dma_handle(&self) -> DmaAddress {
+        self.inner.lock().gsp_mem.0.dma_handle()
+    }
```

This is called during init from `MessageQueueInitArguments::new()`. Since `dma_handle()` returns an immutable property (the DMA address doesn't change after allocation), acquiring the mutex here is unnecessary overhead but harmless. If this becomes a hot path in the future, it could be worth storing the DMA handle separately outside the mutex, but for now this is fine.

5. **`boot()` signature change** drops `mut` from `Pin<&mut Self>`:
```rust
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
```

This is correct since `cmdq` methods no longer need `&mut self`.

6. **Potential concern -- holding mutex during `receive_msg` timeout**: The `receive_msg` call inside `send_command` can block for up to `RECEIVE_TIMEOUT` (5 seconds) while holding the mutex lock. During this time, no other thread can send commands or receive messages. The cover letter acknowledges this: "For now this should be ok, and we expect GSP to be fast anyway." This is acceptable for the current use case but worth noting for future scaling.

7. **The `receive_msg` public wrapper also acquires the lock**:
```rust
+    pub(crate) fn receive_msg<M: MessageFromGsp>(&self, timeout: Delta) -> Result<M>
+    ...
+        self.inner.lock().receive_msg(timeout)
```

This is used by `wait_gsp_init_done` and `GspSequencer::run`, which call `receive_msg` in loops. Each iteration will acquire and release the lock, which is fine -- it allows other threads to interleave between iterations.

The `ALLOCATE_TIMEOUT` constant correctly moved from `Cmdq` to `CmdqInner` since it's only used by `CmdqInner::send_single_command`.

The `dev_dbg!`/`dev_err!` changes from `self.dev` to `&self.dev` are because `dev` is now `ARef<device::Device>` inside `CmdqInner` and the macro needs a reference.

No blocking issues. The series is clean and well-decomposed.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-03-11  3:32 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-10  8:09 [PATCH v4 0/5] gpu: nova-core: gsp: add locking to Cmdq Eliot Courtney
2026-03-10  8:09 ` [PATCH v4 1/5] gpu: nova-core: gsp: fix stale doc comments on command queue methods Eliot Courtney
2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
2026-03-10  8:09 ` [PATCH v4 2/5] gpu: nova-core: gsp: add `RECEIVE_TIMEOUT` constant for command queue Eliot Courtney
2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
2026-03-10  8:09 ` [PATCH v4 3/5] gpu: nova-core: gsp: add reply/no-reply info to `CommandToGsp` Eliot Courtney
2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
2026-03-10  8:09 ` [PATCH v4 4/5] gpu: nova-core: gsp: make `Cmdq` a pinned type Eliot Courtney
2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
2026-03-10  8:09 ` [PATCH v4 5/5] gpu: nova-core: gsp: add mutex locking to Cmdq Eliot Courtney
2026-03-11  3:32   ` Claude review: " Claude Code Review Bot
2026-03-11  3:32 ` Claude review: gpu: nova-core: gsp: add " Claude Code Review Bot
  -- strict thread matches above, loose matches on Subject: below --
2026-03-04  2:46 [PATCH v3 0/5] " Eliot Courtney
2026-03-05  3:52 ` Claude review: " Claude Code Review Bot
2026-02-26 14:50 [PATCH v2 0/4] " Eliot Courtney
2026-02-27  2:04 ` Claude review: " Claude Code Review Bot
2026-02-25 13:41 [PATCH 0/4] " 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