* Re: [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path @ 2026-10-14 8:15 netdev-bot+sashiko 0 siblings, 0 replies; 2+ messages in thread From: netdev-bot+sashiko @ 2026-10-14 8:15 UTC (permalink / raw) To: weh Cc: longli, kotaranov, kuba, davem, pabeni, edumazet, andrew+netdev, jgg, leon, haiyangz, wei.liu, decui, shradhagupta, horms, ernis, stephen, netdev, linux-rdma, linux-hyperv, linux-kernel, gargaditya, kees+treewide, kees, mawasthi, weh, edumazet Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider. Critical: 0 · High: 0 · Medium: 2 · Low: 5 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] The size limit that callers use and the limit that mana_hwc_send_request() now enforces can disagree. - [Medium] If the rebuilt channel fails its test, setup aborts instead of falling back to bootstrap. - [Low] This feature patch also fixes an older bug without saying so. - [Low] The commit message contains revision-history text that won't make sense in git history: 'instead of the goto-heavy creation flow from… - [Low] Two comments are now inaccurate. - [Low] The new size gate in mana_hwc_send_request() runs before mana_hwc_get_msg_index() checks whether the channel is up. - [Low] An invalid doorbell report produces a 60-second timeout instead of an immediate -EPROTO. -- net: mana: add dynamic HWC queue depth with reinit path The patch treats the depth the PF reports for the HWC as a maximum. It rebuilds the depth-one bootstrap channel when the report is 2 through 128. If a rebuild fails, it falls back to fresh bootstrap queues. It also enforces the reported request/response maxima on every HWC request and validates the doorbell the PF reports during ESTABLISH_HWC. Link: https://lore.kernel.org/r/20260908035201.402424-5-longli@microsoft.com > 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. [Severity: Low] This isn't a bug, but these two paragraphs read like revision-history notes. No dynamic-depth capability bit exists in the tree. The only DYNAMIC flag in include/net/mana/gdma.h is GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT. Also, "from v5" won't mean anything once this is in git history. Could these notes move below the --- line? > diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c > index 9fdd84ccba653..c9f7db5a76a58 100644 > --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c > +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c > @@ -227,8 +227,16 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self, > switch (event->type) { > case GDMA_EQE_HWC_INIT_EQ_ID_DB: > eq_db.as_uint32 = event->details[0]; > + if (!mana_gd_is_valid_doorbell(gd->gdma_context, > + eq_db.doorbell)) { > + dev_err(hwc->dev, "HWC: invalid doorbell %u\n", > + eq_db.doorbell); > + break; > + } > + > hwc->cq->gdma_eq->id = eq_db.eq_id; > gd->doorbell = eq_db.doorbell; > + hwc->hwc_init_doorbell = true; > break; [Severity: Low] Before this patch, gd->doorbell was taken from GDMA_EQE_HWC_INIT_EQ_ID_DB with no range check. mana_gd_ring_doorbell() turns it directly into an MMIO address: addr = gc->db_page_base + gc->db_page_size * db_index; ... writeq(e.as_uint64, addr); So an out-of-range index from the PF could already trigger a writeq() outside BAR0 on every boot, not only on re-establish. mana_gd_register_device() already applied the same BAR0 check to resp.db_id. This closes an existing hole. Could it be split into a separate fix with a Fixes: tag, so stable trees pick it up? [ ... ] > @@ -838,6 +853,18 @@ static int mana_hwc_establish_channel(struct hw_channel_context *hwc, > u32 cq_id; > int err; > > + hwc->hwc_init_q_depth_max = 0; [ ... ] > + gc->hwc.pdid = INVALID_PDID; > + /* Re-establish must not rearm through the previous channel's doorbell. */ > + gc->hwc.doorbell = INVALID_DOORBELL; [ ... ] > @@ -852,9 +879,14 @@ static int mana_hwc_establish_channel(struct hw_channel_context *hwc, > if (!wait_for_completion_timeout(&hwc->hwc_init_eqe_comp, 60 * HZ)) > return -ETIMEDOUT; > > - *q_depth = hwc->hwc_init_q_depth_max; > - *max_req_msg_size = hwc->hwc_init_max_req_msg_size; > - *max_resp_msg_size = hwc->hwc_init_max_resp_msg_size; > + if (!hwc->hwc_init_doorbell) { > + dev_err(hwc->dev, "HWC: missing valid doorbell in init data\n"); > + return -EPROTO; > + } [Severity: Low] Can this -EPROTO check be reached when the PF reports a bad doorbell? gc->hwc.doorbell is set to INVALID_DOORBELL above. If mana_hwc_init_event_handler() rejects GDMA_EQE_HWC_INIT_EQ_ID_DB, the doorbell stays invalid. mana_gd_process_eq_events() handles at most 5 EQEs per interrupt and then re-arms the EQ: mana_gd_ring_doorbell(gc, eq->gdma_dev->doorbell, eq->type, eq->id, head, SET_ARM_BIT); With this patch, mana_gd_ring_doorbell() returns early for INVALID_DOORBELL, so the EQ is never re-armed. The rest of the init sequence, including GDMA_EQE_HWC_INIT_DONE, would then not raise another interrupt. Wouldn't wait_for_completion_timeout() run the full 60 seconds and return -ETIMEDOUT, instead of failing fast with -EPROTO? On the rebuild path this can happen at each establish stage. [ ... ] > @@ -943,6 +979,44 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, [ ... ] > +/* Setup owns an unpublished HWC and has no runtime senders here. */ > +static void mana_hwc_destroy_queues(struct hw_channel_context *hwc) [Severity: Low] This isn't a bug, but is this comment accurate? mana_hwc_destroy_queues() is also reached during runtime teardown: mana_hwc_destroy_channel() mana_hwc_release_channel() mana_hwc_teardown_queues() mana_hwc_destroy_queues() On that path it is safe because of the unpublish plus mana_hwc_stop_channel(), not because setup owns the channel. The existing comment on the error path in mana_hwc_init_queues() also no longer holds: out: /* mana_hwc_create_channel() will do the cleanup.*/ return err; On the rebuild path, mana_hwc_rebuild_channel() cleans up the failed candidate itself with mana_hwc_teardown_queues(). [ ... ] > @@ -1110,36 +1173,150 @@ static bool mana_hwc_release_channel(struct hw_channel_context *hwc) [ ... ] > +static int mana_hwc_rebuild_channel(struct hw_channel_context *hwc, > + u16 q_depth, > + struct mana_hwc_init_report *report) > +{ > + 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) > + 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; > + > + return mana_hwc_restore_bootstrap(hwc, report); > +} [ ... ] > + } else if (report.queue_depth > > + HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH) { > + rebuild_depth = report.queue_depth; > + err = mana_hwc_rebuild_channel(hwc, rebuild_depth, &report); > + if (err) > + return err; > } > > + /* MAX_REQUEST sizes the RQ; MAX_RESPONSE sizes the SQ. */ > + hwc->rx_msg_size_limit = report.max_req_msg_size; > + hwc->tx_msg_size_limit = report.max_resp_msg_size; > + > err = mana_hwc_test_channel(hwc); > if (err) > dev_err(hwc->dev, "Failed to test HWC: %d\n", err); [Severity: Medium] What happens if mana_hwc_test_channel() fails on a rebuilt channel? mana_hwc_rebuild_channel() returns 0 as soon as the candidate is established and its report validates. The bootstrap restore therefore only covers build and validation failures. A failure here goes straight back to mana_hwc_create_channel(), which releases the channel and fails probe. Examples are an EQ test timeout, an error status, or mana_hwc_post_rx_wqe() failing at the larger depth. The commit message says: 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 Should the EQ test be part of the rebuild, so a failure there also falls back to bootstrap? With the parent commit, the same PF would have run on a tested depth-one channel. Recovery retries would also go through the same rebuild again. [ ... ] > @@ -1329,8 +1506,19 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, > struct gdma_req_hdr *req_msg; > struct hwc_caller_ctx *ctx; > u32 command; > + u32 req_limit; > + u32 resp_limit; > int err; > > + req_limit = min(hwc->tx_msg_size_limit, hwc->max_resp_msg_size); > + resp_limit = min(hwc->rx_msg_size_limit, hwc->max_req_msg_size); > + if (req_len > req_limit || resp_len > resp_limit) { > + dev_err(hwc->dev, > + "HWC: message sizes req=%u/%u resp=%u/%u exceed channel maxima\n", > + req_len, req_limit, resp_len, resp_limit); > + return -EMSGSIZE; > + } > + [Severity: Medium] Can this gate reject requests that callers have already sized correctly? Callers size their requests from hwc->max_req_msg_size. That field stays at HW_CHANNEL_MAX_REQUEST_SIZE and is never lowered to the effective limit. For example, mana_gd_create_dma_region() does: if (req_msg_size > hwc->max_req_msg_size) return -EINVAL; and mana_ib_gd_create_dma_region() in drivers/infiniband/hw/mana/main.c does: max_pgs_create_cmd = (hwc->max_req_msg_size - sizeof(*create_req)) / sizeof(u64); ... max_pgs_add_cmd = (hwc->max_req_msg_size - sizeof(*add_req)) / sizeof(u64); This gate instead bounds req_len by tx_msg_size_limit. mana_hwc_prepare_channel() takes that value from report.max_resp_msg_size. The kept-bootstrap paths accept any non-zero report: depth one, depth above 128, or maxima other than 4096. Suppose the PF reports MAX_RESPONSE below 4096. Wouldn't queue DMA region creation and large RDMA MR registration then fail with -EMSGSIZE, instead of being split into smaller chunks? The reverse case also applies: a smaller MAX_REQUEST is never shown to request producers. The direction also looks inverted from the producers' side. max_req_msg_size is the RQ stride used in mana_hwc_rx_event_handler(): rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; while this gate bounds requests by the SQ size (max_resp_msg_size). The commit message says the effective limit is enforced on every request. Should the in-tree producers be switched to that same limit? [Severity: Low] Should this check come after the channel_up test in mana_hwc_get_msg_index()? If mana_hwc_prepare_channel() fails before it assigns rx_msg_size_limit and tx_msg_size_limit, both stay at zero from kzalloc. If mana_smc_teardown_hwc() then also fails, mana_hwc_release_channel() stores this hwc in gc->hwc.driver_data through mana_hwc_retain_channel(): err = mana_hwc_teardown_queues(hwc); if (err) { mana_hwc_retain_channel(hwc); return false; } A later mana_gd_send_request() on that retained channel would compute req_limit = min(0, ...) = 0 here. It would then return -EMSGSIZE and log an unratelimited dev_err() on every attempt. Before this patch it returned -ENODEV, because channel_up is false. > err = mana_hwc_get_msg_index(hwc, resp, resp_len, &ctx); > if (err) > return err; -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1791561995.git.weh%40linux.microsoft.com ^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH net-next v7 0/4] net: mana: concurrent HWC requests and dynamic queue depth @ 2026-10-13 8:05 Wei Hu 2026-10-13 8:05 ` [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path Wei Hu 0 siblings, 1 reply; 2+ messages in thread From: Wei Hu @ 2026-10-13 8:05 UTC (permalink / raw) To: longli, kotaranov, kuba, davem, pabeni, edumazet, andrew+netdev, jgg, leon, haiyangz, wei.liu, decui, shradhagupta, horms, ernis, stephen Cc: netdev, linux-rdma, linux-hyperv, linux-kernel, gargaditya, kees+treewide, kees, mawasthi, weh, edumazet 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/cover.1790795005.git.weh@linux.microsoft.com/ 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/20261009151906.GF83879@horms.kernel.org/ 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/cover.1790665894.git.weh@linux.microsoft.com/ v5: https://lore.kernel.org/all/20260908035201.402424-1-longli@microsoft.com/ v4: https://lore.kernel.org/all/20260901200018.3194525-1-longli@microsoft.com/ v3: https://lore.kernel.org/all/20260803234355.636038-1-longli@microsoft.com/ v2: https://lore.kernel.org/all/20260721234339.1476932-1-longli@microsoft.com/ v1: https://lore.kernel.org/all/20260715032942.3945317-1-longli@microsoft.com/ 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 -- 2.43.0 ^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path 2026-10-13 8:05 [PATCH net-next v7 0/4] net: mana: concurrent HWC requests and dynamic queue depth Wei Hu @ 2026-10-13 8:05 ` Wei Hu 0 siblings, 0 replies; 2+ messages in thread From: Wei Hu @ 2026-10-13 8:05 UTC (permalink / raw) To: longli, kotaranov, kuba, davem, pabeni, edumazet, andrew+netdev, jgg, leon, haiyangz, wei.liu, decui, shradhagupta, horms, ernis, stephen Cc: netdev, linux-rdma, linux-hyperv, linux-kernel, gargaditya, kees+treewide, kees, mawasthi, weh, edumazet From: Long Li <longli@microsoft.com> 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. Link: https://lore.kernel.org/r/20260908035201.402424-5-longli@microsoft.com Signed-off-by: Long Li <longli@microsoft.com> Signed-off-by: Wei Hu <weh@microsoft.com> --- .../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(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index e4a732039dbc..a73618a422bd 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -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; } diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 9fdd84ccba65..c9f7db5a76a5 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -227,8 +227,16 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self, switch (event->type) { case GDMA_EQE_HWC_INIT_EQ_ID_DB: eq_db.as_uint32 = event->details[0]; + if (!mana_gd_is_valid_doorbell(gd->gdma_context, + eq_db.doorbell)) { + dev_err(hwc->dev, "HWC: invalid doorbell %u\n", + eq_db.doorbell); + break; + } + hwc->cq->gdma_eq->id = eq_db.eq_id; gd->doorbell = eq_db.doorbell; + hwc->hwc_init_doorbell = true; break; case GDMA_EQE_HWC_INIT_DATA: @@ -250,7 +258,8 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self, break; case HWC_INIT_DATA_QUEUE_DEPTH: - hwc->hwc_init_q_depth_max = (u16)val; + /* Preserve the full 24-bit report for validation. */ + hwc->hwc_init_q_depth_max = val; break; case HWC_INIT_DATA_MAX_REQUEST: @@ -617,7 +626,8 @@ static int mana_hwc_alloc_dma_buf(struct hw_channel_context *hwc, u16 q_depth, dma_buf->num_reqs = q_depth; - buf_size = MANA_PAGE_ALIGN(q_depth * max_msg_size); + /* mana_gd_alloc_memory() requires a power-of-two length. */ + buf_size = roundup_pow_of_two(MANA_PAGE_ALIGN(q_depth * max_msg_size)); gmi = &dma_buf->mem_info; err = mana_gd_alloc_memory(gc, buf_size, gmi, false); @@ -823,10 +833,15 @@ static int mana_hwc_test_channel(struct hw_channel_context *hwc) return err; } -static int mana_hwc_establish_channel(struct hw_channel_context *hwc, - u16 *q_depth, - u32 *max_req_msg_size, - u32 *max_resp_msg_size) +struct mana_hwc_init_report { + u32 queue_depth; + u32 max_req_msg_size; + u32 max_resp_msg_size; +}; + +static int +mana_hwc_establish_channel(struct hw_channel_context *hwc, + struct mana_hwc_init_report *report) { struct gdma_context *gc = hwc->gdma_dev->gdma_context; struct gdma_queue *rq = hwc->rxq->gdma_wq; @@ -838,6 +853,18 @@ static int mana_hwc_establish_channel(struct hw_channel_context *hwc, u32 cq_id; int err; + hwc->hwc_init_q_depth_max = 0; + hwc->hwc_init_max_req_msg_size = 0; + hwc->hwc_init_max_resp_msg_size = 0; + hwc->hwc_init_max_num_cqs = 0; + hwc->hwc_init_cq_id = 0; + hwc->hwc_init_doorbell = false; + gc->hwc.pdid = INVALID_PDID; + /* Re-establish must not rearm through the previous channel's doorbell. */ + gc->hwc.doorbell = INVALID_DOORBELL; + hwc->dest_vrq_id = 0; + hwc->dest_vrcq_id = 0; + init_completion(&hwc->hwc_init_eqe_comp); err = mana_smc_setup_hwc(&gc->shm_channel, false, @@ -852,9 +879,14 @@ static int mana_hwc_establish_channel(struct hw_channel_context *hwc, if (!wait_for_completion_timeout(&hwc->hwc_init_eqe_comp, 60 * HZ)) return -ETIMEDOUT; - *q_depth = hwc->hwc_init_q_depth_max; - *max_req_msg_size = hwc->hwc_init_max_req_msg_size; - *max_resp_msg_size = hwc->hwc_init_max_resp_msg_size; + if (!hwc->hwc_init_doorbell) { + dev_err(hwc->dev, "HWC: missing valid doorbell in init data\n"); + return -EPROTO; + } + + report->queue_depth = hwc->hwc_init_q_depth_max; + report->max_req_msg_size = hwc->hwc_init_max_req_msg_size; + report->max_resp_msg_size = hwc->hwc_init_max_resp_msg_size; /* Snapshot the device-reported count and id once, so the same value * sizes, bounds and indexes cq_table even across the sleeping @@ -904,6 +936,9 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, { int err; + if (q_depth > U16_MAX / 2) + return -EINVAL; + err = mana_hwc_init_inflight_msg(hwc, q_depth); if (err) return err; @@ -936,6 +971,7 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, hwc->num_inflight_msg = q_depth; hwc->max_req_msg_size = max_req_msg_size; + hwc->max_resp_msg_size = max_resp_msg_size; return 0; out: @@ -943,6 +979,44 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, return err; } +static void mana_hwc_clear_cq_table(struct gdma_context *gc) +{ + struct gdma_queue **cq_table; + + cq_table = READ_ONCE(gc->cq_table); + WRITE_ONCE(gc->max_num_cqs, 0); + /* Stop new table readers before waiting for prior RCU readers. */ + smp_store_release(&gc->cq_table, NULL); + synchronize_rcu(); + vfree(cq_table); +} + +/* Setup owns an unpublished HWC and has no runtime senders here. */ +static void mana_hwc_destroy_queues(struct hw_channel_context *hwc) +{ + struct gdma_context *gc = hwc->gdma_dev->gdma_context; + + if (hwc->cq) { + mana_hwc_destroy_cq(gc, hwc->cq); + hwc->cq = NULL; + } + mana_hwc_clear_cq_table(gc); + + if (hwc->txq) { + mana_hwc_destroy_wq(hwc, hwc->txq); + hwc->txq = NULL; + } + if (hwc->rxq) { + mana_hwc_destroy_wq(hwc, hwc->rxq); + hwc->rxq = NULL; + } + + kfree(hwc->caller_ctx); + hwc->caller_ctx = NULL; + mana_gd_free_res_map(&hwc->inflight_msg_res); + hwc->num_inflight_msg = 0; +} + static int mana_hwc_publish_channel(struct hw_channel_context *hwc) { struct gdma_context *gc = hwc->gdma_dev->gdma_context; @@ -1060,17 +1134,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; - struct gdma_queue **old_cq_table; 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) { @@ -1078,31 +1146,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; } - /* 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); - - /* Reset only after mana_hwc_destroy_cq() has cleared the CQ table - * slot, so it is not left dangling. - */ - WRITE_ONCE(gc->max_num_cqs, 0); - - 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; + } hwc->gdma_dev->doorbell = INVALID_DOORBELL; hwc->gdma_dev->pdid = INVALID_PDID; @@ -1110,36 +1173,150 @@ static bool mana_hwc_release_channel(struct hw_channel_context *hwc) kfree(hwc); gc->hwc.gdma_context = NULL; - old_cq_table = READ_ONCE(gc->cq_table); - /* Stop new table readers before waiting for existing IRQ readers. */ - smp_store_release(&gc->cq_table, NULL); - synchronize_rcu(); - vfree(old_cq_table); - return true; } -static int mana_hwc_create_bootstrap_channel(struct hw_channel_context *hwc) +static int +mana_hwc_validate_report(struct hw_channel_context *hwc, + const struct mana_hwc_init_report *report, + u32 expected_depth, bool check_depth, + bool require_exact_msg_sizes) +{ + if (!report->queue_depth || !report->max_req_msg_size || + !report->max_resp_msg_size) { + dev_err(hwc->dev, + "HWC: invalid maxima depth=%u req=%u resp=%u\n", + report->queue_depth, report->max_req_msg_size, + report->max_resp_msg_size); + return -EPROTO; + } + + if (require_exact_msg_sizes && + (report->max_req_msg_size != HW_CHANNEL_MAX_REQUEST_SIZE || + report->max_resp_msg_size != HW_CHANNEL_MAX_RESPONSE_SIZE)) { + dev_err(hwc->dev, + "HWC: rebuilt message maxima req=%u resp=%u, expected %u/%u\n", + report->max_req_msg_size, report->max_resp_msg_size, + HW_CHANNEL_MAX_REQUEST_SIZE, + HW_CHANNEL_MAX_RESPONSE_SIZE); + return -EPROTO; + } + + if (check_depth && report->queue_depth != expected_depth) { + dev_err(hwc->dev, "HWC: rebuilt depth %u, expected %u\n", + report->queue_depth, expected_depth); + return -EPROTO; + } + + return 0; +} + +static int mana_hwc_build_channel(struct hw_channel_context *hwc, u16 q_depth, + struct mana_hwc_init_report *report) { - u32 max_req_msg_size, max_resp_msg_size; - u16 q_depth_max; int err; - err = mana_hwc_init_queues(hwc, HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH, + err = mana_hwc_init_queues(hwc, q_depth, HW_CHANNEL_MAX_REQUEST_SIZE, HW_CHANNEL_MAX_RESPONSE_SIZE); if (err) { - dev_err(hwc->dev, "Failed to initialize HWC: %d\n", err); + dev_err(hwc->dev, "Failed to initialize HWC depth %u: %d\n", + q_depth, err); return err; } - err = mana_hwc_establish_channel(hwc, &q_depth_max, &max_req_msg_size, - &max_resp_msg_size); - if (err) { - dev_err(hwc->dev, "Failed to establish HWC: %d\n", err); + err = mana_hwc_establish_channel(hwc, report); + if (err) + dev_err(hwc->dev, "Failed to establish HWC depth %u: %d\n", + q_depth, err); + + return err; +} + +static int +mana_hwc_restore_bootstrap(struct hw_channel_context *hwc, + struct mana_hwc_init_report *report) +{ + int err; + + err = mana_hwc_build_channel(hwc, + HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH, + report); + if (err) return err; + + return mana_hwc_validate_report(hwc, report, 0, false, false); +} + +static int mana_hwc_rebuild_channel(struct hw_channel_context *hwc, + u16 q_depth, + struct mana_hwc_init_report *report) +{ + 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) + 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; + + return mana_hwc_restore_bootstrap(hwc, report); +} + +static int mana_hwc_prepare_channel(struct hw_channel_context *hwc) +{ + struct mana_hwc_init_report report; + u16 rebuild_depth; + int err; + + err = mana_hwc_build_channel(hwc, + HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH, + &report); + if (err) + return err; + + err = mana_hwc_validate_report(hwc, &report, 0, false, false); + if (err) + return err; + + if (report.queue_depth > HW_CHANNEL_MAX_QUEUE_DEPTH) { + dev_warn(hwc->dev, + "HWC depth %u exceeds limit %u, keeping bootstrap\n", + report.queue_depth, HW_CHANNEL_MAX_QUEUE_DEPTH); + } else if (report.queue_depth > + HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH && + (report.max_req_msg_size != HW_CHANNEL_MAX_REQUEST_SIZE || + report.max_resp_msg_size != HW_CHANNEL_MAX_RESPONSE_SIZE)) { + dev_warn(hwc->dev, + "HWC maxima req=%u resp=%u differ from rebuild policy %u/%u, keeping bootstrap\n", + report.max_req_msg_size, report.max_resp_msg_size, + HW_CHANNEL_MAX_REQUEST_SIZE, + HW_CHANNEL_MAX_RESPONSE_SIZE); + } else if (report.queue_depth > + HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH) { + rebuild_depth = report.queue_depth; + err = mana_hwc_rebuild_channel(hwc, rebuild_depth, &report); + if (err) + return err; } + /* MAX_REQUEST sizes the RQ; MAX_RESPONSE sizes the SQ. */ + hwc->rx_msg_size_limit = report.max_req_msg_size; + hwc->tx_msg_size_limit = report.max_resp_msg_size; + err = mana_hwc_test_channel(hwc); if (err) dev_err(hwc->dev, "Failed to test HWC: %d\n", err); @@ -1177,7 +1354,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; @@ -1329,8 +1506,19 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, struct gdma_req_hdr *req_msg; struct hwc_caller_ctx *ctx; u32 command; + u32 req_limit; + u32 resp_limit; int err; + req_limit = min(hwc->tx_msg_size_limit, hwc->max_resp_msg_size); + resp_limit = min(hwc->rx_msg_size_limit, hwc->max_req_msg_size); + if (req_len > req_limit || resp_len > resp_limit) { + dev_err(hwc->dev, + "HWC: message sizes req=%u/%u resp=%u/%u exceed channel maxima\n", + req_len, req_limit, resp_len, resp_limit); + return -EMSGSIZE; + } + err = mana_hwc_get_msg_index(hwc, resp, resp_len, &ctx); if (err) return err; diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 1e653b7cad72..c30a7eb6f7d3 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -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, diff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h index 01568d10b6fd..896effd1f716 100644 --- a/include/net/mana/hw_channel.h +++ b/include/net/mana/hw_channel.h @@ -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; -- 2.43.0 ^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-14 8:16 UTC | newest] Thread overview: 2+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-14 8:15 [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path netdev-bot+sashiko -- strict thread matches above, loose matches on Subject: below -- 2026-10-13 8:05 [PATCH net-next v7 0/4] net: mana: concurrent HWC requests and dynamic queue depth Wei Hu 2026-10-13 8:05 ` [PATCH net-next v7 4/4] net: mana: add dynamic HWC queue depth with reinit path Wei Hu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox