net: mana: concurrent HWC requests and dynamic queue depth
Subsystems (5)
v7 cover net: mana: concurrent HWC requests and dynamic queue depth
This series picks up Long Li's v5 MANA Hardware Channel (HWC) work, addresses its review findings, and retains its four-patch feature lineage. Safety changes required by concurrency and repeated HWC reinitialization are folded into the patch which owns each behavior rather than posted as a separate fixes series.
I have taken over follow-up upstreaming from Long. Long remains the author of every patch, his existing sign-off is preserved, and I added mine for the changes and submission custody.
Patch 1 prepares HWC ownership for safe reinitialization. It serializes complete shared-memory transactions, tracks possible PF ownership, retains mappings after an unacknowledged destroy, fences the HWC EQ before releasing CQ state, and safely publishes and withdraws the HWC CQ table.
Patch 2 gives each slot independent completion state, withdraws caller storage before sender return, handles the response-versus-timeout race, and prevents depth-one slot reuse after a genuine timeout.
Patch 3 adds concurrent requests with bounded admission, serialized SQ posting, separate sender/response ownership, stale-response quarantine, coherent timeout updates, private setup publication, and sender draining.
Patch 4 rebuilds the depth-one bootstrap queues at validated depths from 2 through 128. It validates both reports, message limits and doorbells, keeps the untouched bootstrap channel for depth one or reports above 128, and restores fresh bootstrap queues after a failed rebuild only when teardown was acknowledged.
Generic Ethernet/RDMA CQ callback refcounting and generic service-work versus device-lifecycle serialization are independent pre-existing work and are intentionally excluded. Malformed-RX handling, duplicate-success correlation, and broad device-controlled allocation/timeout policy caps also remain separate follow-ups.
This series is independent of the separately posted queue-set v6 series. That series addresses the pre-existing generic Ethernet CQ reclamation gap discussed in the HWC v6 review. The CQ-publication hunks overlap; whichever series lands second will need reconciliation preserving both table-publication and individual-CQ lifetime guarantees.
https://lore.kernel.org/all/[email protected]/
Changes in v7 (v6 -> v7):
- Rebase onto net-next d8674294aefef02266c4d47ad10131f1bffbe534, following Simon Horman's request after the v6 CI application failure.
- Preserve the upstream GDMA core/PCI transport split and CDX support. Initialize the HWC lifetime lock on both transport probe paths and perform PCI recovery's timeout cancellation through a GDMA core helper which protects the published HWC pointer across the module boundary.
- Retain the four-patch lineage and the v6 ownership, request, timeout, publication, depth-validation and fallback policies.
Rebase request:
https://lore.kernel.org/all/[email protected]/
Changes in v6 (v5 -> v6):
- Keep the four-patch v5 structure while folding review-required HWC ownership, teardown, timeout, publication and fallback safety into their owning feature patches.
- Serialize complete ESTABLISH_HWC and DESTROY_HWC shared-memory transactions. Record possible PF ownership before MMIO submission and clear it only after DESTROY_HWC is acknowledged.
- Retain PF-visible queues after an indeterminate destroy. Fence the dedicated HWC EQ and withdraw its CQ from dispatch without freeing the mappings, then retry teardown before assigning fresh queues.
- Destroy the HWC EQ before CQ and completion state. Release-publish the CQ table and wait for IRQ-side RCU readers before freeing it, without adding per-completion refcount operations.
- Withdraw the caller's response buffer before return and quarantine a timed-out slot until its late response arrives or teardown cancels it.
- Return -EBUSY for healthy slot pressure and reserve -ETIMEDOUT for an admitted request which receives no response.
- Publish slots under the inflight lock, protect response state with the per-slot lock, and retain sender/response references until both sides finish.
- Synchronize hwc_timeout updates and publish the HWC only after queues, admission state, caller contexts and the direct EQ test are ready.
- Replace the goto-heavy request and channel-creation flows called out in review with focused admission, submission, wait, build, validation, rebuild and fallback helpers.
- Treat reported depth as a maximum: rebuild exactly for 2 through 128, retain untouched bootstrap queues for 1 or above 128, and require the rebuilt report to repeat the selected depth.
- Enforce directional request/response maxima against both the report and allocated buffers. Rebuild only for exact 4096-byte reports while permitting bootstrap operation for other nonzero maxima.
- Validate BAR page geometry and every stored doorbell. Invalidate the old HWC doorbell before re-establishment and suppress MMIO while the invalid sentinel is present, without adding full validation to each data-path doorbell ring.
- Remove the proposed capability bit because driver-version advertisement occurs after HWC establishment and cannot negotiate creation-time behavior.
- Preserve current net-next behavior which always uses the hardware-reported dest_vrq_id and dest_vrcq_id.
Changes in v5 (v4 -> v5):
- Shortened comments and documented the existing error paths in patches 1 and 2.
- Rejected apparent success from a response accepted before request submission.
- Guarded CQ unpublication when establishment failed before cq_table allocation.
- Required successful retry teardown before creating fresh bootstrap queues after an initial destroy failure.
Changes in v4 (return to net-next after the v3 split):
- Reworked the feature series into four standalone patches for handover, per-slot completion state, concurrency and dynamic depth.
- Added bounded admission, guarded sender accounting, timeout quarantine, queue-depth bounds, message-buffer sizing and rebuild validation.
- Reset routing IDs for each establish and rejected a missing doorbell.
Changes in v3 (v2 -> net v3, historical fixes-only posting):
- Split the combined series after maintainer feedback, posting fixes to net and deferring concurrency and dynamic depth.
- Added response-buffer withdrawal and stale-response handling.
- Removed pcie_flr() teardown fallback and retained resources after an unacknowledged destroy.
Changes in v2 (v1 -> v2):
- Bounds-checked the SGE pointer derived from inline OOB size.
- Protected active-sender accounting and drained senders during teardown and failure exits.
- Rebased on net-next e354f7d60f14.
v1:
- Combined CQ-table lifetime, queue sizing, RX validation, teardown, concurrent-request and dynamic-depth work in seven patches.
Validation:
- Exact application and strict checkpatch for all four patches.
- Baseline and every intermediate commit built with GCC/Clang W=1, including GDMA core, PCI, CDX COMPILE_TEST and RDMA target objects.
- Full baseline/release kernel and module builds on the new base.
- Sparse: no new diagnostics relative to baseline. Smatch remains limited by identical baseline frontend and array-bound diagnostics; it is not counted as a clean pass.
- Fresh baseline/release runs on two Azure MANA VMs: concurrent PD/MR and native RNIC PD/CQ/MR resource controls, channel/MTU changes, three PCI remove/rescan cycles per VM, and three samples each of forward/reverse TCP and 64-byte UDP. No new kernel warning or functional failure in these runs; safe kernels/configurations restored.
- These runs do not establish negotiated multi-request depth, connected RDMA data-plane operation, CDX hardware support or fresh firmware-fault, fallback, sanitizer/lockdep coverage. Prior v6 fault evidence is not presented as fresh v7 execution.
v6:
https://lore.kernel.org/all/[email protected]/
v5:
https://lore.kernel.org/all/[email protected]/
v4:
https://lore.kernel.org/all/[email protected]/
v3:
https://lore.kernel.org/all/[email protected]/
v2:
https://lore.kernel.org/all/[email protected]/
v1:
https://lore.kernel.org/all/[email protected]/
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
.../net/ethernet/microsoft/mana/gdma_cdx.c | 1 + .../net/ethernet/microsoft/mana/gdma_main.c | 140 ++- .../net/ethernet/microsoft/mana/gdma_pci.c | 11 +- .../net/ethernet/microsoft/mana/hw_channel.c | 928 +++++++++++++++--- .../net/ethernet/microsoft/mana/shm_channel.c | 58 +- include/net/mana/gdma.h | 16 + include/net/mana/hw_channel.h | 56 +- include/net/mana/shm_channel.h | 10 +- 8 files changed, 1019 insertions(+), 201 deletions(-)
base-commit: d8674294aefef02266c4d47ad10131f1bffbe534
Signature · 1 line
2.43.0
PATCH v7 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.
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. The EQ handler acquires the table pointer but does not take a per-event CQ reference; HWC teardown instead fences the dedicated EQ and waits for the existing IRQ-side RCU readers before freeing the table.
On acknowledged teardown, destroy the HWC EQ before the CQ and completion buffer, then release the TX and RX queues. This ordering makes both the normal reinitialization path and every failure unwind safe.
--- .../net/ethernet/microsoft/mana/gdma_main.c | 25 ++- .../net/ethernet/microsoft/mana/hw_channel.c | 150 +++++++++++++++--- .../net/ethernet/microsoft/mana/shm_channel.c | 58 ++++++- include/net/mana/gdma.h | 3 + include/net/mana/hw_channel.h | 4 + include/net/mana/shm_channel.h | 10 +- 6 files changed, 217 insertions(+), 33 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_main.c +20 -5
@@ -754,6 +754,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)union gdma_eqe_info eqe_info;enum gdma_eqe_type type;struct gdma_event event; +struct gdma_queue **cq_table;struct gdma_queue *cq;struct gdma_eqe *eqe;u32 cq_id;
@@ -765,11 +766,16 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)switch (type) {case GDMA_EQE_COMPLETION : cq_id = eqe->details[0 ] &0xFFFFFF ; -if (WARN_ON_ONCE (cq_id >= gc->max_num_cqs)) +/* The IRQ handler's RCU read-side section protects the table +* until HWC teardown has fenced its EQ and waited for readers. +*/ + cq_table =smp_load_acquire (&gc->cq_table); +if (!cq_table || cq_id >=READ_ONCE (gc->max_num_cqs))break ; - cq = gc->cq_table[cq_id]; -if (WARN_ON_ONCE (!cq || cq->type !=GDMA_CQ || cq->id != cq_id)) + cq =READ_ONCE (cq_table[cq_id]); +if (!cq ||WARN_ON_ONCE (cq->type !=GDMA_CQ || + cq->id != cq_id))break ;if (cq->cq.callback)
@@ -993,23 +999,32 @@ int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)return err; } -static void mana_gd_destroy_eq (struct gdma_context *gc,bool flush_evenets, +static void mana_gd_destroy_eq (struct gdma_context *gc,bool flush_events,struct gdma_queue *queue) {int err; -if (flush_evenets) { +if (queue->eq.msix_index ==INVALID_PCI_MSIX_INDEX ) +return ; + +if (flush_events) { err =mana_gd_test_eq (gc, queue);if (err &&mana_need_log (gc, err))dev_warn (gc->dev,"Failed to flush EQ: %d\n" , err); }mana_gd_deregister_irq (queue); + queue->eq.msix_index =INVALID_PCI_MSIX_INDEX ;if (queue->eq.disable_needed)mana_gd_disable_queue (queue); } +void mana_gd_fence_eq (struct gdma_context *gc,struct gdma_queue *queue) +{ +mana_gd_destroy_eq (gc, false, queue); +} +static int mana_gd_create_eq (struct gdma_dev *gd,const struct gdma_queue_spec *spec,bool create_hwq,struct gdma_queue *queue)
drivers/net/ethernet/microsoft/mana/hw_channel.c +128 -22
230 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 +3 -0
@@ -580,6 +580,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);
include/net/mana/hw_channel.h +4 -0
@@ -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,6 +201,8 @@ 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; };
include/net/mana/shm_channel.h +8 -2
@@ -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
[email protected]
AI reviewRe: [PATCH net-next v7 1/4] net: mana: prepare HWC ownership for safe reinitialization
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] net/mana: use-after-free in mana_gd_process_eqe() due to missing RCU grace period for CQ…
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] net/mana: use-after-free in mana_gd_process_eqe() due to missing RCU grace period for CQ
- [Medium] net/mana: memory leak of software structures on HWC teardown failure
- [Low] net/mana: missing WRITE_ONCE in mana_gd_destroy_cq creates data race
--
commit 361a37c1499976f2c320b5cabc5e1273595fd5dd
Author: Long Li <[email protected]>
net: mana: prepare HWC ownership for safe reinitialization
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.
Quoted 4 lines
drivers/net/ethernet/microsoft/mana/gdma_main.c +0
-0
[ ... ]
Quoted 5 lines
drivers/net/ethernet/microsoft/mana/gdma_main.c +0
-1@@ -765,11 +766,16 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)switch (type) {case GDMA_EQE_COMPLETION : cq_id = eqe->details[0 ] &0xFFFFFF ; -if (WARN_ON_ONCE (cq_id >= gc->max_num_cqs))
drivers/net/ethernet/microsoft/mana/gdma_main.c static void mana_gd_process_eqe(struct gdma_queue *eq) · line 776
Quoted 10 lines
drivers/net/ethernet/microsoft/mana/gdma_main.c +6
-2+/* The IRQ handler's RCU read-side section protects the table +* until HWC teardown has fenced its EQ and waited for readers. +*/ + cq_table =smp_load_acquire (&gc->cq_table); +if (!cq_table || cq_id >=READ_ONCE (gc->max_num_cqs))break ; - cq = gc->cq_table[cq_id]; -if (WARN_ON_ONCE (!cq || cq->type !=GDMA_CQ || cq->id != cq_id)) + cq =READ_ONCE (cq_table[cq_id]);
[Severity: High]
During HWC teardown, the dedicated HWC EQ is fenced (which includes a synchronize_rcu() wait), and then the HWC CQ is destroyed. The CQ memory is freed without a subsequent RCU grace period. Can a concurrently running IRQ handler for a different active EQ (like an ENET EQ) read the CQ pointer from cq_table just before it is cleared, and dereference it after it is freed here?
Since the PF is untrusted, could a malicious or compromised PF send an HWC CQ completion event to a different EQ? The synchronize_rcu() in the EQ teardown doesn't protect the CQ because the CQ is removed from cq_table after that wait.
[Severity: Low]
This problem wasn't introduced by this patch, but by upgrading the reader to READ_ONCE() here, it highlights that mana_gd_destroy_cq() clears the CQ table entry using a plain write:
gc->cq_table[id] = NULL;
For normal networking queues, this happens before the EQ is fenced, so the EQ handler may concurrently execute the READ_ONCE(cq_table[cq_id]). Should this writer be updated to WRITE_ONCE() to prevent a formal data race?
Quoted 5 lines
drivers/net/ethernet/microsoft/mana/gdma_main.c +2
-0+if (!cq ||WARN_ON_ONCE (cq->type !=GDMA_CQ || + cq->id != cq_id))break ;if (cq->cq.callback)
[ ... ]
Quoted 4 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +0
-0
[ ... ]
Quoted 1 line
drivers/net/ethernet/microsoft/mana/hw_channel.c +0
-0@@ -808,30 +885,56 @@ int mana_hwc_create_channel(struct gdma_context *gc)
[ ... ]
Quoted 16 lines
void
mana_hwc_destroy_channel(struct gdma_context *gc){drivers/net/ethernet/microsoft/mana/hw_channel.c +4
-4struct hw_channel_context *hwc = gc->hwc.driver_data; +struct gdma_queue **old_cq_table; +int err;if (!hwc)return ; -/* gc->max_num_cqs is set in mana_hwc_init_event_handler(). If it's -* non-zero, the HWC worked and we should tear down the HWC here. +/* An unacknowledged destroy leaves the PF's mappings live. Fence +* software dispatch, but retain every PF-visible allocation. */ -if (gc->max_num_cqs >0 ) { -mana_smc_teardown_hwc (&gc->shm_channel, false);
drivers/net/ethernet/microsoft/mana/hw_channel.c int mana_hwc_create_channel(struct gdma_context *gc) · line 921
Quoted 10 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +8
-1- gc->max_num_cqs =0 ; + err =mana_smc_teardown_hwc (&gc->shm_channel, false, + &hwc->setup_active); +if (err) { +dev_err (hwc->dev, +"HWC teardown failed: %d, retaining PF-visible resources\n" , + err); +mana_hwc_fence_channel (gc, hwc); +return ; }
[Severity: Medium]
If mana_smc_teardown_hwc() fails, mana_hwc_destroy_channel() returns early to prevent the PF from writing to reallocated DMA buffers. However, does this early return skip the deallocation of CPU-side software structures like hwc, hwc->caller_ctx, and gc->cq_table?
Since these structures are pure software allocations and not DMA-mapped PF-visible memory, should they be freed even if the hardware is unresponsive?
During driver unload (mana_gd_remove), vfree(gc) is eventually called, which destroys the parent gdma_context and permanently leaks the retained gc->hwc.driver_data (hwc) and gc->cq_table on this error path.
Quoted 5 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +4
-0+/* Fence the EQ before releasing any state its handlers can reach. */ +if (hwc->cq) +mana_hwc_destroy_cq (hwc->gdma_dev->gdma_context, hwc->cq); +
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Wei Hu
maintainerRE: [EXTERNAL] Re: [PATCH net-next v7 1/4] net: mana: prepare HWC ownership for safe reinitialization
Thanks for the review. I have prepared a four-patch v8 based on net-next commit 05b5019b01fb, with the following dispositions.
Quoted 7 lines · On Wed, 14 Oct 2026 08:06:25 +0000, [email protected] wrote:
[Severity: High]
During HWC teardown, the dedicated HWC EQ is fenced (which includes asynchronize_rcu()wait), and then the HWC CQ is destroyed. The CQ memory is freed without a subsequent RCU grace period. Can a concurrently running IRQ handler for a different active EQ (like an ENET EQ) read the CQ pointer fromcq_tablejust before it is cleared, and dereference it after it is freed here?
Addressed in v8 patch 1. The CQ table is withdrawn and existing IRQ-side RCU readers are drained BEFORE the HWC CQ, completion buffer and queue bundle are freed. Fencing only the dedicated HWC EQ is not used as proof that another EQ cannot hold this CQ. The ordering is in the owning first patch, not a later repair. This is HWC table/bundle protection, not a claim to repair every individual Ethernet or RDMA CQ lifetime
Quoted 5 lines
[Severity: Low]
For normal networking queues, this happens before the EQ is fenced, so the EQ handler may concurrently execute theREAD_ONCE(cq_table[cq_id]). Should this writer be updated toWRITE_ONCE()to prevent a formal data race?
Fixed in v8 patch 1: CQ-slot clearing uses WRITE_ONCE to pair with the annotated reader. This annotation does not by itself supply a grace period or solve individual CQ publication/lifetime.
Quoted 5 lines
[Severity: Medium]
During driver unload (mana_gd_remove), vfree(gc) is eventually called, which destroys the parentgdma_contextand permanently leaks the retainedgc->hwc.driver_data(hwc) andgc->cq_tableon this error path.
Fixed in v8 patch 1. A retained channel remains available for retry while its gdma_context survives. When a PCI or CDX path finally discards that context, a common final-disposal helper now releases software-only wrappers, caller state, the bitmap and CQ table after the required CPU users are quiesced. Coherent allocations are deliberately NOT freed without an acknowledged ownership transfer. This fixes the additional software leak; it does not claim that an unresponsive PF can be made to release DMA memory.
Regards,
Wei
PATCH v7 2/4 net: mana: give each HWC message slot its own completion state
In reply to Wei HuFrom: 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 therefore cannot copy through a pointer to caller storage whose lifetime has ended.
Reinitialize completion and result state when the slot is acquired. If the response records its result while the timed wait expires, return that result instead of reporting a false timeout.
HWC responses contain only the slot ID. After a genuine timeout, prevent the serialized bootstrap channel from reusing that ID until teardown. This keeps a stale response from completing a later request before the next patch replaces the depth-one latch with per-slot sender and response ownership.
Split response-status handling and request cleanup into helpers so the send path has structured returns rather than phase-jumping gotos.
--- .../net/ethernet/microsoft/mana/hw_channel.c | 134 +++++++++++++----- include/net/mana/hw_channel.h | 7 +- 2 files changed, 106 insertions(+), 35 deletions(-)
drivers/net/ethernet/microsoft/mana/hw_channel.c +100 -34
@@ -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;
@@ -16,9 +17,22 @@ static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)spin_lock_irqsave (&r->lock, flags); +if (hwc->hwc_timed_out) { +spin_unlock_irqrestore (&r->lock, flags); +up (&hwc->sema); +return -ETIMEDOUT ; + } + index =find_first_zero_bit (hwc->inflight_msg_res.map, hwc->inflight_msg_res.size); + ctx = &hwc->caller_ctx[index]; +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_irqrestore (&r->lock, flags);
@@ -28,12 +42,15 @@ static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)return 0 ; } -static void mana_hwc_put_msg_index (struct hw_channel_context *hwc,u16 msg_id) +static void mana_hwc_put_msg_index (struct hw_channel_context *hwc,u16 msg_id, +bool timed_out) {struct gdma_resource *r = &hwc->inflight_msg_res;unsigned long flags;spin_lock_irqsave (&r->lock, flags); +if (timed_out) + hwc->hwc_timed_out = true;bitmap_clear (hwc->inflight_msg_res.map, msg_id,1 );spin_unlock_irqrestore (&r->lock, flags);
@@ -90,14 +107,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 +130,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,
@@ -694,8 +719,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;
@@ -958,6 +985,38 @@ void mana_hwc_destroy_channel(struct gdma_context *gc)vfree (old_cq_table); } +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, +bool timed_out) +{ +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, timed_out); +} +int mana_hwc_send_request (struct hw_channel_context *hwc,u32 req_len,const void *req,u32 resp_len,void *resp) {
@@ -965,26 +1024,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, false); +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)
@@ -1006,11 +1071,23 @@ 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, false); +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, false); +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);
@@ -1019,27 +1096,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, true); +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, false); +return mana_hwc_response_status (hwc, command, err, status); }
include/net/mana/hw_channel.h +6 -1
@@ -168,10 +168,12 @@ struct hwc_wq {struct hwc_caller_ctx {struct completion comp_event; +/* Protects the output buffer and response state from timeout. */ +spinlock_t lock;void *output_buf;u32 output_buflen; -u32 error;/* Linux error code */ +int error;/* Linux error code */ u32 status_code; };
@@ -201,6 +203,9 @@ struct hw_channel_context {u32 dest_vrcq_id;u32 hwc_timeout; +/* Prevents message ID reuse after a timeout; protected by the map lock. */ +bool hwc_timed_out; +/* The PF may own the HWC queues while this is true. */ bool setup_active;struct hwc_caller_ctx *caller_ctx;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net-next v7 2/4] net: mana: give each HWC message slot its own completion state
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 20 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 therefore cannot copy through a pointer to caller storage whose lifetime has ended.
Reinitialize completion and result state when the slot is acquired. If the response records its result while the timed wait expires, return that result instead of reporting a false timeout.
HWC responses contain only the slot ID. After a genuine timeout, prevent the serialized bootstrap channel from reusing that ID until teardown. This keeps a stale response from completing a later request before the next patch replaces the depth-one latch with per-slot sender and response ownership.
Quoted 7 lines
Split response-status handling and request cleanup into helpers so the send path has structured returns rather than phase-jumping gotos.
Signed-off-by Long Li <[email protected]>Signed-off-by Wei Hu <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v7 3/4 net: mana: support concurrent HWC requests
In reply to Wei HuFrom: Long Li <[email protected]>
Replace the depth-one timeout latch with per-slot sender and response ownership. Initialize each slot under the inflight resource lock, publish its bitmap bit last, and release it only after both sides have finished. This preserves response-buffer withdrawal and timeout quarantine while allowing independent message IDs to make progress.
Serialize SQ posting so concurrent senders cannot corrupt the producer state. A posted request which times out keeps its response reference and admission permit until its late response arrives or teardown cancels it.
Return -EBUSY when all slots remain occupied. Admission pressure is not a response timeout and must not make callers start dead-channel recovery.
Synchronize hwc_timeout updates with atomic access helpers. Teardown cancellation is terminal for a channel instance, firmware and query updates may replace only a live timeout, and fail-fast reduction may only lower a live value to one millisecond.
Keep setup private until the queues, admission state, caller contexts and 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.
Split admission, submission, waiting, response status and unwind into helpers so mana_hwc_send_request() uses structured returns instead of phase-jumping gotos.
--- .../net/ethernet/microsoft/mana/gdma_cdx.c | 1 + .../net/ethernet/microsoft/mana/gdma_main.c | 91 ++- .../net/ethernet/microsoft/mana/gdma_pci.c | 11 +- .../net/ethernet/microsoft/mana/hw_channel.c | 578 +++++++++++++----- include/net/mana/gdma.h | 11 + include/net/mana/hw_channel.h | 43 +- 6 files changed, 546 insertions(+), 189 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_cdx.c +1 -0
@@ -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 +79 -12
174 lines not on this page · raw message
drivers/net/ethernet/microsoft/mana/gdma_pci.c +4 -7
@@ -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 +413 -165
800 lines not on this page · raw message
include/net/mana/gdma.h +11 -0
@@ -528,6 +528,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;
@@ -1164,6 +1173,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 +38 -5
78 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net-next v7 3/4] net: mana: support concurrent HWC requests
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] net: mana: out-of-bounds read on unvalidated hardware msg_id --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] net: mana: out-of-bounds read on unvalidated hardware msg_id
--
commit 9775f43816d83151fa521309768c62ad04b477da
Author: Long Li <[email protected]>
net: mana: support concurrent HWC requests
This commit replaces the depth-one timeout latch with per-slot sender and response ownership. It synchronizes timeout updates, implements runtime teardown logic, and splits HWC request paths into structured helpers to support concurrent hardware channel requests.
Quoted 4 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +0
-0
[ ... ]
Quoted 9 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +2
-0@@ -97,40 +162,54 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, struct hwc_work_request *rx_req, u16 msg_id) {const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; +struct gdma_resource *r = &hwc->inflight_msg_res;struct hwc_caller_ctx *ctx; +bool release;int err;
drivers/net/ethernet/microsoft/mana/hw_channel.c static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, · line 178
Quoted 10 lines
drivers/net/ethernet/microsoft/mana/hw_channel.c +3
-1-if (!test_bit (msg_id, hwc->inflight_msg_res.map)) { +spin_lock (&r->lock); +if (!test_bit (msg_id, r->map)) { +spin_unlock (&r->lock);dev_err (hwc->dev,"hwc_rx: invalid msg_id = %u\n" , msg_id);mana_hwc_post_rx_wqe (hwc->rxq, rx_req);return ; } ctx = hwc->caller_ctx + msg_id;
[Severity: High]
Can this result in an out-of-bounds memory access if the hardware returns an invalid msg_id?
The msg_id value originates from the hardware completion structure. In mana_hwc_handle_resp(), it is used directly in test_bit(msg_id, r->map) and as an array offset for hwc->caller_ctx without any bounds verification.
If the hardware provides a malformed msg_id (e.g., larger than r->size), test_bit() will perform an out-of-bounds read, and the array indexing will result in out-of-bounds pointer arithmetic. This can subsequently lead to memory corruption when the code acquires ctx->lock and copies data into ctx->output_buf.
Should there be a bounds check against r->size or hwc->num_inflight_msg before msg_id is used here?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Wei Hu
maintainerRE: [EXTERNAL] Re: [PATCH net-next v7 3/4] net: mana: support concurrent HWC requests
Thanks for the review. I have prepared a four-patch v8 based on net-next commit 05b5019b01fb, with the following dispositions.
Quoted 4 lines · On Wed, 14 Oct 2026 08:06:24 +0000, [email protected] wrote:
[Severity: High]
Should there be a bounds check againstr->sizeorhwc->num_inflight_msgbeforemsg_idis used here?
No additional bounds check is needed on the reported path. mana_hwc_handle_resp() is static and its sole caller is mana_hwc_rx_event_handler(). That caller reads hwc_msg_id once with READ_ONCE, rejects msg_id >= num_inflight_msg, and passes the already-bounded local value to the helper. The helper does not reread the shared DMA field for indexing. The existing out-of-range early return's missing RX repost is a separate baseline liveness issue, acknowledged here; it is not an out-of-bounds caller_ctx access.
Regards,
Wei
PATCH v7 4/4 net: mana: add dynamic HWC queue depth with reinit path
In reply to Wei HuFrom: 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, then enforce the effective report/allocation limit on every request and expected response.
Keep each candidate channel private through queue creation, establishment and the direct EQ test. Publish only the final tested channel. If a rebuild fails after acknowledged teardown, destroy that candidate and establish fresh bootstrap queues; never reuse mappings already handed to the PF.
Invalidate the previous HWC doorbell before every ESTABLISH_HWC. Accept a new doorbell only after validating the BAR0 page geometry and reported index, and suppress MMIO while re-establishment leaves the HWC doorbell invalid.
Do not advertise the proposed dynamic-depth capability bit. Driver-version advertisement occurs after HWC establishment and cannot negotiate behavior needed while the channel is being created.
Use focused build, validation, rebuild and fallback helpers instead of the goto-heavy creation flow from v5.
--- .../net/ethernet/microsoft/mana/gdma_main.c | 24 +- .../net/ethernet/microsoft/mana/hw_channel.c | 290 +++++++++++++++--- include/net/mana/gdma.h | 2 + include/net/mana/hw_channel.h | 10 +- 4 files changed, 266 insertions(+), 60 deletions(-)
drivers/net/ethernet/microsoft/mana/gdma_main.c +16 -8
@@ -588,12 +588,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) { -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;switch (q_type) {case GDMA_EQ :
@@ -1533,13 +1547,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 +239 -51
403 lines not on this page · raw message
include/net/mana/gdma.h +2 -0
@@ -594,6 +594,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 +9 -1
@@ -11,6 +11,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
@@ -198,12 +201,17 @@ 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;struct completion hwc_init_eqe_comp;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 18 lines
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, then enforce the effective report/allocation limit on every request and expected response.
Keep each candidate channel private through queue creation, establishment and the direct EQ test. Publish only the final tested channel. If a rebuild fails after acknowledged teardown, destroy that candidate and establish [ ... ]
goto-heavy creation flow from v5.
Quoted 3 lines
Signed-off-by Long Li <[email protected]>Signed-off-by Wei Hu <[email protected]>
Sashiko has reviewed this patch and found no issues. It looks great!
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4