Skip to contentSkip to conversation

net: mana: concurrent HWC requests and dynamic queue depth

PATCH New Wei Hu 2 participants 9 messages Nov 9 – Nov 10

Subsystems (5)
lore
Share & tools
lore

Wei Hu

author
#0 lore raw

v10 cover net: mana: concurrent HWC requests and dynamic queue depth

This is v10 of Long Li's four-patch HWC series, rebased for the reopened net-next tree. I am carrying the series forward with review fixes and integration work. Original authorship and sign-offs are retained.

The series supports concurrent requests over the MANA hardware channel and rebuilds its queues using the queue depth and message sizes required by the PF. It prepares ownership and teardown, gives each message its own completion, adds concurrent request tracking, and enables dynamic sizing.

v9 was deferred while net-next was closed. This revision resolves its conflicts with the upstream throttled-EQE support; it is not an unchanged RESEND. No new actionable v9 review finding was located during the reposting check.

Changes since v9:
- Rebase onto current net-next, including the throttled-EQE support from 684603842b78 ("net: mana: Add support to process throttled EQEs").
- In patch 1, use one acquire-loaded CQ-table snapshot in the new throttle reader, matching the existing normal-EQE reader and HWC table-withdrawal lifetime contract.
- In patch 1, fence the HWC EQ before destroying its CQ, but keep the EQ allocation alive until the CQ has detached its upstream child entry. Preserve the upstream generic CQ detach/RCU implementation and allowance for empty HWC CQ polls.
- Refresh patch 4's context while preserving the upstream doorbell client_offset argument and address adjustment.
- Patches 2 and 3 retain their v9 changes. PF-directed queue geometry, healthy-admission waiting and timeout/cancellation policy are unchanged.

Validation on this exact revision:
- GCC and Clang W=1 MANA Ethernet/RDMA target builds for the unmodified base and each of the four intermediate commits.
- Strict netdev checkpatch with an 80-column limit and whitespace checks.
- Bounded actual-source userspace regression tests for request ownership, admission, retention, teardown, initialization, rebuild and TX budgets.
- New GCC/Clang ASan/UBSan tests of the throttle reader and CQ/EQ cleanup on patches 1 and 4, including partial setup, prior fencing, table withdrawal and negative controls for the integration changes.
- Source review of ownership, IRQ/RCU readers and failure paths.

These are static/offline checks. No new full-kernel, hardware, runtime or performance qualification is claimed for this rebase.

Previous revision:
https://lore.kernel.org/all/[email protected]/

Deferral while net-next was closed:
https://lore.kernel.org/all/[email protected]/

Base: fa5196392691051963726787944db872420dfa75

Long Li (4):
net: mana: prepare HWC ownership for safe reinitialization
net: mana: give each HWC message slot its own completion state
net: mana: support concurrent HWC requests
net: mana: add dynamic HWC queue depth with reinit path

 drivers/infiniband/hw/mana/main.c             |   19 +-
 .../net/ethernet/microsoft/mana/gdma_cdx.c    |    5 +-
 .../net/ethernet/microsoft/mana/gdma_main.c   |  162 ++-
 .../net/ethernet/microsoft/mana/gdma_pci.c    |   15 +-
 .../net/ethernet/microsoft/mana/hw_channel.c  | 1022 ++++++++++++++---
 .../net/ethernet/microsoft/mana/shm_channel.c |   58 +-
 include/net/mana/gdma.h                       |   17 +
 include/net/mana/hw_channel.h                 |   70 +-
 include/net/mana/shm_channel.h                |   10 +-
 9 files changed, 1157 insertions(+), 221 deletions(-)

base-commit: fa5196392691051963726787944db872420dfa75

Signature · 1 line
2.43.0
author
#1 lore raw

PATCH v10 1/4 net: mana: prepare HWC ownership for safe reinitialization

From: Long Li <[email protected]>

Dynamic HWC queue sizing tears down the bootstrap queues and establishes a second channel before publishing it. Prepare the existing HWC ownership and teardown paths so that sequence cannot free or reuse state still reachable by either the PF or an EQ handler.

The shared-memory aperture carries one request at a time. Serialize the complete ESTABLISH_HWC and DESTROY_HWC transactions, including possession polling and response validation. Record setup_active immediately before the MMIO submission and clear it only after DESTROY_HWC is acknowledged.

Treat an unacknowledged destroy as retained PF ownership. Fence the HWC EQ and withdraw its CQ from dispatch, but keep all queue memory and wrappers for a later teardown retry. A new channel creation retries that retained context before assigning fresh queues to the PF.

When a PCI or CDX transport permanently discards its gdma_context, no such retry is possible. After IRQ and service-work teardown, release the software-only HWC state through a common final-disposal helper. Leave the coherent DMA allocations unreclaimed rather than exposing freed memory to a PF whose teardown was not acknowledged.

Snapshot the bootstrap CQ count and CQ ID after INIT_DONE. Publish the fixed bound with the allocated table so later init events cannot grow the bound past the allocation. Use a single acquire-loaded table snapshot in both normal and throttled EQE dispatch, with annotated CQ-slot reads.

On acknowledged teardown, withdraw the CQ table and wait for all existing IRQ-side RCU readers before freeing the HWC queue bundle. This also covers a completion dispatched through a different EQ, without a per-event reference. Fence the dedicated EQ's remaining callbacks before releasing the CQ, completion buffer and work queues. Keep the parent EQ allocated until CQ destruction has detached its throttled-EQE child entry. Preserve the existing generic CQ detach and RCU handling.

Copilot-Session: 8c2361e6-c0f4-4b0c-bf01-b5e4cdacec50

---
 .../net/ethernet/microsoft/mana/gdma_cdx.c    |   4 +-
 .../net/ethernet/microsoft/mana/gdma_main.c   |  41 +++-
 .../net/ethernet/microsoft/mana/gdma_pci.c    |   4 +-
 .../net/ethernet/microsoft/mana/hw_channel.c  | 187 +++++++++++++++---
 .../net/ethernet/microsoft/mana/shm_channel.c |  58 +++++-
 include/net/mana/gdma.h                       |   4 +
 include/net/mana/hw_channel.h                 |   5 +
 include/net/mana/shm_channel.h                |  10 +-
 8 files changed, 275 insertions(+), 38 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_cdx.c +2 -2
index 0d01e3d2f0d9..2e0dcb902d7e 100644
@@ -279,7 +279,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)
 	iounmap(bar0_va);
 free_gc:
 	dev_set_drvdata(&cdx_dev->dev, NULL);
-	vfree(gc);
+	mana_gd_free_context(gc);
 clear_master:
 	cdx_clear_master(cdx_dev);
 	return err;
@@ -298,7 +298,7 @@ static int mana_cdx_gd_remove(struct cdx_device *cdx_dev)
 	iounmap(gc->bar0_va);
 
 	dev_set_drvdata(&cdx_dev->dev, NULL);
-	vfree(gc);
+	mana_gd_free_context(gc);
 	return 0;
 }
 
drivers/net/ethernet/microsoft/mana/gdma_main.c +35 -6

103 lines not on this page · raw message

drivers/net/ethernet/microsoft/mana/gdma_pci.c +2 -2
index ed8925296c8a..7935cb274e36 100644
@@ -725,7 +725,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	pci_iounmap(pdev, bar0_va);
 free_gc:
 	pci_set_drvdata(pdev, NULL);
-	vfree(gc);
+	mana_gd_free_context(gc);
 release_region:
 	pci_release_regions(pdev);
 disable_dev:
@@ -778,7 +778,7 @@ static void mana_gd_remove(struct pci_dev *pdev)
 
 	pci_iounmap(pdev, gc->bar0_va);
 
-	vfree(gc);
+	mana_gd_free_context(gc);
 
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
drivers/net/ethernet/microsoft/mana/hw_channel.c +165 -22

277 lines not on this page · raw message

drivers/net/ethernet/microsoft/mana/shm_channel.c +54 -4

95 lines not on this page · raw message

include/net/mana/gdma.h +4 -0
index 29d152e18596..af62cd6d771e 100644
@@ -596,6 +596,9 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd,
 
 void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue);
 
+/* Remove an EQ from interrupt dispatch without freeing its queue memory. */
+void mana_gd_fence_eq(struct gdma_context *gc, struct gdma_queue *queue);
+
 int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe);
 
 void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit);
@@ -1146,6 +1149,7 @@ int mana_gd_verify_vf_version(struct gdma_context *gc);
 int mana_gd_query_max_resources(struct gdma_context *gc);
 int mana_gd_setup(struct gdma_context *gc);
 void mana_gd_cleanup(struct gdma_context *gc);
+void mana_gd_free_context(struct gdma_context *gc);
 int mana_gd_detect_devices(struct gdma_context *gc);
 int mana_gd_init_registers(struct gdma_context *gc);
 irqreturn_t mana_gd_intr(int irq, void *arg);
include/net/mana/hw_channel.h +5 -0
index 1ab2d66c891e..1320bdfa9d2a 100644
@@ -185,6 +185,8 @@ struct hw_channel_context {
 	u16 hwc_init_q_depth_max;
 	u32 hwc_init_max_req_msg_size;
 	u32 hwc_init_max_resp_msg_size;
+	u32 hwc_init_max_num_cqs;
+	u32 hwc_init_cq_id;
 
 	struct completion hwc_init_eqe_comp;
 
@@ -199,11 +201,14 @@ struct hw_channel_context {
 	u32 dest_vrcq_id;
 	u32 hwc_timeout;
 
+	/* The PF may own the HWC queues while this is true. */
+	bool setup_active;
 	struct hwc_caller_ctx *caller_ctx;
 };
 
 int mana_hwc_create_channel(struct gdma_context *gc);
 void mana_hwc_destroy_channel(struct gdma_context *gc);
+void mana_hwc_free_retained_channel(struct gdma_context *gc);
 
 int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 			  const void *req, u32 resp_len, void *resp);
include/net/mana/shm_channel.h +8 -2
index dbabcfb95daf..c8daa61fa759 100644
@@ -4,6 +4,8 @@
 #ifndef _SHM_CHANNEL_H
 #define _SHM_CHANNEL_H
 
+#include <linux/mutex.h>
+
 #define SMC_APERTURE_BITS 256
 #define SMC_BASIC_UNIT (sizeof(u32))
 #define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8))
@@ -13,6 +15,9 @@
 struct shm_channel {
 	struct device *dev;
 	void __iomem *base;
+	/* Protects base and complete aperture request/response transactions. */
+	struct mutex transaction_lock;
+	bool transaction_lock_initialized;
 };
 
 void mana_smc_init(struct shm_channel *sc, struct device *dev,
@@ -20,8 +25,9 @@ void mana_smc_init(struct shm_channel *sc, struct device *dev,
 
 int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,
 		       u64 cq_addr, u64 rq_addr, u64 sq_addr,
-		       u32 eq_msix_index);
+		       u32 eq_msix_index, bool *submitted);
 
-int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf);
+int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf,
+			  bool *setup_active);
 
 #endif /* _SHM_CHANNEL_H */
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH net-next v10 1/4] net: mana: prepare HWC ownership for safe reinitialization

Sashiko has reviewed this patch and found no issues.
Quoted 15 lines

From: Long Li <[email protected]>

Dynamic HWC queue sizing tears down the bootstrap queues and establishes a second channel before publishing it. Prepare the existing HWC ownership and teardown paths so that sequence cannot free or reuse state still reachable by either the PF or an EQ handler.

The shared-memory aperture carries one request at a time. Serialize the complete ESTABLISH_HWC and DESTROY_HWC transactions, including possession polling and response validation. Record setup_active immediately before the MMIO submission and clear it only after DESTROY_HWC is acknowledged.

Treat an unacknowledged destroy as retained PF ownership. Fence the HWC EQ and withdraw its CQ from dispatch, but keep all queue memory and wrappers for a later teardown retry. A new channel creation retries that retained

Quoted 6 lines

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
author
#3 lore raw

PATCH v10 2/4 net: mana: give each HWC message slot its own completion state

In reply to Wei Hu

From: Long Li <[email protected]>

Concurrent requests require the response handler and the waiting sender to agree on the lifetime and result state of each message slot.

Protect the caller's output buffer, Linux error and device status with a per-slot lock. Publish the output buffer only while the sender is waiting and withdraw it before every return. A late response cannot copy through a pointer to caller storage whose lifetime has ended.

Reinitialize completion and result state under both the allocation and per-slot locks when acquiring the slot. A response handler can still hold the per-slot lock after the previous sender has returned its admission permit, so an unpublished bitmap bit alone is not sufficient protection. If the response records its result while the timed wait expires, return that result instead of reporting a false timeout.

Keep the baseline's post-timeout admission behavior at this step rather than introducing a temporary channel-wide latch that suppresses subsequent teardown commands. This does not solve late-response correlation after ID reuse. The next patch adds response ownership, per-slot quarantine and the associated admission and teardown rules together.

Split response-status handling and request cleanup into helpers so the send path has structured returns rather than phase-jumping gotos.

Copilot-Session: 8c2361e6-c0f4-4b0c-bf01-b5e4cdacec50

---
 .../net/ethernet/microsoft/mana/hw_channel.c  | 125 +++++++++++++-----
 include/net/mana/hw_channel.h                 |   4 +-
 2 files changed, 95 insertions(+), 34 deletions(-)
drivers/net/ethernet/microsoft/mana/hw_channel.c +92 -33
index 9e0964072af9..6f0ccdeffc4f 100644
@@ -9,6 +9,7 @@
 static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)
 {
 	struct gdma_resource *r = &hwc->inflight_msg_res;
+	struct hwc_caller_ctx *ctx;
 	unsigned long flags;
 	u32 index;
 
@@ -19,8 +20,17 @@ static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)
 	index = find_first_zero_bit(hwc->inflight_msg_res.map,
 				    hwc->inflight_msg_res.size);
 
+	ctx = &hwc->caller_ctx[index];
+	spin_lock(&ctx->lock);
+	reinit_completion(&ctx->comp_event);
+	ctx->output_buf = NULL;
+	ctx->output_buflen = 0;
+	ctx->error = -EINPROGRESS;
+	ctx->status_code = 0;
+
 	bitmap_set(hwc->inflight_msg_res.map, index, 1);
 
+	spin_unlock(&ctx->lock);
 	spin_unlock_irqrestore(&r->lock, flags);
 
 	*msg_id = index;
@@ -90,14 +100,21 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
 	}
 
 	ctx = hwc->caller_ctx + msg_id;
-	err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
-	if (err)
-		goto out;
 
-	ctx->status_code = resp_msg->status;
+	spin_lock(&ctx->lock);
+	if (!ctx->output_buf) {
+		spin_unlock(&ctx->lock);
+		mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
+		return;
+	}
 
-	memcpy(ctx->output_buf, resp_msg, resp_len);
-out:
+	err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
+	if (!err) {
+		ctx->status_code = resp_msg->status;
+		memcpy(ctx->output_buf, resp_msg, resp_len);
+	}
+
+	ctx->output_buf = NULL;
 	ctx->error = err;
 
 	/* Must post rx wqe before complete(), otherwise the next rx may
@@ -106,6 +123,7 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
 	mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
 
 	complete(&ctx->comp_event);
+	spin_unlock(&ctx->lock);
 }
 
 static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
@@ -696,8 +714,10 @@ static int mana_hwc_test_channel(struct hw_channel_context *hwc, u16 q_depth,
 	if (!ctx)
 		return -ENOMEM;
 
-	for (i = 0; i < q_depth; ++i)
+	for (i = 0; i < q_depth; ++i) {
 		init_completion(&ctx[i].comp_event);
+		spin_lock_init(&ctx[i].lock);
+	}
 
 	hwc->caller_ctx = ctx;
 
@@ -1000,6 +1020,37 @@ void mana_hwc_free_retained_channel(struct gdma_context *gc)
 	gc->hwc.gdma_context = NULL;
 }
 
+static int mana_hwc_response_status(struct hw_channel_context *hwc,
+				    u32 command, int error, u32 status)
+{
+	if (error)
+		return error;
+
+	if (!status || status == GDMA_STATUS_MORE_ENTRIES)
+		return 0;
+
+	if (status == GDMA_STATUS_CMD_UNSUPPORTED)
+		return -EOPNOTSUPP;
+
+	if (command != MANA_QUERY_PHY_STAT)
+		dev_err(hwc->dev, "Command 0x%x failed with status: 0x%x\n",
+			command, status);
+
+	return -EPROTO;
+}
+
+static void mana_hwc_finish_request(struct hw_channel_context *hwc,
+				    struct hwc_caller_ctx *ctx, u16 msg_id)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctx->lock, flags);
+	ctx->output_buf = NULL;
+	spin_unlock_irqrestore(&ctx->lock, flags);
+
+	mana_hwc_put_msg_index(hwc, msg_id);
+}
+
 int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 			  const void *req, u32 resp_len, void *resp)
 {
@@ -1007,26 +1058,32 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 	struct hwc_wq *txq = hwc->txq;
 	struct gdma_req_hdr *req_msg;
 	struct hwc_caller_ctx *ctx;
+	unsigned long flags;
 	u32 dest_vrcq;
 	u32 dest_vrq;
 	u32 command;
+	u32 status;
 	u16 msg_id;
 	int err;
 
-	mana_hwc_get_msg_index(hwc, &msg_id);
+	err = mana_hwc_get_msg_index(hwc, &msg_id);
+	if (err)
+		return err;
 
 	tx_wr = &txq->msg_buf->reqs[msg_id];
+	ctx = hwc->caller_ctx + msg_id;
 
 	if (req_len > tx_wr->buf_len) {
 		dev_err(hwc->dev, "HWC: req msg size: %d > %d\n", req_len,
 			tx_wr->buf_len);
-		err = -EINVAL;
-		goto out;
+		mana_hwc_finish_request(hwc, ctx, msg_id);
+		return -EINVAL;
 	}
 
-	ctx = hwc->caller_ctx + msg_id;
+	spin_lock_irqsave(&ctx->lock, flags);
 	ctx->output_buf = resp;
 	ctx->output_buflen = resp_len;
+	spin_unlock_irqrestore(&ctx->lock, flags);
 
 	req_msg = (struct gdma_req_hdr *)tx_wr->buf_va;
 	if (req)
@@ -1048,11 +1105,24 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 	err = mana_hwc_post_tx_wqe(txq, tx_wr, dest_vrq, dest_vrcq, false);
 	if (err) {
 		dev_err(hwc->dev, "HWC: Failed to post send WQE: %d\n", err);
-		goto out;
+		mana_hwc_finish_request(hwc, ctx, msg_id);
+		return err;
 	}
 
 	if (!wait_for_completion_timeout(&ctx->comp_event,
-					 (msecs_to_jiffies(hwc->hwc_timeout)))) {
+					 msecs_to_jiffies(hwc->hwc_timeout))) {
+		spin_lock_irqsave(&ctx->lock, flags);
+		ctx->output_buf = NULL;
+		err = ctx->error;
+		status = ctx->status_code;
+		spin_unlock_irqrestore(&ctx->lock, flags);
+
+		if (err != -EINPROGRESS) {
+			mana_hwc_finish_request(hwc, ctx, msg_id);
+			return mana_hwc_response_status(hwc, command, err,
+							status);
+		}
+
 		if (hwc->hwc_timeout != 0)
 			dev_err(hwc->dev, "Command 0x%x timed out: %u ms\n",
 				command, hwc->hwc_timeout);
@@ -1061,27 +1131,16 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 		if (hwc->hwc_timeout > 1)
 			hwc->hwc_timeout = 1;
 
-		err = -ETIMEDOUT;
-		goto out;
+		mana_hwc_finish_request(hwc, ctx, msg_id);
+		return -ETIMEDOUT;
 	}
 
-	if (ctx->error) {
-		err = ctx->error;
-		goto out;
-	}
+	spin_lock_irqsave(&ctx->lock, flags);
+	ctx->output_buf = NULL;
+	err = ctx->error;
+	status = ctx->status_code;
+	spin_unlock_irqrestore(&ctx->lock, flags);
 
-	if (ctx->status_code && ctx->status_code != GDMA_STATUS_MORE_ENTRIES) {
-		if (ctx->status_code == GDMA_STATUS_CMD_UNSUPPORTED) {
-			err = -EOPNOTSUPP;
-			goto out;
-		}
-		if (command != MANA_QUERY_PHY_STAT)
-			dev_err(hwc->dev, "Command 0x%x failed with status: 0x%x\n",
-				command, ctx->status_code);
-		err = -EPROTO;
-		goto out;
-	}
-out:
-	mana_hwc_put_msg_index(hwc, msg_id);
-	return err;
+	mana_hwc_finish_request(hwc, ctx, msg_id);
+	return mana_hwc_response_status(hwc, command, err, status);
 }
include/net/mana/hw_channel.h +3 -1
index 1320bdfa9d2a..c7570a5f7376 100644
@@ -168,10 +168,12 @@ struct hwc_wq {
 
 struct hwc_caller_ctx {
 	struct completion comp_event;
+	/* Protects initialization, output storage and response state. */
+	spinlock_t lock;
 	void *output_buf;
 	u32 output_buflen;
 
-	u32 error; /* Linux error code */
+	int error; /* Linux error code */
 	u32 status_code;
 };
 
Signature · 1 line
2.43.0
#4 lore raw

Re: [PATCH net-next v10 2/4] net: mana: give each HWC message slot its own completion state

Sashiko has reviewed this patch and found no issues.
Quoted 15 lines

From: Long Li <[email protected]>

Concurrent requests require the response handler and the waiting sender to agree on the lifetime and result state of each message slot.

Protect the caller's output buffer, Linux error and device status with a per-slot lock. Publish the output buffer only while the sender is waiting and withdraw it before every return. A late response cannot copy through a pointer to caller storage whose lifetime has ended.

Reinitialize completion and result state under both the allocation and per-slot locks when acquiring the slot. A response handler can still hold the per-slot lock after the previous sender has returned its admission permit, so an unpublished bitmap bit alone is not sufficient protection. If the response records its result while the timed wait expires, return

Quoted 6 lines

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author
#5 lore raw

PATCH v10 3/4 net: mana: support concurrent HWC requests

In reply to Wei Hu

From: Long Li <[email protected]>

Give HWC message slots separate sender and response ownership. Initialize each slot under the inflight resource and per-slot locks, publish its bitmap bit last, and release the slot only after both references are released. The per-slot lock also excludes an older duplicate callback from resetting or consuming the next caller's state.

Serialize SQ posting so concurrent senders cannot corrupt the producer state. A posted request which times out keeps its response reference and slot until its late response arrives or teardown cancels it. The bootstrap channel remains depth one here; the next patch selects and allocates larger queues.

Wait for a free slot while the channel is healthy instead of imposing a separate admission deadline. A congestion-only -EBUSY can make destructive callers skip a teardown command and still free its DMA memory. Keeping healthy callers queued avoids introducing that failure into the existing cleanup paths. The response wait of an admitted command remains bounded.

Wake admission waiters when a slot is released, a response is abandoned, waits are cancelled or the channel stops. If no slot is available, return -ETIMEDOUT for outstanding abandoned responses or cancelled waits, and -ENODEV for a stopped channel. This also handles waiters which arrived before abandonment was recorded, without mistaking healthy contention for a failed command. Retire the abandoned-response count with its response reference exactly once.

Keep available slots usable for best-effort commands when waits have been cancelled, preserving the existing cleanup policy. Suppress expected timeout/device-gone logging after cancellation or unpublication, but keep logging live-channel failures and other error classes. This does not repair the wider data-queue DMA policy after a real control-command failure or establish device quiescence.

Synchronize hwc_timeout updates with atomic access helpers. Cancellation is terminal for a channel instance; firmware and query updates may replace only a live timeout, and fail-fast may only lower a live value to one millisecond.

Keep setup private until queues, admission state, caller contexts and the direct EQ test are ready. Runtime teardown unpublishes the channel, rejects new admissions, wakes waiters and drains active senders before releasing or retaining PF-owned mappings. Apply the same unpublication and sender-drain rules to final disposal of retained software state.

Split admission, submission, waiting, response status and unwind into helpers so mana_hwc_send_request() uses structured returns.

Copilot-Session: 8c2361e6-c0f4-4b0c-bf01-b5e4cdacec50

---
 .../net/ethernet/microsoft/mana/gdma_cdx.c    |   1 +
 .../net/ethernet/microsoft/mana/gdma_main.c   |  95 ++-
 .../net/ethernet/microsoft/mana/gdma_pci.c    |  11 +-
 .../net/ethernet/microsoft/mana/hw_channel.c  | 598 +++++++++++++-----
 include/net/mana/gdma.h                       |  11 +
 include/net/mana/hw_channel.h                 |  46 +-
 6 files changed, 574 insertions(+), 188 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_cdx.c +1 -0
index 2e0dcb902d7e..fa10681fd793 100644
@@ -221,6 +221,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)
 
 	mutex_init(&gc->eq_test_event_mutex);
 	mutex_init(&gc->gic_mutex);
+	spin_lock_init(&gc->hwc_lock);
 	dev_set_drvdata(&cdx_dev->dev, gc);
 	gc->bar0_pa = cdx_resource_start(cdx_dev, 0);
 	gc->bar0_size = cdx_resource_len(cdx_dev, 0);
drivers/net/ethernet/microsoft/mana/gdma_main.c +81 -14

181 lines not on this page · raw message

drivers/net/ethernet/microsoft/mana/gdma_pci.c +4 -7
index 7935cb274e36..636d9b0dd581 100644
@@ -14,7 +14,6 @@
 #include <linux/xarray.h>
 
 #include <net/mana/mana.h>
-#include <net/mana/hw_channel.h>
 
 struct mana_dev_recovery {
 	struct list_head list;
@@ -85,7 +84,6 @@ static void mana_serv_fpga(struct pci_dev *pdev)
 static void mana_serv_reset(struct pci_dev *pdev)
 {
 	struct gdma_context *gc = pci_get_drvdata(pdev);
-	struct hw_channel_context *hwc;
 	int ret;
 
 	if (!gc) {
@@ -95,15 +93,13 @@ static void mana_serv_reset(struct pci_dev *pdev)
 		return;
 	}
 
-	hwc = gc->hwc.driver_data;
-	if (!hwc) {
+	/* HWC is not responding in this case, so don't wait */
+	ret = mana_gd_cancel_hwc_timeout(gc);
+	if (ret) {
 		dev_err(&pdev->dev, "MANA service: no HWC\n");
 		goto out;
 	}
 
-	/* HWC is not responding in this case, so don't wait */
-	hwc->hwc_timeout = 0;
-
 	dev_info(&pdev->dev, "MANA reset cycle start\n");
 
 	mana_gd_suspend(pdev, PMSG_SUSPEND);
@@ -674,6 +670,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	mutex_init(&gc->eq_test_event_mutex);
 	mutex_init(&gc->gic_mutex);
+	spin_lock_init(&gc->hwc_lock);
 	pci_set_drvdata(pdev, gc);
 	gc->bar0_pa = pci_resource_start(pdev, 0);
 	gc->bar0_size = pci_resource_len(pdev, 0);
drivers/net/ethernet/microsoft/mana/hw_channel.c +435 -163

830 lines not on this page · raw message

include/net/mana/gdma.h +11 -0
index af62cd6d771e..47255e662fc3 100644
@@ -544,6 +544,15 @@ struct gdma_context {
 	/* Hardware communication channel (HWC) */
 	struct gdma_dev		hwc;
 
+	/* Sender drain; the final wakeup runs under hwc_lock. */
+	wait_queue_head_t	hwc_drain_waitq;
+
+	/* Protects runtime HWC publication and active sender references.
+	 * Setup owns an unpublished HWC directly; timeout updates use atomic
+	 * access helpers and do not require this lock.
+	 */
+	spinlock_t		hwc_lock;
+
 	/* Azure network adapter */
 	struct gdma_dev		mana;
 
@@ -1192,6 +1201,8 @@ void mana_gd_free_memory(struct gdma_mem_info *gmi);
 int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,
 			 u32 resp_len, void *resp);
 
+int mana_gd_cancel_hwc_timeout(struct gdma_context *gc);
+
 int mana_gd_destroy_dma_region(struct gdma_context *gc, u64 dma_region_handle);
 void mana_register_debugfs(void);
 void mana_unregister_debugfs(void);
include/net/mana/hw_channel.h +42 -4

85 lines not on this page · raw message

Signature · 1 line
2.43.0
#6 lore raw

Re: [PATCH net-next v10 3/4] net: mana: support concurrent HWC requests

Sashiko has reviewed this patch and found no issues.
Quoted 14 lines

From: Long Li <[email protected]>

Give HWC message slots separate sender and response ownership. Initialize each slot under the inflight resource and per-slot locks, publish its bitmap bit last, and release the slot only after both references are released. The per-slot lock also excludes an older duplicate callback from resetting or consuming the next caller's state.

Serialize SQ posting so concurrent senders cannot corrupt the producer state. A posted request which times out keeps its response reference and slot until its late response arrives or teardown cancels it. The bootstrap channel remains depth one here; the next patch selects and allocates larger queues.

Quoted 7 lines

Wait for a free slot while the channel is healthy instead of imposing a

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author
#7 lore raw

PATCH v10 4/4 net: mana: add dynamic HWC queue depth with reinit path

In reply to Wei Hu

From: Long Li <[email protected]>

The HWC starts with depth-one bootstrap queues. Treat the PF-reported depth as a maximum and rebuild the channel exactly for reports from 2 through
128. Keep the untouched bootstrap queues for depth one and reports above 128, and require a rebuilt channel to repeat the selected depth.

Treat the reported request and response sizes as directional maxima. Rebuild only when both reports equal the 4096-byte bootstrap allocation. Keep allocation strides separate from these limits, and share the effective host-to-PF request budget with the GDMA and RDMA request builders. Bound RDMA CREATE and ADD_PAGES batches by that budget, checking that the headers and at least one page address fit before subtracting their sizes.

Reject oversized messages before acquiring a slot or publishing caller storage. A retained, stopped channel still returns -ENODEV rather than reporting a size error against uninitialized negotiated limits.

Keep each candidate private through queue creation, establishment and the direct EQ test. Publish only the final tested channel. If a rebuild fails, including its functional test, tear it down and restore fresh, tested bootstrap queues. Stop if teardown is not acknowledged; never reuse PF-owned mappings. Restore the previous live timeout budget before the fallback so a failed test's fail-fast reduction does not poison the retry, without reviving terminal cancellation.

Invalidate the previous HWC doorbell before every ESTABLISH_HWC. Validate the BAR0 page geometry and reported index before accepting a new doorbell, and suppress MMIO while it is invalid. This also hardens initial setup, which previously accepted an unchecked device-supplied index. A malformed doorbell completes initialization with -EPROTO without relying on later INIT_DONE events that might require re-arming through that doorbell.

Use the initial PF report to select the queue geometry. Driver-version advertisement occurs after HWC establishment and cannot negotiate behavior needed while the channel is being created.

Copilot-Session: 8c2361e6-c0f4-4b0c-bf01-b5e4cdacec50

---
 drivers/infiniband/hw/mana/main.c             |  19 +-
 .../net/ethernet/microsoft/mana/gdma_main.c   |  26 +-
 .../net/ethernet/microsoft/mana/hw_channel.c  | 316 +++++++++++++++---
 include/net/mana/gdma.h                       |   2 +
 include/net/mana/hw_channel.h                 |  19 +-
 5 files changed, 317 insertions(+), 65 deletions(-)
drivers/infiniband/hw/mana/main.c +13 -6
index 858bbc325eb2..e2ddbcb1f646 100644
@@ -414,28 +414,38 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem
 	unsigned int create_req_msg_size;
 	struct hw_channel_context *hwc;
 	struct ib_block_iter biter;
-	size_t max_pgs_add_cmd = 0;
+	size_t max_pgs_add_cmd;
 	size_t max_pgs_create_cmd;
 	struct gdma_context *gc;
 	size_t num_pages_total;
 	unsigned int tail = 0;
+	u32 req_limit;
 	u64 *page_addr_list;
 	void *request_buf;
 	int err = 0;
 
 	gc = mdev_to_gc(dev);
 	hwc = gc->hwc.driver_data;
+	req_limit = mana_hwc_get_max_req_size(hwc);
+	if (req_limit < struct_size(create_req, page_addr_list, 1) ||
+	    req_limit < struct_size(add_req, page_addr_list, 1)) {
+		ibdev_dbg(&dev->ib_dev,
+			  "HWC request budget %u is too small for DMA region commands\n",
+			  req_limit);
+		return -EMSGSIZE;
+	}
 
 	num_pages_total = ib_umem_num_dma_blocks(umem, page_sz);
 
 	max_pgs_create_cmd =
-		(hwc->max_req_msg_size - sizeof(*create_req)) / sizeof(u64);
+		(req_limit - sizeof(*create_req)) / sizeof(u64);
+	max_pgs_add_cmd = (req_limit - sizeof(*add_req)) / sizeof(u64);
 	num_pages_to_handle =
 		min_t(size_t, num_pages_total, max_pgs_create_cmd);
 	create_req_msg_size =
 		struct_size(create_req, page_addr_list, num_pages_to_handle);
 
-	request_buf = kzalloc(hwc->max_req_msg_size, GFP_KERNEL);
+	request_buf = kzalloc(req_limit, GFP_KERNEL);
 	if (!request_buf)
 		return -ENOMEM;
 
@@ -478,9 +488,6 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem
 			if (err)
 				goto out;
 
-			max_pgs_add_cmd = (hwc->max_req_msg_size -
-				sizeof(*add_req)) / sizeof(u64);
-
 			add_req = request_buf;
 			add_req->dma_region_handle = *gdma_region;
 			add_req->reserved3 = 0;
drivers/net/ethernet/microsoft/mana/gdma_main.c +17 -9
index bb4a963c2cf5..dace16eaeaa5 100644
@@ -589,12 +589,26 @@ static int mana_gd_disable_queue(struct gdma_queue *queue)
 #define DOORBELL_OFFSET_EQ	0xFF8
 #define DOORBELL_OFFSET_DIM	0x820
 
+bool mana_gd_is_valid_doorbell(struct gdma_context *gc, u32 db_id)
+{
+	if (!gc->db_page_size || gc->db_page_off >= gc->bar0_size)
+		return false;
+
+	return gc->db_page_off +
+	       gc->db_page_size * ((u64)db_id + 1) <= gc->bar0_size;
+}
+
 static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index,
 				  enum gdma_queue_type q_type, u32 qid,
 				  u32 tail_ptr, u8 num_req, u8 client_offset)
 {
-	void __iomem *addr = gc->db_page_base + gc->db_page_size * db_index;
 	union gdma_doorbell_entry e = {};
+	void __iomem *addr;
+
+	if (unlikely(db_index == INVALID_DOORBELL))
+		return;
+
+	addr = gc->db_page_base + gc->db_page_size * db_index;
 
 	addr += client_offset;
 
@@ -1342,7 +1356,7 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd,
 	/* The caller must keep the HWC alive throughout queue creation. */
 	hwc = gc->hwc.driver_data;
 	req_msg_size = struct_size(req, page_addr_list, num_page);
-	if (req_msg_size > hwc->max_req_msg_size)
+	if (req_msg_size > mana_hwc_get_max_req_size(hwc))
 		return -EINVAL;
 
 	req = kzalloc(req_msg_size, GFP_KERNEL);
@@ -1622,13 +1636,7 @@ int mana_gd_register_device(struct gdma_dev *gd)
 		return err ? err : -EPROTO;
 	}
 
-	/* Validate that doorbell page for db_id is within the BAR0 region.
-	 * In mana_gd_ring_doorbell(), the address is calculated as:
-	 *   addr = db_page_base + db_page_size * db_id
-	 *        = (bar0_va + db_page_off) + (db_page_size * db_id)
-	 * So we need: db_page_off + db_page_size * (db_id + 1) <= bar0_size
-	 */
-	if (gc->db_page_off + gc->db_page_size * ((u64)resp.db_id + 1) > gc->bar0_size) {
+	if (!mana_gd_is_valid_doorbell(gc, resp.db_id)) {
 		dev_err(gc->dev, "Doorbell ID %u out of range\n", resp.db_id);
 		return -EPROTO;
 	}
drivers/net/ethernet/microsoft/mana/hw_channel.c +267 -49

438 lines not on this page · raw message

include/net/mana/gdma.h +2 -0
index 47255e662fc3..21fdb0cbcb5a 100644
@@ -610,6 +610,8 @@ void mana_gd_fence_eq(struct gdma_context *gc, struct gdma_queue *queue);
 
 int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe);
 
+bool mana_gd_is_valid_doorbell(struct gdma_context *gc, u32 db_id);
+
 void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit);
 
 ssize_t mana_gd_read_ring(struct gdma_queue *q, char __user *buf,
include/net/mana/hw_channel.h +18 -1
index 8f2ac3d37770..fd6d04e1e5cb 100644
@@ -5,6 +5,7 @@
 #define _HW_CHANNEL_H
 
 #include <linux/atomic.h>
+#include <linux/minmax.h>
 
 #define DEFAULT_LOG2_THROTTLING_FOR_ERROR_EQ  4
 
@@ -13,6 +14,9 @@
 
 #define HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH 1
 
+/* Largest supported rebuild depth; larger reports retain bootstrap queues. */
+#define HW_CHANNEL_MAX_QUEUE_DEPTH 128
+
 #define HWC_INIT_DATA_CQID		1
 #define HWC_INIT_DATA_RQID		2
 #define HWC_INIT_DATA_SQID		3
@@ -200,12 +204,18 @@ struct hw_channel_context {
 
 	u16 num_inflight_msg;
 	u32 max_req_msg_size;
+	u32 max_resp_msg_size;
+	/* Immutable reported maxima for the final RQ and SQ directions. */
+	u32 rx_msg_size_limit;
+	u32 tx_msg_size_limit;
 
-	u16 hwc_init_q_depth_max;
+	u32 hwc_init_q_depth_max;
 	u32 hwc_init_max_req_msg_size;
 	u32 hwc_init_max_resp_msg_size;
 	u32 hwc_init_max_num_cqs;
 	u32 hwc_init_cq_id;
+	bool hwc_init_doorbell;
+	int hwc_init_error;
 
 	struct completion hwc_init_eqe_comp;
 
@@ -240,6 +250,13 @@ struct hw_channel_context {
 	struct hwc_caller_ctx *caller_ctx;
 };
 
+/* Host-to-PF requests are bounded by the negotiated TX limit and SQ stride. */
+static inline u32
+mana_hwc_get_max_req_size(const struct hw_channel_context *hwc)
+{
+	return min(hwc->tx_msg_size_limit, hwc->max_resp_msg_size);
+}
+
 int mana_hwc_create_channel(struct gdma_context *gc);
 void mana_hwc_destroy_channel(struct gdma_context *gc);
 void mana_hwc_free_retained_channel(struct gdma_context *gc);
Signature · 1 line
2.43.0
#8 lore raw

Re: [PATCH net-next v10 4/4] net: mana: add dynamic HWC queue depth with reinit path

Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Medium] net: mana: duplicate mana_hwc_teardown_queues() on HWC rebuild failure --

Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Medium] net: mana: duplicate mana_hwc_teardown_queues() on HWC rebuild failure
--

commit 63009cdb17b7f248623a5c752d26416fb4226096
Author: Long Li <[email protected]>

net: mana: add dynamic HWC queue depth with reinit path

This commit adds dynamic HWC queue depth negotiation by rebuilding the bootstrap hardware channel when the PF reports a supported queue depth between 2 and 128, with fallback to fresh bootstrap queues if the rebuild fails.

Quoted 4 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +0 -0
index 8bc00001d20e5..867f8dd3ea547 100644

[ ... ]

Quoted 58 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +15 -22
@@ -1088,16 +1176,11 @@ static void mana_hwc_fence_channel(struct gdma_context *gc,
 		mana_hwc_unpublish_cq(gc, hwc->cq->gdma_cq);
 }
 
-static bool mana_hwc_release_channel(struct hw_channel_context *hwc)
+static int mana_hwc_teardown_queues(struct hw_channel_context *hwc)
 {
 	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
 	int err;
 
-	mana_hwc_stop_channel(hwc);
-
-	/* An unacknowledged destroy leaves the PF's mappings live.  Fence
-	 * software dispatch, but retain every PF-visible allocation.
-	 */
 	err = mana_smc_teardown_hwc(&gc->shm_channel, false,
 				    &hwc->setup_active);
 	if (err) {
@@ -1105,28 +1188,26 @@ static bool mana_hwc_release_channel(struct hw_channel_context *hwc)
 			"HWC teardown failed: %d, retaining PF-visible resources\n",
 			err);
 		mana_hwc_fence_channel(gc, hwc);
-		mana_hwc_retain_channel(hwc);
-		return false;
+		return err;
 	}
 
-	mana_hwc_clear_cq_table(gc);
-
-	/* Fence the EQ before releasing the remaining HWC state. */
-	if (hwc->cq)
-		mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc->cq);
-
-	if (hwc->txq)
-		mana_hwc_destroy_wq(hwc, hwc->txq);
+	mana_hwc_destroy_queues(hwc);
 
-	if (hwc->rxq)
-		mana_hwc_destroy_wq(hwc, hwc->rxq);
+	return 0;
+}
 
-	kfree(hwc->caller_ctx);
-	hwc->caller_ctx = NULL;
+static bool mana_hwc_release_channel(struct hw_channel_context *hwc)
+{
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+	int err;
 
-	mana_gd_free_res_map(&hwc->inflight_msg_res);
+	mana_hwc_stop_channel(hwc);
 
-	hwc->num_inflight_msg = 0;
+	err = mana_hwc_teardown_queues(hwc);
+	if (err) {
+		mana_hwc_retain_channel(hwc);
+		return false;
+	}

[ ... ]

Quoted 28 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +28 -0
+static int mana_hwc_rebuild_channel(struct hw_channel_context *hwc,
+				    u16 q_depth,
+				    struct mana_hwc_init_report *report)
+{
+	u32 timeout_ms = mana_hwc_timeout_read(hwc);
+	int cleanup_err;
+	int err;
+
+	err = mana_hwc_teardown_queues(hwc);
+	if (err)
+		return err;
+
+	err = mana_hwc_build_channel(hwc, q_depth, report);
+	if (!err)
+		err = mana_hwc_validate_report(hwc, report, q_depth,
+					       true, true);
+	if (!err)
+		err = mana_hwc_finish_channel(hwc, report);
+	if (!err)
+		return 0;
+
+	dev_warn(hwc->dev,
+		 "HWC depth %u rebuild failed, restoring bootstrap: %d\n",
+		 q_depth, err);
+
+	cleanup_err = mana_hwc_teardown_queues(hwc);
+	if (cleanup_err)
+		return cleanup_err;

[ ... ]

drivers/net/ethernet/microsoft/mana/hw_channel.c int mana_hwc_create_channel(struct gdma_context *gc) · line 1458

Quoted 9 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +1 -1
@@ -1234,7 +1452,7 @@ int mana_hwc_create_channel(struct gdma_context *gc)
 	gd->pdid = INVALID_PDID;
 	gd->doorbell = INVALID_DOORBELL;
 
-	err = mana_hwc_create_bootstrap_channel(hwc);
+	err = mana_hwc_prepare_channel(hwc);
 	if (err) {
 		mana_hwc_release_channel(hwc);
 		return err;

[Severity: Medium]
Does mana_hwc_release_channel() issue a duplicate mana_hwc_teardown_queues() when mana_hwc_rebuild_channel() fails inside mana_hwc_teardown_queues()?

mana_smc_teardown_hwc_locked() only clears hwc->setup_active when the SMC teardown transaction succeeds. If mana_hwc_teardown_queues() fails in mana_hwc_rebuild_channel(), it fences the channel and returns the error with hwc->setup_active still true, and the error path in mana_hwc_create_channel() then calls mana_hwc_release_channel() -> mana_hwc_teardown_queues() a second time on the same channel. On an unresponsive shared-memory channel this stalls for a second 20-40s poll timeout; if the PF completes the first SMC_MSG_TYPE_DESTROY_HWC right after the first read times out and rejects the second destroy with a status error, mana_hwc_release_channel() retains the already-destroyed channel.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4