AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/07 13:06 flow patch-triage
19m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies the Microsoft Azure Network Adapter (MANA) driver (drivers/net/ethernet/microsoft/mana/), specifically the GDMA hardware communication channel (HWC) synchronization and message handling. MANA is a vendor-specific SmartNIC / PCIe device deployed in Microsoft Azure / Hyper-V environments and is not emulated in standard QEMU or GCE testbeds. Because the modified code is structurally unreachable without specific physical or hypervisor-provided MANA hardware, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/10/07 13:06 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 737d578bbbea4373908f6ae6c484d02429ba067c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Oct 7 13:06:46 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c\nindex 8e9bfc1d6a2ae..571c7f1d5b211 100644\n--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c\n+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c\n@@ -162,6 +162,8 @@ static int mana_gd_init_registers(struct pci_dev *pdev)\n bool mana_need_log(struct gdma_context *gc, int err)\n {\n \tstruct hw_channel_context *hwc;\n+\tbool need_log = true;\n+\tunsigned long flags;\n \n \tif (err != -ETIMEDOUT)\n \t\treturn true;\n@@ -169,11 +171,13 @@ bool mana_need_log(struct gdma_context *gc, int err)\n \tif (!gc)\n \t\treturn true;\n \n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n \thwc = gc-\u003ehwc.driver_data;\n-\tif (hwc \u0026\u0026 hwc-\u003ehwc_timeout == 0)\n-\t\treturn false;\n+\tif (hwc \u0026\u0026 !mana_hwc_timeout_read(hwc))\n+\t\tneed_log = false;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n \n-\treturn true;\n+\treturn need_log;\n }\n \n static int mana_gd_query_max_resources(struct pci_dev *pdev)\n@@ -317,7 +321,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)\n \treturn 0;\n }\n \n-static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)\n+static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 timeout_ms,\n+\t\t\t\t     u32 *new_timeout_ms)\n {\n \tstruct gdma_context *gc = pci_get_drvdata(pdev);\n \tstruct gdma_query_hwc_timeout_resp resp = {};\n@@ -326,12 +331,12 @@ static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)\n \n \tmana_gd_init_req_hdr(\u0026req.hdr, GDMA_QUERY_HWC_TIMEOUT,\n \t\t\t     sizeof(req), sizeof(resp));\n-\treq.timeout_ms = *timeout_val;\n+\treq.timeout_ms = timeout_ms;\n \terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\n \tif (err || resp.hdr.status)\n \t\treturn err ? err : -EPROTO;\n \n-\t*timeout_val = resp.timeout_ms;\n+\t*new_timeout_ms = resp.timeout_ms;\n \n \treturn 0;\n }\n@@ -387,9 +392,27 @@ static int mana_gd_detect_devices(struct pci_dev *pdev)\n int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,\n \t\t\t u32 resp_len, void *resp)\n {\n-\tstruct hw_channel_context *hwc = gc-\u003ehwc.driver_data;\n+\tstruct hw_channel_context *hwc;\n+\tunsigned long flags;\n+\tint err;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\thwc = gc-\u003ehwc.driver_data;\n+\tif (!hwc) {\n+\t\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\t\treturn -ENODEV;\n+\t}\n+\thwc-\u003eactive_senders++;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\terr = mana_hwc_send_request(hwc, req_len, req, resp_len, resp);\n \n-\treturn mana_hwc_send_request(hwc, req_len, req, resp_len, resp);\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\tif (--hwc-\u003eactive_senders == 0)\n+\t\twake_up(\u0026gc-\u003ehwc_drain_waitq);\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\treturn err;\n }\n EXPORT_SYMBOL_NS(mana_gd_send_request, \"NET_MANA\");\n \n@@ -557,12 +580,26 @@ static int mana_gd_disable_queue(struct gdma_queue *queue)\n #define DOORBELL_OFFSET_EQ\t0xFF8\n #define DOORBELL_OFFSET_DIM\t0x820\n \n+bool mana_gd_is_valid_doorbell(struct gdma_context *gc, u32 db_id)\n+{\n+\tif (!gc-\u003edb_page_size || gc-\u003edb_page_off \u003e= gc-\u003ebar0_size)\n+\t\treturn false;\n+\n+\treturn gc-\u003edb_page_off +\n+\t       gc-\u003edb_page_size * ((u64)db_id + 1) \u003c= gc-\u003ebar0_size;\n+}\n+\n static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index,\n \t\t\t\t  enum gdma_queue_type q_type, u32 qid,\n \t\t\t\t  u32 tail_ptr, u8 num_req)\n {\n-\tvoid __iomem *addr = gc-\u003edb_page_base + gc-\u003edb_page_size * db_index;\n \tunion gdma_doorbell_entry e = {};\n+\tvoid __iomem *addr;\n+\n+\tif (unlikely(db_index == INVALID_DOORBELL))\n+\t\treturn;\n+\n+\taddr = gc-\u003edb_page_base + gc-\u003edb_page_size * db_index;\n \n \tswitch (q_type) {\n \tcase GDMA_EQ:\n@@ -710,6 +747,7 @@ static void mana_serv_reset(struct pci_dev *pdev)\n {\n \tstruct gdma_context *gc = pci_get_drvdata(pdev);\n \tstruct hw_channel_context *hwc;\n+\tunsigned long flags;\n \tint ret;\n \n \tif (!gc) {\n@@ -719,14 +757,17 @@ static void mana_serv_reset(struct pci_dev *pdev)\n \t\treturn;\n \t}\n \n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n \thwc = gc-\u003ehwc.driver_data;\n \tif (!hwc) {\n+\t\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n \t\tdev_err(\u0026pdev-\u003edev, \"MANA service: no HWC\\n\");\n \t\tgoto out;\n \t}\n \n \t/* HWC is not responding in this case, so don't wait */\n-\thwc-\u003ehwc_timeout = 0;\n+\tmana_hwc_timeout_cancel(hwc);\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n \n \tdev_info(\u0026pdev-\u003edev, \"MANA reset cycle start\\n\");\n \n@@ -911,6 +952,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)\n \tunion gdma_eqe_info eqe_info;\n \tenum gdma_eqe_type type;\n \tstruct gdma_event event;\n+\tstruct gdma_queue **cq_table;\n \tstruct gdma_queue *cq;\n \tstruct gdma_eqe *eqe;\n \tu32 cq_id;\n@@ -922,11 +964,16 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)\n \tswitch (type) {\n \tcase GDMA_EQE_COMPLETION:\n \t\tcq_id = eqe-\u003edetails[0] \u0026 0xFFFFFF;\n-\t\tif (WARN_ON_ONCE(cq_id \u003e= gc-\u003emax_num_cqs))\n+\t\t/* The IRQ handler's RCU read-side section protects the table\n+\t\t * until HWC teardown has fenced its EQ and waited for readers.\n+\t\t */\n+\t\tcq_table = smp_load_acquire(\u0026gc-\u003ecq_table);\n+\t\tif (!cq_table || cq_id \u003e= READ_ONCE(gc-\u003emax_num_cqs))\n \t\t\tbreak;\n \n-\t\tcq = gc-\u003ecq_table[cq_id];\n-\t\tif (WARN_ON_ONCE(!cq || cq-\u003etype != GDMA_CQ || cq-\u003eid != cq_id))\n+\t\tcq = READ_ONCE(cq_table[cq_id]);\n+\t\tif (!cq || WARN_ON_ONCE(cq-\u003etype != GDMA_CQ ||\n+\t\t\t\t\tcq-\u003eid != cq_id))\n \t\t\tbreak;\n \n \t\tif (cq-\u003ecq.callback)\n@@ -1101,7 +1148,9 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)\n \tsynchronize_rcu();\n }\n \n-int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)\n+static int mana_gd_test_eq_request(struct gdma_context *gc,\n+\t\t\t\t   struct hw_channel_context *hwc,\n+\t\t\t\t   struct gdma_queue *eq)\n {\n \tstruct gdma_generate_test_event_req req = {};\n \tstruct gdma_general_resp resp = {};\n@@ -1119,7 +1168,12 @@ int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)\n \treq.hdr.dev_id = eq-\u003egdma_dev-\u003edev_id;\n \treq.queue_index = eq-\u003eid;\n \n-\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\n+\tif (hwc)\n+\t\terr = mana_hwc_send_request(hwc, sizeof(req), \u0026req,\n+\t\t\t\t\t    sizeof(resp), \u0026resp);\n+\telse\n+\t\terr = mana_gd_send_request(gc, sizeof(req), \u0026req,\n+\t\t\t\t\t   sizeof(resp), \u0026resp);\n \tif (err) {\n \t\tif (mana_need_log(gc, err))\n \t\t\tdev_err(dev, \"test_eq failed: %d\\n\", err);\n@@ -1150,23 +1204,43 @@ int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)\n \treturn err;\n }\n \n-static void mana_gd_destroy_eq(struct gdma_context *gc, bool flush_evenets,\n+int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)\n+{\n+\treturn mana_gd_test_eq_request(gc, NULL, eq);\n+}\n+\n+int mana_gd_test_hwc_eq(struct hw_channel_context *hwc,\n+\t\t\tstruct gdma_queue *eq)\n+{\n+\treturn mana_gd_test_eq_request(hwc-\u003egdma_dev-\u003egdma_context, hwc, eq);\n+}\n+\n+static void mana_gd_destroy_eq(struct gdma_context *gc, bool flush_events,\n \t\t\t       struct gdma_queue *queue)\n {\n \tint err;\n \n-\tif (flush_evenets) {\n+\tif (queue-\u003eeq.msix_index == INVALID_PCI_MSIX_INDEX)\n+\t\treturn;\n+\n+\tif (flush_events) {\n \t\terr = mana_gd_test_eq(gc, queue);\n \t\tif (err \u0026\u0026 mana_need_log(gc, err))\n \t\t\tdev_warn(gc-\u003edev, \"Failed to flush EQ: %d\\n\", err);\n \t}\n \n \tmana_gd_deregister_irq(queue);\n+\tqueue-\u003eeq.msix_index = INVALID_PCI_MSIX_INDEX;\n \n \tif (queue-\u003eeq.disable_needed)\n \t\tmana_gd_disable_queue(queue);\n }\n \n+void mana_gd_fence_eq(struct gdma_context *gc, struct gdma_queue *queue)\n+{\n+\tmana_gd_destroy_eq(gc, false, queue);\n+}\n+\n static int mana_gd_create_eq(struct gdma_dev *gd,\n \t\t\t     const struct gdma_queue_spec *spec,\n \t\t\t     bool create_hwq, struct gdma_queue *queue)\n@@ -1333,6 +1407,7 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd,\n \tif (gmi-\u003enr_pages == 0 \u0026\u0026 !MANA_PAGE_ALIGNED(gmi-\u003evirt_addr))\n \t\treturn -EINVAL;\n \n+\t/* The caller must keep the HWC alive throughout queue creation. */\n \thwc = gc-\u003ehwc.driver_data;\n \treq_msg_size = struct_size(req, page_addr_list, num_page);\n \tif (req_msg_size \u003e hwc-\u003emax_req_msg_size)\n@@ -1536,9 +1611,12 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)\n \tstruct gdma_verify_ver_resp resp = {};\n \tstruct gdma_verify_ver_req req = {};\n \tstruct hw_channel_context *hwc;\n+\tu32 timeout_ms;\n \tint err;\n \n+\t/* The setup caller must exclude concurrent HWC teardown. */\n \thwc = gc-\u003ehwc.driver_data;\n+\n \tmana_gd_init_req_hdr(\u0026req.hdr, GDMA_VERIFY_VF_DRIVER_VERSION,\n \t\t\t     sizeof(req), sizeof(resp));\n \n@@ -1574,12 +1652,16 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)\n \t\t\t   \u0026gc-\u003epf_cap_flags1);\n \n \tif (resp.pf_cap_flags1 \u0026 GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG) {\n-\t\terr = mana_gd_query_hwc_timeout(pdev, \u0026hwc-\u003ehwc_timeout);\n+\t\terr = mana_gd_query_hwc_timeout(pdev,\n+\t\t\t\t\t\tmana_hwc_timeout_read(hwc),\n+\t\t\t\t\t\t\u0026timeout_ms);\n \t\tif (err) {\n \t\t\tdev_err(gc-\u003edev, \"Failed to set the hwc timeout %d\\n\", err);\n \t\t\treturn err;\n \t\t}\n-\t\tdev_dbg(gc-\u003edev, \"set the hwc timeout to %u\\n\", hwc-\u003ehwc_timeout);\n+\t\tmana_hwc_timeout_update(hwc, timeout_ms);\n+\t\tdev_dbg(gc-\u003edev, \"set the hwc timeout to %u\\n\",\n+\t\t\tmana_hwc_timeout_read(hwc));\n \t}\n \treturn 0;\n }\n@@ -1607,13 +1689,7 @@ int mana_gd_register_device(struct gdma_dev *gd)\n \t\treturn err ? err : -EPROTO;\n \t}\n \n-\t/* Validate that doorbell page for db_id is within the BAR0 region.\n-\t * In mana_gd_ring_doorbell(), the address is calculated as:\n-\t *   addr = db_page_base + db_page_size * db_id\n-\t *        = (bar0_va + db_page_off) + (db_page_size * db_id)\n-\t * So we need: db_page_off + db_page_size * (db_id + 1) \u003c= bar0_size\n-\t */\n-\tif (gc-\u003edb_page_off + gc-\u003edb_page_size * ((u64)resp.db_id + 1) \u003e gc-\u003ebar0_size) {\n+\tif (!mana_gd_is_valid_doorbell(gc, resp.db_id)) {\n \t\tdev_err(gc-\u003edev, \"Doorbell ID %u out of range\\n\", resp.db_id);\n \t\treturn -EPROTO;\n \t}\n@@ -2532,6 +2608,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \n \tmutex_init(\u0026gc-\u003eeq_test_event_mutex);\n \tmutex_init(\u0026gc-\u003egic_mutex);\n+\tspin_lock_init(\u0026gc-\u003ehwc_lock);\n \tpci_set_drvdata(pdev, gc);\n \tgc-\u003ebar0_pa = pci_resource_start(pdev, 0);\n \tgc-\u003ebar0_size = pci_resource_len(pdev, 0);\ndiff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c\nindex 3bca4b683134b..c9f7db5a76a58 100644\n--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c\n+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c\n@@ -6,24 +6,99 @@\n #include \u003cnet/mana/hw_channel.h\u003e\n #include \u003clinux/vmalloc.h\u003e\n \n-static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)\n+u32 mana_hwc_timeout_read(const struct hw_channel_context *hwc)\n+{\n+\treturn READ_ONCE(hwc-\u003ehwc_timeout);\n+}\n+\n+void mana_hwc_timeout_update(struct hw_channel_context *hwc, u32 timeout_ms)\n+{\n+\tu32 old_timeout;\n+\n+\tif (!timeout_ms)\n+\t\treturn;\n+\n+\told_timeout = mana_hwc_timeout_read(hwc);\n+\twhile (old_timeout \u0026\u0026\n+\t       cmpxchg(\u0026hwc-\u003ehwc_timeout, old_timeout, timeout_ms) != old_timeout)\n+\t\told_timeout = mana_hwc_timeout_read(hwc);\n+}\n+\n+void mana_hwc_timeout_cancel(struct hw_channel_context *hwc)\n+{\n+\txchg(\u0026hwc-\u003ehwc_timeout, 0);\n+}\n+\n+static void mana_hwc_timeout_reduce(struct hw_channel_context *hwc)\n+{\n+\tu32 old_timeout = mana_hwc_timeout_read(hwc);\n+\n+\twhile (old_timeout \u003e 1 \u0026\u0026\n+\t       cmpxchg(\u0026hwc-\u003ehwc_timeout, old_timeout, 1) != old_timeout)\n+\t\told_timeout = mana_hwc_timeout_read(hwc);\n+}\n+\n+static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, void *resp,\n+\t\t\t\t  u32 resp_len,\n+\t\t\t\t  struct hwc_caller_ctx **caller_ctx)\n {\n \tstruct gdma_resource *r = \u0026hwc-\u003einflight_msg_res;\n+\tstruct hwc_caller_ctx *ctx;\n \tunsigned long flags;\n+\tbool channel_up;\n+\tu32 wait_ms;\n \tu32 index;\n \n-\tdown(\u0026hwc-\u003esema);\n+\twait_ms = mana_hwc_timeout_read(hwc);\n+\tif (down_timeout(\u0026hwc-\u003esema, msecs_to_jiffies(wait_ms))) {\n+\t\tspin_lock_irqsave(\u0026r-\u003elock, flags);\n+\t\tchannel_up = hwc-\u003echannel_up;\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\n+\t\t/* Slot pressure is not evidence that the HWC stopped responding. */\n+\t\treturn channel_up ? -EBUSY : -ENODEV;\n+\t}\n \n \tspin_lock_irqsave(\u0026r-\u003elock, flags);\n+\tif (!hwc-\u003echannel_up) {\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\t\tup(\u0026hwc-\u003esema);\n+\t\treturn -ENODEV;\n+\t}\n \n-\tindex = find_first_zero_bit(hwc-\u003einflight_msg_res.map,\n-\t\t\t\t    hwc-\u003einflight_msg_res.size);\n+\t/* The semaphore admits at most r-\u003esize holders at a time, so a slot\n+\t * acquired above always has a free bit waiting for it here.\n+\t */\n+\tindex = find_first_zero_bit(r-\u003emap, r-\u003esize);\n+\tif (WARN_ON_ONCE(index \u003e= r-\u003esize)) {\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\t\tup(\u0026hwc-\u003esema);\n+\t\treturn -EIO;\n+\t}\n \n-\tbitmap_set(hwc-\u003einflight_msg_res.map, index, 1);\n+\tctx = \u0026hwc-\u003ecaller_ctx[index];\n+\treinit_completion(\u0026ctx-\u003ecomp_event);\n+\t/* Take both references (sender + response handler) before publishing\n+\t * the slot, so an early response cannot free it under the sender.\n+\t */\n+\trefcount_set(\u0026ctx-\u003erefcnt, 2);\n+\tctx-\u003eoutput_buf = resp;\n+\tctx-\u003eoutput_buflen = resp_len;\n+\tctx-\u003eerror = -EINPROGRESS;\n+\tctx-\u003estatus_code = 0;\n+\tctx-\u003eresponded = false;\n+\tctx-\u003eresp_pending = true;\n+\tctx-\u003emsg_id = index;\n+\n+\t/* The response path takes r-\u003elock before ctx-\u003elock, so publishing the\n+\t * bitmap last makes every field above visible before it can consume\n+\t * the response-side reference.\n+\t */\n+\tbitmap_set(r-\u003emap, index, 1);\n \n \tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n \n-\t*msg_id = index;\n+\t*caller_ctx = ctx;\n \n \treturn 0;\n }\n@@ -34,12 +109,19 @@ static void mana_hwc_put_msg_index(struct hw_channel_context *hwc, u16 msg_id)\n \tunsigned long flags;\n \n \tspin_lock_irqsave(\u0026r-\u003elock, flags);\n-\tbitmap_clear(hwc-\u003einflight_msg_res.map, msg_id, 1);\n+\tbitmap_clear(r-\u003emap, msg_id, 1);\n \tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n \n \tup(\u0026hwc-\u003esema);\n }\n \n+static void hwc_ctx_put(struct hw_channel_context *hwc,\n+\t\t\tstruct hwc_caller_ctx *ctx)\n+{\n+\tif (refcount_dec_and_test(\u0026ctx-\u003erefcnt))\n+\t\tmana_hwc_put_msg_index(hwc, ctx-\u003emsg_id);\n+}\n+\n static int mana_hwc_verify_resp_msg(const struct hwc_caller_ctx *caller_ctx,\n \t\t\t\t    const struct gdma_resp_hdr *resp_msg,\n \t\t\t\t    u32 resp_len)\n@@ -80,32 +162,54 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,\n \t\t\t\t struct hwc_work_request *rx_req, u16 msg_id)\n {\n \tconst struct gdma_resp_hdr *resp_msg = rx_req-\u003ebuf_va;\n+\tstruct gdma_resource *r = \u0026hwc-\u003einflight_msg_res;\n \tstruct hwc_caller_ctx *ctx;\n+\tbool release;\n \tint err;\n \n-\tif (!test_bit(msg_id, hwc-\u003einflight_msg_res.map)) {\n+\tspin_lock(\u0026r-\u003elock);\n+\tif (!test_bit(msg_id, r-\u003emap)) {\n+\t\tspin_unlock(\u0026r-\u003elock);\n \t\tdev_err(hwc-\u003edev, \"hwc_rx: invalid msg_id = %u\\n\", msg_id);\n \t\tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\n \t\treturn;\n \t}\n \n \tctx = hwc-\u003ecaller_ctx + msg_id;\n-\terr = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);\n-\tif (err)\n-\t\tgoto out;\n+\tspin_lock(\u0026ctx-\u003elock);\n+\tspin_unlock(\u0026r-\u003elock);\n \n-\tctx-\u003estatus_code = resp_msg-\u003estatus;\n+\t/* Consume the response-side reference exactly once. This releases a\n+\t * quarantined slot after its late response arrives.\n+\t */\n+\trelease = ctx-\u003eresp_pending;\n+\tctx-\u003eresp_pending = false;\n \n-\tmemcpy(ctx-\u003eoutput_buf, resp_msg, resp_len);\n-out:\n+\tif (ctx-\u003eresponded) {\n+\t\tspin_unlock(\u0026ctx-\u003elock);\n+\t\tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\n+\t\tif (release)\n+\t\t\thwc_ctx_put(hwc, ctx);\n+\t\treturn;\n+\t}\n+\tctx-\u003eresponded = true;\n+\n+\terr = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);\n+\tif (!err) {\n+\t\tctx-\u003estatus_code = resp_msg-\u003estatus;\n+\t\tmemcpy(ctx-\u003eoutput_buf, resp_msg, resp_len);\n+\t}\n \tctx-\u003eerror = err;\n \n-\t/* Must post rx wqe before complete(), otherwise the next rx may\n-\t * hit no_wqe error.\n+\t/* Post RX WQE before completing; the next response may arrive\n+\t * immediately and needs a posted buffer.\n \t */\n \tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\n-\n \tcomplete(\u0026ctx-\u003ecomp_event);\n+\tspin_unlock(\u0026ctx-\u003elock);\n+\n+\tif (release)\n+\t\thwc_ctx_put(hwc, ctx);\n }\n \n static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n@@ -123,8 +227,16 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n \tswitch (event-\u003etype) {\n \tcase GDMA_EQE_HWC_INIT_EQ_ID_DB:\n \t\teq_db.as_uint32 = event-\u003edetails[0];\n+\t\tif (!mana_gd_is_valid_doorbell(gd-\u003egdma_context,\n+\t\t\t\t\t       eq_db.doorbell)) {\n+\t\t\tdev_err(hwc-\u003edev, \"HWC: invalid doorbell %u\\n\",\n+\t\t\t\teq_db.doorbell);\n+\t\t\tbreak;\n+\t\t}\n+\n \t\thwc-\u003ecq-\u003egdma_eq-\u003eid = eq_db.eq_id;\n \t\tgd-\u003edoorbell = eq_db.doorbell;\n+\t\thwc-\u003ehwc_init_doorbell = true;\n \t\tbreak;\n \n \tcase GDMA_EQE_HWC_INIT_DATA:\n@@ -134,7 +246,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n \n \t\tswitch (type) {\n \t\tcase HWC_INIT_DATA_CQID:\n-\t\t\thwc-\u003ecq-\u003egdma_cq-\u003eid = val;\n+\t\t\tWRITE_ONCE(hwc-\u003ehwc_init_cq_id, val);\n \t\t\tbreak;\n \n \t\tcase HWC_INIT_DATA_RQID:\n@@ -146,7 +258,8 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n \t\t\tbreak;\n \n \t\tcase HWC_INIT_DATA_QUEUE_DEPTH:\n-\t\t\thwc-\u003ehwc_init_q_depth_max = (u16)val;\n+\t\t\t/* Preserve the full 24-bit report for validation. */\n+\t\t\thwc-\u003ehwc_init_q_depth_max = val;\n \t\t\tbreak;\n \n \t\tcase HWC_INIT_DATA_MAX_REQUEST:\n@@ -158,7 +271,11 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n \t\t\tbreak;\n \n \t\tcase HWC_INIT_DATA_MAX_NUM_CQS:\n-\t\t\tgd-\u003egdma_context-\u003emax_num_cqs = val;\n+\t\t\t/* Store only; establish_channel() commits it to\n+\t\t\t * max_num_cqs once, so a later event cannot grow the\n+\t\t\t * bound past the allocation.  Pairs with its READ_ONCE().\n+\t\t\t */\n+\t\t\tWRITE_ONCE(hwc-\u003ehwc_init_max_num_cqs, val);\n \t\t\tbreak;\n \n \t\tcase HWC_INIT_DATA_PDID:\n@@ -192,7 +309,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\n \n \t\tswitch (type) {\n \t\tcase HWC_DATA_CFG_HWC_TIMEOUT:\n-\t\t\thwc-\u003ehwc_timeout = val;\n+\t\t\tmana_hwc_timeout_update(hwc, val);\n \t\t\tbreak;\n \n \t\tcase HWC_DATA_HW_LINK_CONNECT:\n@@ -382,16 +499,49 @@ static void mana_hwc_comp_event(void *ctx, struct gdma_queue *q_self)\n \tmana_gd_ring_cq(q_self, SET_ARM_BIT);\n }\n \n-static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)\n+static int mana_hwc_publish_cq(struct gdma_context *gc,\n+\t\t\t       struct gdma_queue *cq)\n {\n-\tkfree(hwc_cq-\u003ecomp_buf);\n+\tstruct gdma_queue **cq_table = READ_ONCE(gc-\u003ecq_table);\n+\tu32 id = READ_ONCE(cq-\u003eid);\n \n-\tif (hwc_cq-\u003egdma_cq)\n-\t\tmana_gd_destroy_queue(gc, hwc_cq-\u003egdma_cq);\n+\tif (!cq_table || id \u003e= READ_ONCE(gc-\u003emax_num_cqs) ||\n+\t    READ_ONCE(cq_table[id]))\n+\t\treturn -EINVAL;\n+\n+\tWRITE_ONCE(cq_table[id], cq);\n+\n+\treturn 0;\n+}\n \n+static void mana_hwc_unpublish_cq(struct gdma_context *gc,\n+\t\t\t\t  struct gdma_queue *cq)\n+{\n+\tstruct gdma_queue **cq_table = READ_ONCE(gc-\u003ecq_table);\n+\tu32 id;\n+\n+\tif (!cq_table || !cq)\n+\t\treturn;\n+\n+\tid = READ_ONCE(cq-\u003eid);\n+\tif (id \u003c READ_ONCE(gc-\u003emax_num_cqs) \u0026\u0026\n+\t    READ_ONCE(cq_table[id]) == cq)\n+\t\tWRITE_ONCE(cq_table[id], NULL);\n+}\n+\n+static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)\n+{\n+\t/* Destroy the EQ first: it deregisters the IRQ and drains in-flight\n+\t * handlers, so none can touch the CQ after it is freed.\n+\t */\n \tif (hwc_cq-\u003egdma_eq)\n \t\tmana_gd_destroy_queue(gc, hwc_cq-\u003egdma_eq);\n \n+\t/* Safe to free now that the EQ handler is fenced. */\n+\tif (hwc_cq-\u003egdma_cq)\n+\t\tmana_gd_destroy_queue(gc, hwc_cq-\u003egdma_cq);\n+\n+\tkfree(hwc_cq-\u003ecomp_buf);\n \tkfree(hwc_cq);\n }\n \n@@ -476,7 +626,8 @@ static int mana_hwc_alloc_dma_buf(struct hw_channel_context *hwc, u16 q_depth,\n \n \tdma_buf-\u003enum_reqs = q_depth;\n \n-\tbuf_size = MANA_PAGE_ALIGN(q_depth * max_msg_size);\n+\t/* mana_gd_alloc_memory() requires a power-of-two length. */\n+\tbuf_size = roundup_pow_of_two(MANA_PAGE_ALIGN(q_depth * max_msg_size));\n \n \tgmi = \u0026dma_buf-\u003emem_info;\n \terr = mana_gd_alloc_memory(gc, buf_size, gmi, false);\n@@ -560,6 +711,7 @@ static int mana_hwc_create_wq(struct hw_channel_context *hwc,\n \thwc_wq-\u003egdma_wq = queue;\n \thwc_wq-\u003equeue_depth = q_depth;\n \thwc_wq-\u003ehwc_cq = hwc_cq;\n+\tspin_lock_init(\u0026hwc_wq-\u003elock);\n \n \terr = mana_hwc_alloc_dma_buf(hwc, q_depth, max_msg_size,\n \t\t\t\t     \u0026hwc_wq-\u003emsg_buf);\n@@ -577,7 +729,7 @@ static int mana_hwc_create_wq(struct hw_channel_context *hwc,\n \treturn err;\n }\n \n-static int mana_hwc_post_tx_wqe(const struct hwc_wq *hwc_txq,\n+static int mana_hwc_post_tx_wqe(struct hwc_wq *hwc_txq,\n \t\t\t\tstruct hwc_work_request *req,\n \t\t\t\tu32 dest_virt_rq_id, u32 dest_virt_rcq_id,\n \t\t\t\tbool dest_pf)\n@@ -616,7 +768,10 @@ static int mana_hwc_post_tx_wqe(const struct hwc_wq *hwc_txq,\n \treq-\u003ewqe_req.inline_oob_data = tx_oob;\n \treq-\u003ewqe_req.client_data_unit = 0;\n \n+\tspin_lock(\u0026hwc_txq-\u003elock);\n \terr = mana_gd_post_and_ring(hwc_txq-\u003egdma_wq, \u0026req-\u003ewqe_req, NULL);\n+\tspin_unlock(\u0026hwc_txq-\u003elock);\n+\n \tif (err)\n \t\tdev_err(dev, \"Failed to post WQE on HWC SQ: %d\\n\", err);\n \treturn err;\n@@ -635,47 +790,81 @@ static int mana_hwc_init_inflight_msg(struct hw_channel_context *hwc,\n \treturn err;\n }\n \n-static int mana_hwc_test_channel(struct hw_channel_context *hwc, u16 q_depth,\n-\t\t\t\t u32 max_req_msg_size, u32 max_resp_msg_size)\n+static int mana_hwc_test_channel(struct hw_channel_context *hwc)\n {\n-\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n \tstruct hwc_wq *hwc_rxq = hwc-\u003erxq;\n \tstruct hwc_work_request *req;\n \tstruct hwc_caller_ctx *ctx;\n+\tunsigned long flags;\n \tint err;\n \tint i;\n \n \t/* Post all WQEs on the RQ */\n-\tfor (i = 0; i \u003c q_depth; i++) {\n+\tfor (i = 0; i \u003c hwc-\u003enum_inflight_msg; i++) {\n \t\treq = \u0026hwc_rxq-\u003emsg_buf-\u003ereqs[i];\n \t\terr = mana_hwc_post_rx_wqe(hwc_rxq, req);\n \t\tif (err)\n \t\t\treturn err;\n \t}\n \n-\tctx = kzalloc_objs(*ctx, q_depth);\n+\tctx = kzalloc_objs(*ctx, hwc-\u003enum_inflight_msg);\n \tif (!ctx)\n \t\treturn -ENOMEM;\n \n-\tfor (i = 0; i \u003c q_depth; ++i)\n+\tfor (i = 0; i \u003c hwc-\u003enum_inflight_msg; ++i) {\n \t\tinit_completion(\u0026ctx[i].comp_event);\n+\t\tspin_lock_init(\u0026ctx[i].lock);\n+\t}\n \n \thwc-\u003ecaller_ctx = ctx;\n \n-\treturn mana_gd_test_eq(gc, hwc-\u003ecq-\u003egdma_eq);\n+\t/* Setup owns hwc directly; runtime publication follows this test. */\n+\tspin_lock_irqsave(\u0026hwc-\u003einflight_msg_res.lock, flags);\n+\thwc-\u003echannel_up = true;\n+\tspin_unlock_irqrestore(\u0026hwc-\u003einflight_msg_res.lock, flags);\n+\n+\terr = mana_gd_test_hwc_eq(hwc, hwc-\u003ecq-\u003egdma_eq);\n+\tif (err) {\n+\t\tspin_lock_irqsave(\u0026hwc-\u003einflight_msg_res.lock, flags);\n+\t\thwc-\u003echannel_up = false;\n+\t\tspin_unlock_irqrestore(\u0026hwc-\u003einflight_msg_res.lock, flags);\n+\t}\n+\n+\treturn err;\n }\n \n-static int mana_hwc_establish_channel(struct gdma_context *gc, u16 *q_depth,\n-\t\t\t\t      u32 *max_req_msg_size,\n-\t\t\t\t      u32 *max_resp_msg_size)\n+struct mana_hwc_init_report {\n+\tu32 queue_depth;\n+\tu32 max_req_msg_size;\n+\tu32 max_resp_msg_size;\n+};\n+\n+static int\n+mana_hwc_establish_channel(struct hw_channel_context *hwc,\n+\t\t\t   struct mana_hwc_init_report *report)\n {\n-\tstruct hw_channel_context *hwc = gc-\u003ehwc.driver_data;\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n \tstruct gdma_queue *rq = hwc-\u003erxq-\u003egdma_wq;\n \tstruct gdma_queue *sq = hwc-\u003etxq-\u003egdma_wq;\n \tstruct gdma_queue *eq = hwc-\u003ecq-\u003egdma_eq;\n \tstruct gdma_queue *cq = hwc-\u003ecq-\u003egdma_cq;\n+\tstruct gdma_queue **cq_table;\n+\tu32 num_cqs;\n+\tu32 cq_id;\n \tint err;\n \n+\thwc-\u003ehwc_init_q_depth_max = 0;\n+\thwc-\u003ehwc_init_max_req_msg_size = 0;\n+\thwc-\u003ehwc_init_max_resp_msg_size = 0;\n+\thwc-\u003ehwc_init_max_num_cqs = 0;\n+\thwc-\u003ehwc_init_cq_id = 0;\n+\thwc-\u003ehwc_init_doorbell = false;\n+\tgc-\u003ehwc.pdid = INVALID_PDID;\n+\t/* Re-establish must not rearm through the previous channel's doorbell. */\n+\tgc-\u003ehwc.doorbell = INVALID_DOORBELL;\n+\thwc-\u003edest_vrq_id = 0;\n+\thwc-\u003edest_vrcq_id = 0;\n+\n \tinit_completion(\u0026hwc-\u003ehwc_init_eqe_comp);\n \n \terr = mana_smc_setup_hwc(\u0026gc-\u003eshm_channel, false,\n@@ -683,26 +872,61 @@ static int mana_hwc_establish_channel(struct gdma_context *gc, u16 *q_depth,\n \t\t\t\t cq-\u003emem_info.dma_handle,\n \t\t\t\t rq-\u003emem_info.dma_handle,\n \t\t\t\t sq-\u003emem_info.dma_handle,\n-\t\t\t\t eq-\u003eeq.msix_index);\n+\t\t\t\t eq-\u003eeq.msix_index, \u0026hwc-\u003esetup_active);\n \tif (err)\n \t\treturn err;\n \n \tif (!wait_for_completion_timeout(\u0026hwc-\u003ehwc_init_eqe_comp, 60 * HZ))\n \t\treturn -ETIMEDOUT;\n \n-\t*q_depth = hwc-\u003ehwc_init_q_depth_max;\n-\t*max_req_msg_size = hwc-\u003ehwc_init_max_req_msg_size;\n-\t*max_resp_msg_size = hwc-\u003ehwc_init_max_resp_msg_size;\n+\tif (!hwc-\u003ehwc_init_doorbell) {\n+\t\tdev_err(hwc-\u003edev, \"HWC: missing valid doorbell in init data\\n\");\n+\t\treturn -EPROTO;\n+\t}\n \n-\t/* Both were set in mana_hwc_init_event_handler(). */\n-\tif (WARN_ON(cq-\u003eid \u003e= gc-\u003emax_num_cqs))\n+\treport-\u003equeue_depth = hwc-\u003ehwc_init_q_depth_max;\n+\treport-\u003emax_req_msg_size = hwc-\u003ehwc_init_max_req_msg_size;\n+\treport-\u003emax_resp_msg_size = hwc-\u003ehwc_init_max_resp_msg_size;\n+\n+\t/* Snapshot the device-reported count and id once, so the same value\n+\t * sizes, bounds and indexes cq_table even across the sleeping\n+\t * vcalloc() and a concurrent init event.\n+\t */\n+\tnum_cqs = READ_ONCE(hwc-\u003ehwc_init_max_num_cqs);\n+\tcq_id = READ_ONCE(hwc-\u003ehwc_init_cq_id);\n+\n+\t/* Both operands come from untrusted HWC bootstrap events; a missing\n+\t * MAX_NUM_CQS leaves num_cqs at 0.  Reject rather than WARN_ON() so a\n+\t * malformed device response cannot panic a panic_on_warn guest.\n+\t */\n+\tif (cq_id \u003e= num_cqs) {\n+\t\tdev_err_ratelimited(hwc-\u003edev,\n+\t\t\t\t    \"HWC: bad CQ id %u \u003e= max %u\\n\",\n+\t\t\t\t    cq_id, num_cqs);\n \t\treturn -EPROTO;\n+\t}\n \n-\tgc-\u003ecq_table = vcalloc(gc-\u003emax_num_cqs, sizeof(struct gdma_queue *));\n-\tif (!gc-\u003ecq_table)\n+\t/* Init events remain enabled, so commit the validated CQ ID once. */\n+\tWRITE_ONCE(cq-\u003eid, cq_id);\n+\n+\tcq_table = vcalloc(num_cqs, sizeof(*cq_table));\n+\tif (!cq_table)\n \t\treturn -ENOMEM;\n \n-\tgc-\u003ecq_table[cq-\u003eid] = cq;\n+\t/* Publish the bound and the initialised table together; the release\n+\t * pairs with smp_load_acquire() in mana_gd_process_eqe().\n+\t */\n+\tWRITE_ONCE(gc-\u003emax_num_cqs, num_cqs);\n+\t/* Pairs with smp_load_acquire() in mana_gd_process_eqe(). */\n+\tsmp_store_release(\u0026gc-\u003ecq_table, cq_table);\n+\n+\terr = mana_hwc_publish_cq(gc, cq);\n+\tif (err) {\n+\t\tdev_err_ratelimited(hwc-\u003edev,\n+\t\t\t\t    \"HWC: failed to publish CQ %u: %d\\n\",\n+\t\t\t\t    cq_id, err);\n+\t\treturn err;\n+\t}\n \n \treturn 0;\n }\n@@ -712,6 +936,9 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth,\n {\n \tint err;\n \n+\tif (q_depth \u003e U16_MAX / 2)\n+\t\treturn -EINVAL;\n+\n \terr = mana_hwc_init_inflight_msg(hwc, q_depth);\n \tif (err)\n \t\treturn err;\n@@ -744,6 +971,7 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth,\n \n \thwc-\u003enum_inflight_msg = q_depth;\n \thwc-\u003emax_req_msg_size = max_req_msg_size;\n+\thwc-\u003emax_resp_msg_size = max_resp_msg_size;\n \n \treturn 0;\n out:\n@@ -751,23 +979,373 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth,\n \treturn err;\n }\n \n+static void mana_hwc_clear_cq_table(struct gdma_context *gc)\n+{\n+\tstruct gdma_queue **cq_table;\n+\n+\tcq_table = READ_ONCE(gc-\u003ecq_table);\n+\tWRITE_ONCE(gc-\u003emax_num_cqs, 0);\n+\t/* Stop new table readers before waiting for prior RCU readers. */\n+\tsmp_store_release(\u0026gc-\u003ecq_table, NULL);\n+\tsynchronize_rcu();\n+\tvfree(cq_table);\n+}\n+\n+/* Setup owns an unpublished HWC and has no runtime senders here. */\n+static void mana_hwc_destroy_queues(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\n+\tif (hwc-\u003ecq) {\n+\t\tmana_hwc_destroy_cq(gc, hwc-\u003ecq);\n+\t\thwc-\u003ecq = NULL;\n+\t}\n+\tmana_hwc_clear_cq_table(gc);\n+\n+\tif (hwc-\u003etxq) {\n+\t\tmana_hwc_destroy_wq(hwc, hwc-\u003etxq);\n+\t\thwc-\u003etxq = NULL;\n+\t}\n+\tif (hwc-\u003erxq) {\n+\t\tmana_hwc_destroy_wq(hwc, hwc-\u003erxq);\n+\t\thwc-\u003erxq = NULL;\n+\t}\n+\n+\tkfree(hwc-\u003ecaller_ctx);\n+\thwc-\u003ecaller_ctx = NULL;\n+\tmana_gd_free_res_map(\u0026hwc-\u003einflight_msg_res);\n+\thwc-\u003enum_inflight_msg = 0;\n+}\n+\n+static int mana_hwc_publish_channel(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\tunsigned long flags;\n+\tint err = 0;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\tif (WARN_ON_ONCE(gc-\u003ehwc.driver_data))\n+\t\terr = -EBUSY;\n+\telse\n+\t\tgc-\u003ehwc.driver_data = hwc;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\treturn err;\n+}\n+\n+static struct hw_channel_context *\n+mana_hwc_unpublish_channel(struct gdma_context *gc)\n+{\n+\tstruct hw_channel_context *hwc;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\thwc = gc-\u003ehwc.driver_data;\n+\tgc-\u003ehwc.driver_data = NULL;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\treturn hwc;\n+}\n+\n+static void mana_hwc_retain_channel(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\tif (WARN_ON_ONCE(gc-\u003ehwc.driver_data))\n+\t\tdev_err(hwc-\u003edev, \"HWC retention slot is already occupied\\n\");\n+\telse\n+\t\tgc-\u003ehwc.driver_data = hwc;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+}\n+\n+static bool mana_hwc_senders_drained(struct gdma_context *gc,\n+\t\t\t\t     struct hw_channel_context *hwc)\n+{\n+\tunsigned long flags;\n+\tbool drained;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\tdrained = hwc-\u003eactive_senders == 0;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\treturn drained;\n+}\n+\n+static void mana_hwc_stop_channel(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_resource *r = \u0026hwc-\u003einflight_msg_res;\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\tunsigned long flags;\n+\tint i;\n+\n+\tif (hwc-\u003enum_inflight_msg) {\n+\t\tspin_lock_irqsave(\u0026r-\u003elock, flags);\n+\t\thwc-\u003echannel_up = false;\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\t\t/* Wake one admission waiter; each rejected waiter returns the\n+\t\t * permit and wakes the next.\n+\t\t */\n+\t\tup(\u0026hwc-\u003esema);\n+\t}\n+\tmana_hwc_timeout_cancel(hwc);\n+\n+\tfor (i = 0; hwc-\u003ecaller_ctx \u0026\u0026 i \u003c hwc-\u003enum_inflight_msg; i++) {\n+\t\tstruct hwc_caller_ctx *ctx;\n+\t\tbool drop_resp_ref;\n+\n+\t\tspin_lock_irqsave(\u0026r-\u003elock, flags);\n+\t\tif (!test_bit(i, r-\u003emap)) {\n+\t\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tctx = \u0026hwc-\u003ecaller_ctx[i];\n+\t\tspin_lock(\u0026ctx-\u003elock);\n+\t\tspin_unlock(\u0026r-\u003elock);\n+\n+\t\tif (!ctx-\u003eresponded)\n+\t\t\tctx-\u003eerror = -ENODEV;\n+\t\tctx-\u003eoutput_buf = NULL;\n+\t\tdrop_resp_ref = ctx-\u003eresp_pending;\n+\t\tctx-\u003eresp_pending = false;\n+\t\tctx-\u003eresponded = true;\n+\t\tcomplete(\u0026ctx-\u003ecomp_event);\n+\t\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n+\n+\t\tif (drop_resp_ref)\n+\t\t\thwc_ctx_put(hwc, ctx);\n+\t}\n+\n+\twait_event(gc-\u003ehwc_drain_waitq, mana_hwc_senders_drained(gc, hwc));\n+}\n+\n+static void mana_hwc_fence_channel(struct gdma_context *gc,\n+\t\t\t\t   struct hw_channel_context *hwc)\n+{\n+\tif (!hwc-\u003ecq)\n+\t\treturn;\n+\n+\tif (hwc-\u003ecq-\u003egdma_eq)\n+\t\tmana_gd_fence_eq(gc, hwc-\u003ecq-\u003egdma_eq);\n+\n+\tif (hwc-\u003ecq-\u003egdma_cq)\n+\t\tmana_hwc_unpublish_cq(gc, hwc-\u003ecq-\u003egdma_cq);\n+}\n+\n+static int mana_hwc_teardown_queues(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\tint err;\n+\n+\terr = mana_smc_teardown_hwc(\u0026gc-\u003eshm_channel, false,\n+\t\t\t\t    \u0026hwc-\u003esetup_active);\n+\tif (err) {\n+\t\tdev_err(hwc-\u003edev,\n+\t\t\t\"HWC teardown failed: %d, retaining PF-visible resources\\n\",\n+\t\t\terr);\n+\t\tmana_hwc_fence_channel(gc, hwc);\n+\t\treturn err;\n+\t}\n+\n+\tmana_hwc_destroy_queues(hwc);\n+\n+\treturn 0;\n+}\n+\n+static bool mana_hwc_release_channel(struct hw_channel_context *hwc)\n+{\n+\tstruct gdma_context *gc = hwc-\u003egdma_dev-\u003egdma_context;\n+\tint err;\n+\n+\tmana_hwc_stop_channel(hwc);\n+\n+\terr = mana_hwc_teardown_queues(hwc);\n+\tif (err) {\n+\t\tmana_hwc_retain_channel(hwc);\n+\t\treturn false;\n+\t}\n+\n+\thwc-\u003egdma_dev-\u003edoorbell = INVALID_DOORBELL;\n+\thwc-\u003egdma_dev-\u003epdid = INVALID_PDID;\n+\n+\tkfree(hwc);\n+\tgc-\u003ehwc.gdma_context = NULL;\n+\n+\treturn true;\n+}\n+\n+static int\n+mana_hwc_validate_report(struct hw_channel_context *hwc,\n+\t\t\t const struct mana_hwc_init_report *report,\n+\t\t\t u32 expected_depth, bool check_depth,\n+\t\t\t bool require_exact_msg_sizes)\n+{\n+\tif (!report-\u003equeue_depth || !report-\u003emax_req_msg_size ||\n+\t    !report-\u003emax_resp_msg_size) {\n+\t\tdev_err(hwc-\u003edev,\n+\t\t\t\"HWC: invalid maxima depth=%u req=%u resp=%u\\n\",\n+\t\t\treport-\u003equeue_depth, report-\u003emax_req_msg_size,\n+\t\t\treport-\u003emax_resp_msg_size);\n+\t\treturn -EPROTO;\n+\t}\n+\n+\tif (require_exact_msg_sizes \u0026\u0026\n+\t    (report-\u003emax_req_msg_size != HW_CHANNEL_MAX_REQUEST_SIZE ||\n+\t     report-\u003emax_resp_msg_size != HW_CHANNEL_MAX_RESPONSE_SIZE)) {\n+\t\tdev_err(hwc-\u003edev,\n+\t\t\t\"HWC: rebuilt message maxima req=%u resp=%u, expected %u/%u\\n\",\n+\t\t\treport-\u003emax_req_msg_size, report-\u003emax_resp_msg_size,\n+\t\t\tHW_CHANNEL_MAX_REQUEST_SIZE,\n+\t\t\tHW_CHANNEL_MAX_RESPONSE_SIZE);\n+\t\treturn -EPROTO;\n+\t}\n+\n+\tif (check_depth \u0026\u0026 report-\u003equeue_depth != expected_depth) {\n+\t\tdev_err(hwc-\u003edev, \"HWC: rebuilt depth %u, expected %u\\n\",\n+\t\t\treport-\u003equeue_depth, expected_depth);\n+\t\treturn -EPROTO;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int mana_hwc_build_channel(struct hw_channel_context *hwc, u16 q_depth,\n+\t\t\t\t  struct mana_hwc_init_report *report)\n+{\n+\tint err;\n+\n+\terr = mana_hwc_init_queues(hwc, q_depth,\n+\t\t\t\t   HW_CHANNEL_MAX_REQUEST_SIZE,\n+\t\t\t\t   HW_CHANNEL_MAX_RESPONSE_SIZE);\n+\tif (err) {\n+\t\tdev_err(hwc-\u003edev, \"Failed to initialize HWC depth %u: %d\\n\",\n+\t\t\tq_depth, err);\n+\t\treturn err;\n+\t}\n+\n+\terr = mana_hwc_establish_channel(hwc, report);\n+\tif (err)\n+\t\tdev_err(hwc-\u003edev, \"Failed to establish HWC depth %u: %d\\n\",\n+\t\t\tq_depth, err);\n+\n+\treturn err;\n+}\n+\n+static int\n+mana_hwc_restore_bootstrap(struct hw_channel_context *hwc,\n+\t\t\t   struct mana_hwc_init_report *report)\n+{\n+\tint err;\n+\n+\terr = mana_hwc_build_channel(hwc,\n+\t\t\t\t     HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,\n+\t\t\t\t     report);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn mana_hwc_validate_report(hwc, report, 0, false, false);\n+}\n+\n+static int mana_hwc_rebuild_channel(struct hw_channel_context *hwc,\n+\t\t\t\t    u16 q_depth,\n+\t\t\t\t    struct mana_hwc_init_report *report)\n+{\n+\tint cleanup_err;\n+\tint err;\n+\n+\terr = mana_hwc_teardown_queues(hwc);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = mana_hwc_build_channel(hwc, q_depth, report);\n+\tif (!err)\n+\t\terr = mana_hwc_validate_report(hwc, report, q_depth, true, true);\n+\tif (!err)\n+\t\treturn 0;\n+\n+\tdev_warn(hwc-\u003edev,\n+\t\t \"HWC depth %u rebuild failed, restoring bootstrap: %d\\n\",\n+\t\t q_depth, err);\n+\n+\tcleanup_err = mana_hwc_teardown_queues(hwc);\n+\tif (cleanup_err)\n+\t\treturn cleanup_err;\n+\n+\treturn mana_hwc_restore_bootstrap(hwc, report);\n+}\n+\n+static int mana_hwc_prepare_channel(struct hw_channel_context *hwc)\n+{\n+\tstruct mana_hwc_init_report report;\n+\tu16 rebuild_depth;\n+\tint err;\n+\n+\terr = mana_hwc_build_channel(hwc,\n+\t\t\t\t     HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,\n+\t\t\t\t     \u0026report);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = mana_hwc_validate_report(hwc, \u0026report, 0, false, false);\n+\tif (err)\n+\t\treturn err;\n+\n+\tif (report.queue_depth \u003e HW_CHANNEL_MAX_QUEUE_DEPTH) {\n+\t\tdev_warn(hwc-\u003edev,\n+\t\t\t \"HWC depth %u exceeds limit %u, keeping bootstrap\\n\",\n+\t\t\t report.queue_depth, HW_CHANNEL_MAX_QUEUE_DEPTH);\n+\t} else if (report.queue_depth \u003e\n+\t\t   HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH \u0026\u0026\n+\t\t   (report.max_req_msg_size != HW_CHANNEL_MAX_REQUEST_SIZE ||\n+\t\t    report.max_resp_msg_size != HW_CHANNEL_MAX_RESPONSE_SIZE)) {\n+\t\tdev_warn(hwc-\u003edev,\n+\t\t\t \"HWC maxima req=%u resp=%u differ from rebuild policy %u/%u, keeping bootstrap\\n\",\n+\t\t\t report.max_req_msg_size, report.max_resp_msg_size,\n+\t\t\t HW_CHANNEL_MAX_REQUEST_SIZE,\n+\t\t\t HW_CHANNEL_MAX_RESPONSE_SIZE);\n+\t} else if (report.queue_depth \u003e\n+\t\t   HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH) {\n+\t\trebuild_depth = report.queue_depth;\n+\t\terr = mana_hwc_rebuild_channel(hwc, rebuild_depth, \u0026report);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\t/* MAX_REQUEST sizes the RQ; MAX_RESPONSE sizes the SQ. */\n+\thwc-\u003erx_msg_size_limit = report.max_req_msg_size;\n+\thwc-\u003etx_msg_size_limit = report.max_resp_msg_size;\n+\n+\terr = mana_hwc_test_channel(hwc);\n+\tif (err)\n+\t\tdev_err(hwc-\u003edev, \"Failed to test HWC: %d\\n\", err);\n+\n+\treturn err;\n+}\n+\n int mana_hwc_create_channel(struct gdma_context *gc)\n {\n-\tu32 max_req_msg_size, max_resp_msg_size;\n \tstruct gdma_dev *gd = \u0026gc-\u003ehwc;\n \tstruct hw_channel_context *hwc;\n-\tu16 q_depth_max;\n \tint err;\n \n+\t/* Retry a retained context before assigning queues to the PF again. */\n+\tif (gd-\u003edriver_data) {\n+\t\tmana_hwc_destroy_channel(gc);\n+\t\tif (gd-\u003edriver_data)\n+\t\t\treturn -ETIMEDOUT;\n+\t}\n+\n \thwc = kzalloc_obj(*hwc);\n \tif (!hwc)\n \t\treturn -ENOMEM;\n \n \tgd-\u003egdma_context = gc;\n-\tgd-\u003edriver_data = hwc;\n \thwc-\u003egdma_dev = gd;\n \thwc-\u003edev = gc-\u003edev;\n-\thwc-\u003ehwc_timeout = HW_CHANNEL_WAIT_RESOURCE_TIMEOUT_MS;\n+\tWRITE_ONCE(hwc-\u003ehwc_timeout, HW_CHANNEL_WAIT_RESOURCE_TIMEOUT_MS);\n+\tinit_waitqueue_head(\u0026gc-\u003ehwc_drain_waitq);\n \n \t/* HWC's instance number is always 0. */\n \tgd-\u003edev_id.as_uint32 = 0;\n@@ -776,164 +1354,194 @@ int mana_hwc_create_channel(struct gdma_context *gc)\n \tgd-\u003epdid = INVALID_PDID;\n \tgd-\u003edoorbell = INVALID_DOORBELL;\n \n-\t/* mana_hwc_init_queues() only creates the required data structures,\n-\t * and doesn't touch the HWC device.\n-\t */\n-\terr = mana_hwc_init_queues(hwc, HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,\n-\t\t\t\t   HW_CHANNEL_MAX_REQUEST_SIZE,\n-\t\t\t\t   HW_CHANNEL_MAX_RESPONSE_SIZE);\n-\tif (err) {\n-\t\tdev_err(hwc-\u003edev, \"Failed to initialize HWC: %d\\n\", err);\n-\t\tgoto out;\n-\t}\n-\n-\terr = mana_hwc_establish_channel(gc, \u0026q_depth_max, \u0026max_req_msg_size,\n-\t\t\t\t\t \u0026max_resp_msg_size);\n+\terr = mana_hwc_prepare_channel(hwc);\n \tif (err) {\n-\t\tdev_err(hwc-\u003edev, \"Failed to establish HWC: %d\\n\", err);\n-\t\tgoto out;\n+\t\tmana_hwc_release_channel(hwc);\n+\t\treturn err;\n \t}\n \n-\terr = mana_hwc_test_channel(gc-\u003ehwc.driver_data,\n-\t\t\t\t    HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,\n-\t\t\t\t    max_req_msg_size, max_resp_msg_size);\n+\terr = mana_hwc_publish_channel(hwc);\n \tif (err) {\n-\t\tdev_err(hwc-\u003edev, \"Failed to test HWC: %d\\n\", err);\n-\t\tgoto out;\n+\t\tmana_hwc_release_channel(hwc);\n+\t\treturn err;\n \t}\n \n \treturn 0;\n-out:\n-\tmana_hwc_destroy_channel(gc);\n-\treturn err;\n }\n \n void mana_hwc_destroy_channel(struct gdma_context *gc)\n {\n-\tstruct hw_channel_context *hwc = gc-\u003ehwc.driver_data;\n+\tstruct hw_channel_context *hwc;\n \n+\thwc = mana_hwc_unpublish_channel(gc);\n \tif (!hwc)\n \t\treturn;\n \n-\t/* gc-\u003emax_num_cqs is set in mana_hwc_init_event_handler(). If it's\n-\t * non-zero, the HWC worked and we should tear down the HWC here.\n-\t */\n-\tif (gc-\u003emax_num_cqs \u003e 0) {\n-\t\tmana_smc_teardown_hwc(\u0026gc-\u003eshm_channel, false);\n-\t\tgc-\u003emax_num_cqs = 0;\n+\tmana_hwc_release_channel(hwc);\n+}\n+\n+static void mana_hwc_abort_request(struct hw_channel_context *hwc,\n+\t\t\t\t   struct hwc_caller_ctx *ctx)\n+{\n+\tunsigned long flags;\n+\tbool drop_resp_ref;\n+\n+\tspin_lock_irqsave(\u0026ctx-\u003elock, flags);\n+\tctx-\u003eoutput_buf = NULL;\n+\tdrop_resp_ref = ctx-\u003eresp_pending;\n+\tctx-\u003eresp_pending = false;\n+\tctx-\u003eresponded = true;\n+\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n+\n+\tif (drop_resp_ref)\n+\t\thwc_ctx_put(hwc, ctx);\n+\thwc_ctx_put(hwc, ctx);\n+}\n+\n+static int mana_hwc_submit_request(struct hw_channel_context *hwc,\n+\t\t\t\t   struct hwc_caller_ctx *ctx,\n+\t\t\t\t   struct hwc_work_request *tx_wr)\n+{\n+\tunsigned long flags;\n+\tbool drop_resp_ref = false;\n+\tint err;\n+\n+\tspin_lock_irqsave(\u0026ctx-\u003elock, flags);\n+\tif (ctx-\u003eresponded) {\n+\t\terr = ctx-\u003eerror ?: -ENODEV;\n+\t} else {\n+\t\terr = mana_hwc_post_tx_wqe(hwc-\u003etxq, tx_wr,\n+\t\t\t\t\t   hwc-\u003edest_vrq_id,\n+\t\t\t\t\t   hwc-\u003edest_vrcq_id, false);\n+\t\tif (err) {\n+\t\t\tctx-\u003eoutput_buf = NULL;\n+\t\t\tdrop_resp_ref = ctx-\u003eresp_pending;\n+\t\t\tctx-\u003eresp_pending = false;\n+\t\t\tctx-\u003eresponded = true;\n+\t\t}\n \t}\n+\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n \n-\tif (hwc-\u003etxq)\n-\t\tmana_hwc_destroy_wq(hwc, hwc-\u003etxq);\n+\tif (!err)\n+\t\treturn 0;\n \n-\tif (hwc-\u003erxq)\n-\t\tmana_hwc_destroy_wq(hwc, hwc-\u003erxq);\n+\tif (drop_resp_ref)\n+\t\thwc_ctx_put(hwc, ctx);\n+\thwc_ctx_put(hwc, ctx);\n \n-\tif (hwc-\u003ecq)\n-\t\tmana_hwc_destroy_cq(hwc-\u003egdma_dev-\u003egdma_context, hwc-\u003ecq);\n+\treturn err;\n+}\n \n-\tkfree(hwc-\u003ecaller_ctx);\n-\thwc-\u003ecaller_ctx = NULL;\n+static int mana_hwc_response_result(struct hw_channel_context *hwc,\n+\t\t\t\t    u32 command, int err, u32 status)\n+{\n+\tif (err)\n+\t\treturn err;\n \n-\tmana_gd_free_res_map(\u0026hwc-\u003einflight_msg_res);\n+\tif (!status || status == GDMA_STATUS_MORE_ENTRIES)\n+\t\treturn 0;\n \n-\thwc-\u003enum_inflight_msg = 0;\n+\tif (status == GDMA_STATUS_CMD_UNSUPPORTED)\n+\t\treturn -EOPNOTSUPP;\n \n-\thwc-\u003egdma_dev-\u003edoorbell = INVALID_DOORBELL;\n-\thwc-\u003egdma_dev-\u003epdid = INVALID_PDID;\n+\tif (command != MANA_QUERY_PHY_STAT)\n+\t\tdev_err(hwc-\u003edev, \"Command 0x%x failed with status: 0x%x\\n\",\n+\t\t\tcommand, status);\n \n-\thwc-\u003ehwc_timeout = 0;\n+\treturn -EPROTO;\n+}\n \n-\tkfree(hwc);\n-\tgc-\u003ehwc.driver_data = NULL;\n-\tgc-\u003ehwc.gdma_context = NULL;\n+static int mana_hwc_wait_for_response(struct hw_channel_context *hwc,\n+\t\t\t\t      struct hwc_caller_ctx *ctx,\n+\t\t\t\t      u32 command)\n+{\n+\tunsigned long flags;\n+\tbool abandoned = false;\n+\tu32 wait_ms;\n+\tu32 status;\n+\tint err;\n+\n+\twait_ms = mana_hwc_timeout_read(hwc);\n+\tif (wait_for_completion_timeout(\u0026ctx-\u003ecomp_event,\n+\t\t\t\t\tmsecs_to_jiffies(wait_ms))) {\n+\t\tspin_lock_irqsave(\u0026ctx-\u003elock, flags);\n+\t\tctx-\u003eoutput_buf = NULL;\n+\t\terr = ctx-\u003eerror;\n+\t\tstatus = ctx-\u003estatus_code;\n+\t\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n+\t\thwc_ctx_put(hwc, ctx);\n+\n+\t\treturn mana_hwc_response_result(hwc, command, err, status);\n+\t}\n+\n+\tspin_lock_irqsave(\u0026ctx-\u003elock, flags);\n+\tctx-\u003eoutput_buf = NULL;\n+\terr = ctx-\u003eerror;\n+\tstatus = ctx-\u003estatus_code;\n+\tif (err == -EINPROGRESS) {\n+\t\tctx-\u003eresponded = true;\n+\t\tabandoned = true;\n+\t}\n+\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n+\n+\tif (!abandoned) {\n+\t\thwc_ctx_put(hwc, ctx);\n+\t\treturn mana_hwc_response_result(hwc, command, err, status);\n+\t}\n+\n+\tif (wait_ms)\n+\t\tdev_err(hwc-\u003edev, \"Command 0x%x timed out: %u ms\\n\",\n+\t\t\tcommand, wait_ms);\n+\n+\tmana_hwc_timeout_reduce(hwc);\n+\thwc_ctx_put(hwc, ctx);\n \n-\tvfree(gc-\u003ecq_table);\n-\tgc-\u003ecq_table = NULL;\n+\treturn -ETIMEDOUT;\n }\n \n int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,\n \t\t\t  const void *req, u32 resp_len, void *resp)\n {\n \tstruct hwc_work_request *tx_wr;\n-\tstruct hwc_wq *txq = hwc-\u003etxq;\n \tstruct gdma_req_hdr *req_msg;\n \tstruct hwc_caller_ctx *ctx;\n-\tu32 dest_vrcq;\n-\tu32 dest_vrq;\n \tu32 command;\n-\tu16 msg_id;\n+\tu32 req_limit;\n+\tu32 resp_limit;\n \tint err;\n \n-\tmana_hwc_get_msg_index(hwc, \u0026msg_id);\n+\treq_limit = min(hwc-\u003etx_msg_size_limit, hwc-\u003emax_resp_msg_size);\n+\tresp_limit = min(hwc-\u003erx_msg_size_limit, hwc-\u003emax_req_msg_size);\n+\tif (req_len \u003e req_limit || resp_len \u003e resp_limit) {\n+\t\tdev_err(hwc-\u003edev,\n+\t\t\t\"HWC: message sizes req=%u/%u resp=%u/%u exceed channel maxima\\n\",\n+\t\t\treq_len, req_limit, resp_len, resp_limit);\n+\t\treturn -EMSGSIZE;\n+\t}\n \n-\ttx_wr = \u0026txq-\u003emsg_buf-\u003ereqs[msg_id];\n+\terr = mana_hwc_get_msg_index(hwc, resp, resp_len, \u0026ctx);\n+\tif (err)\n+\t\treturn err;\n \n+\ttx_wr = \u0026hwc-\u003etxq-\u003emsg_buf-\u003ereqs[ctx-\u003emsg_id];\n \tif (req_len \u003e tx_wr-\u003ebuf_len) {\n \t\tdev_err(hwc-\u003edev, \"HWC: req msg size: %d \u003e %d\\n\", req_len,\n \t\t\ttx_wr-\u003ebuf_len);\n-\t\terr = -EINVAL;\n-\t\tgoto out;\n+\t\tmana_hwc_abort_request(hwc, ctx);\n+\t\treturn -EINVAL;\n \t}\n \n-\tctx = hwc-\u003ecaller_ctx + msg_id;\n-\tctx-\u003eoutput_buf = resp;\n-\tctx-\u003eoutput_buflen = resp_len;\n-\n-\treq_msg = (struct gdma_req_hdr *)tx_wr-\u003ebuf_va;\n+\treq_msg = tx_wr-\u003ebuf_va;\n \tif (req)\n \t\tmemcpy(req_msg, req, req_len);\n-\n-\treq_msg-\u003ereq.hwc_msg_id = msg_id;\n+\treq_msg-\u003ereq.hwc_msg_id = ctx-\u003emsg_id;\n \n \ttx_wr-\u003emsg_size = req_len;\n \tcommand = req_msg-\u003ereq.msg_type;\n \n-\t/* The hardware reports the HWC destination queues through\n-\t * HWC_INIT_DATA_DEST_RQ_ID and HWC_INIT_DATA_DEST_CQ_ID, and\n-\t * always supplies values that are valid for this function, so no\n-\t * PF-specific handling is needed here.\n-\t */\n-\tdest_vrq = hwc-\u003edest_vrq_id;\n-\tdest_vrcq = hwc-\u003edest_vrcq_id;\n-\n-\terr = mana_hwc_post_tx_wqe(txq, tx_wr, dest_vrq, dest_vrcq, false);\n-\tif (err) {\n-\t\tdev_err(hwc-\u003edev, \"HWC: Failed to post send WQE: %d\\n\", err);\n-\t\tgoto out;\n-\t}\n-\n-\tif (!wait_for_completion_timeout(\u0026ctx-\u003ecomp_event,\n-\t\t\t\t\t (msecs_to_jiffies(hwc-\u003ehwc_timeout)))) {\n-\t\tif (hwc-\u003ehwc_timeout != 0)\n-\t\t\tdev_err(hwc-\u003edev, \"Command 0x%x timed out: %u ms\\n\",\n-\t\t\t\tcommand, hwc-\u003ehwc_timeout);\n-\n-\t\t/* Reduce further waiting if HWC no response */\n-\t\tif (hwc-\u003ehwc_timeout \u003e 1)\n-\t\t\thwc-\u003ehwc_timeout = 1;\n-\n-\t\terr = -ETIMEDOUT;\n-\t\tgoto out;\n-\t}\n-\n-\tif (ctx-\u003eerror) {\n-\t\terr = ctx-\u003eerror;\n-\t\tgoto out;\n-\t}\n+\terr = mana_hwc_submit_request(hwc, ctx, tx_wr);\n+\tif (err)\n+\t\treturn err;\n \n-\tif (ctx-\u003estatus_code \u0026\u0026 ctx-\u003estatus_code != GDMA_STATUS_MORE_ENTRIES) {\n-\t\tif (ctx-\u003estatus_code == GDMA_STATUS_CMD_UNSUPPORTED) {\n-\t\t\terr = -EOPNOTSUPP;\n-\t\t\tgoto out;\n-\t\t}\n-\t\tif (command != MANA_QUERY_PHY_STAT)\n-\t\t\tdev_err(hwc-\u003edev, \"Command 0x%x failed with status: 0x%x\\n\",\n-\t\t\t\tcommand, ctx-\u003estatus_code);\n-\t\terr = -EPROTO;\n-\t\tgoto out;\n-\t}\n-out:\n-\tmana_hwc_put_msg_index(hwc, msg_id);\n-\treturn err;\n+\treturn mana_hwc_wait_for_response(hwc, ctx, command);\n }\ndiff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c\nindex d21b5db06e509..df9d05062dd57 100644\n--- a/drivers/net/ethernet/microsoft/mana/shm_channel.c\n+++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c\n@@ -125,13 +125,24 @@ static int mana_smc_read_response(struct shm_channel *sc, u32 msg_type,\n void mana_smc_init(struct shm_channel *sc, struct device *dev,\n \t\t   void __iomem *base)\n {\n+\t/* The first call precedes channel publication. Later calls refresh the\n+\t * BAR mapping after reset or resume without reinitializing a live lock.\n+\t */\n+\tif (!sc-\u003etransaction_lock_initialized) {\n+\t\tmutex_init(\u0026sc-\u003etransaction_lock);\n+\t\tsc-\u003etransaction_lock_initialized = true;\n+\t}\n+\n+\tmutex_lock(\u0026sc-\u003etransaction_lock);\n \tsc-\u003edev = dev;\n \tsc-\u003ebase = base;\n+\tmutex_unlock(\u0026sc-\u003etransaction_lock);\n }\n \n-int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n-\t\t       u64 cq_addr, u64 rq_addr, u64 sq_addr,\n-\t\t       u32 eq_msix_index)\n+static int mana_smc_setup_hwc_locked(struct shm_channel *sc, bool reset_vf,\n+\t\t\t\t     u64 eq_addr, u64 cq_addr, u64 rq_addr,\n+\t\t\t\t     u64 sq_addr, u32 eq_msix_index,\n+\t\t\t\t     bool *submitted)\n {\n \tunion smc_proto_hdr *hdr;\n \tu16 all_addr_h4bits = 0;\n@@ -144,6 +155,9 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n \tint err;\n \tint i;\n \n+\tlockdep_assert_held(\u0026sc-\u003etransaction_lock);\n+\t*submitted = false;\n+\n \t/* Ensure VF already has possession of shared memory */\n \terr = mana_smc_poll_register(sc-\u003ebase, false);\n \tif (err) {\n@@ -229,6 +243,7 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n \t/* Write 256-message buffer to shared memory (final 32-bit write\n \t * triggers HW to set possession bit to PF).\n \t */\n+\t*submitted = true;\n \tdword = (u32 *)shm_buf;\n \tfor (i = 0; i \u003c SMC_APERTURE_DWORDS; i++)\n \t\twritel(*dword++, sc-\u003ebase + i * SMC_BASIC_UNIT);\n@@ -248,11 +263,32 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n \treturn 0;\n }\n \n-int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf)\n+int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n+\t\t       u64 cq_addr, u64 rq_addr, u64 sq_addr,\n+\t\t       u32 eq_msix_index, bool *submitted)\n+{\n+\tint err;\n+\n+\tmutex_lock(\u0026sc-\u003etransaction_lock);\n+\terr = mana_smc_setup_hwc_locked(sc, reset_vf, eq_addr, cq_addr,\n+\t\t\t\t\trq_addr, sq_addr, eq_msix_index,\n+\t\t\t\t\tsubmitted);\n+\tmutex_unlock(\u0026sc-\u003etransaction_lock);\n+\n+\treturn err;\n+}\n+\n+static int mana_smc_teardown_hwc_locked(struct shm_channel *sc, bool reset_vf,\n+\t\t\t\t\tbool *setup_active)\n {\n \tunion smc_proto_hdr hdr = {};\n \tint err;\n \n+\tlockdep_assert_held(\u0026sc-\u003etransaction_lock);\n+\n+\tif (!*setup_active)\n+\t\treturn 0;\n+\n \t/* Ensure already has possession of shared memory */\n \terr = mana_smc_poll_register(sc-\u003ebase, false);\n \tif (err) {\n@@ -283,5 +319,19 @@ int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf)\n \t\treturn err;\n \t}\n \n+\t*setup_active = false;\n+\n \treturn 0;\n }\n+\n+int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf,\n+\t\t\t  bool *setup_active)\n+{\n+\tint err;\n+\n+\tmutex_lock(\u0026sc-\u003etransaction_lock);\n+\terr = mana_smc_teardown_hwc_locked(sc, reset_vf, setup_active);\n+\tmutex_unlock(\u0026sc-\u003etransaction_lock);\n+\n+\treturn err;\n+}\ndiff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h\nindex 308950f9b54b0..a624b17ec7a55 100644\n--- a/include/net/mana/gdma.h\n+++ b/include/net/mana/gdma.h\n@@ -468,6 +468,15 @@ struct gdma_context {\n \t/* Hardware communication channel (HWC) */\n \tstruct gdma_dev\t\thwc;\n \n+\t/* Sender drain; the final wakeup runs under hwc_lock. */\n+\twait_queue_head_t\thwc_drain_waitq;\n+\n+\t/* Protects runtime HWC publication and active sender references.\n+\t * Setup owns an unpublished HWC directly; timeout updates use atomic\n+\t * access helpers and do not require this lock.\n+\t */\n+\tspinlock_t\t\thwc_lock;\n+\n \t/* Azure network adapter */\n \tstruct gdma_dev\t\tmana;\n \n@@ -520,8 +529,13 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd,\n \n void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue);\n \n+/* Remove an EQ from interrupt dispatch without freeing its queue memory. */\n+void mana_gd_fence_eq(struct gdma_context *gc, struct gdma_queue *queue);\n+\n int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe);\n \n+bool mana_gd_is_valid_doorbell(struct gdma_context *gc, u32 db_id);\n+\n void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit);\n \n ssize_t mana_gd_read_ring(struct gdma_queue *q, char __user *buf,\ndiff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h\nindex 1ab2d66c891e4..896effd1f716e 100644\n--- a/include/net/mana/hw_channel.h\n+++ b/include/net/mana/hw_channel.h\n@@ -11,6 +11,9 @@\n \n #define HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH 1\n \n+/* Largest supported rebuild depth; larger reports retain bootstrap queues. */\n+#define HW_CHANNEL_MAX_QUEUE_DEPTH 128\n+\n #define HWC_INIT_DATA_CQID\t\t1\n #define HWC_INIT_DATA_RQID\t\t2\n #define HWC_INIT_DATA_SQID\t\t3\n@@ -164,15 +167,32 @@ struct hwc_wq {\n \tu16 queue_depth;\n \n \tstruct hwc_cq *hwc_cq;\n+\n+\t/* Serializes SQ posting; unused for the RQ. */\n+\tspinlock_t lock;\n };\n \n struct hwc_caller_ctx {\n \tstruct completion comp_event;\n+\n+\t/* The slot is initialized while unpublished under inflight_msg_res.lock.\n+\t * Once its bitmap bit is set, lock protects every field below except\n+\t * msg_id and refcnt. The sender owns output_buf; the response handler\n+\t * may write it only while holding lock.\n+\t */\n+\tspinlock_t lock;\n \tvoid *output_buf;\n \tu32 output_buflen;\n-\n-\tu32 error; /* Linux error code */\n+\tint error;\n \tu32 status_code;\n+\tbool responded;\n+\tbool resp_pending;\n+\n+\t/* Tracks sender + response-handler ownership. The last put releases\n+\t * the bitmap slot under inflight_msg_res.lock.\n+\t */\n+\trefcount_t refcnt;\n+\tu16 msg_id;\n };\n \n struct hw_channel_context {\n@@ -181,10 +201,17 @@ struct hw_channel_context {\n \n \tu16 num_inflight_msg;\n \tu32 max_req_msg_size;\n+\tu32 max_resp_msg_size;\n+\t/* Immutable reported maxima for the final RQ and SQ directions. */\n+\tu32 rx_msg_size_limit;\n+\tu32 tx_msg_size_limit;\n \n-\tu16 hwc_init_q_depth_max;\n+\tu32 hwc_init_q_depth_max;\n \tu32 hwc_init_max_req_msg_size;\n \tu32 hwc_init_max_resp_msg_size;\n+\tu32 hwc_init_max_num_cqs;\n+\tu32 hwc_init_cq_id;\n+\tbool hwc_init_doorbell;\n \n \tstruct completion hwc_init_eqe_comp;\n \n@@ -192,13 +219,30 @@ struct hw_channel_context {\n \tstruct hwc_wq *txq;\n \tstruct hwc_cq *cq;\n \n+\t/* Admission permits. Timed-out requests retain theirs until a\n+\t * response or teardown releases the slot.\n+\t */\n \tstruct semaphore sema;\n \tstruct gdma_resource inflight_msg_res;\n \n \tu32 dest_vrq_id;\n \tu32 dest_vrcq_id;\n+\n+\t/* Zero permanently cancels waits for this channel instance. Firmware\n+\t * and query updates may replace a live nonzero value; fail-fast may\n+\t * only reduce a live value to one millisecond.\n+\t */\n \tu32 hwc_timeout;\n \n+\t/* Checked after slot acquisition; cleared on teardown to reject sends. */\n+\tbool channel_up;\n+\n+\t/* The PF may own the HWC queues while this is true. */\n+\tbool setup_active;\n+\n+\t/* mana_gd_send_request() callers, including waiters; under hwc_lock. */\n+\tunsigned int active_senders;\n+\n \tstruct hwc_caller_ctx *caller_ctx;\n };\n \n@@ -207,5 +251,11 @@ void mana_hwc_destroy_channel(struct gdma_context *gc);\n \n int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,\n \t\t\t  const void *req, u32 resp_len, void *resp);\n+int mana_gd_test_hwc_eq(struct hw_channel_context *hwc,\n+\t\t\tstruct gdma_queue *eq);\n+\n+u32 mana_hwc_timeout_read(const struct hw_channel_context *hwc);\n+void mana_hwc_timeout_update(struct hw_channel_context *hwc, u32 timeout_ms);\n+void mana_hwc_timeout_cancel(struct hw_channel_context *hwc);\n \n #endif /* _HW_CHANNEL_H */\ndiff --git a/include/net/mana/shm_channel.h b/include/net/mana/shm_channel.h\nindex dbabcfb95daf3..c8daa61fa759e 100644\n--- a/include/net/mana/shm_channel.h\n+++ b/include/net/mana/shm_channel.h\n@@ -4,6 +4,8 @@\n #ifndef _SHM_CHANNEL_H\n #define _SHM_CHANNEL_H\n \n+#include \u003clinux/mutex.h\u003e\n+\n #define SMC_APERTURE_BITS 256\n #define SMC_BASIC_UNIT (sizeof(u32))\n #define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8))\n@@ -13,6 +15,9 @@\n struct shm_channel {\n \tstruct device *dev;\n \tvoid __iomem *base;\n+\t/* Protects base and complete aperture request/response transactions. */\n+\tstruct mutex transaction_lock;\n+\tbool transaction_lock_initialized;\n };\n \n void mana_smc_init(struct shm_channel *sc, struct device *dev,\n@@ -20,8 +25,9 @@ void mana_smc_init(struct shm_channel *sc, struct device *dev,\n \n int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,\n \t\t       u64 cq_addr, u64 rq_addr, u64 sq_addr,\n-\t\t       u32 eq_msix_index);\n+\t\t       u32 eq_msix_index, bool *submitted);\n \n-int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf);\n+int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf,\n+\t\t\t  bool *setup_active);\n \n #endif /* _SHM_CHANNEL_H */\n"
}

2/1 2026/10/07 13:06 agent patch-evaluator
19m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Microsoft Azure Network Adapter (MANA) driver (drivers/net/ethernet/microsoft/mana/), specifically the GDMA hardware communication channel (HWC) synchronization and message handling. MANA is a vendor-specific SmartNIC / PCIe device deployed in Microsoft Azure / Hyper-V environments and is not emulated in standard QEMU or GCE testbeds. Because the modified code is structurally unreachable without specific physical or hypervisor-provided MANA hardware, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 737d578bbbea4373908f6ae6c484d02429ba067c
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Oct 7 13:06:46 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 8e9bfc1d6a2ae..571c7f1d5b211 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -162,6 +162,8 @@ static int mana_gd_init_registers(struct pci_dev *pdev)
 bool mana_need_log(struct gdma_context *gc, int err)
 {
 	struct hw_channel_context *hwc;
+	bool need_log = true;
+	unsigned long flags;
 
 	if (err != -ETIMEDOUT)
 		return true;
@@ -169,11 +171,13 @@ bool mana_need_log(struct gdma_context *gc, int err)
 	if (!gc)
 		return true;
 
+	spin_lock_irqsave(&gc->hwc_lock, flags);
 	hwc = gc->hwc.driver_data;
-	if (hwc && hwc->hwc_timeout == 0)
-		return false;
+	if (hwc && !mana_hwc_timeout_read(hwc))
+		need_log = false;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
 
-	return true;
+	return need_log;
 }
 
 static int mana_gd_query_max_resources(struct pci_dev *pdev)
@@ -317,7 +321,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 	return 0;
 }
 
-static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)
+static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 timeout_ms,
+				     u32 *new_timeout_ms)
 {
 	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct gdma_query_hwc_timeout_resp resp = {};
@@ -326,12 +331,12 @@ static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)
 
 	mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_HWC_TIMEOUT,
 			     sizeof(req), sizeof(resp));
-	req.timeout_ms = *timeout_val;
+	req.timeout_ms = timeout_ms;
 	err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
 	if (err || resp.hdr.status)
 		return err ? err : -EPROTO;
 
-	*timeout_val = resp.timeout_ms;
+	*new_timeout_ms = resp.timeout_ms;
 
 	return 0;
 }
@@ -387,9 +392,27 @@ static int mana_gd_detect_devices(struct pci_dev *pdev)
 int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,
 			 u32 resp_len, void *resp)
 {
-	struct hw_channel_context *hwc = gc->hwc.driver_data;
+	struct hw_channel_context *hwc;
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	hwc = gc->hwc.driver_data;
+	if (!hwc) {
+		spin_unlock_irqrestore(&gc->hwc_lock, flags);
+		return -ENODEV;
+	}
+	hwc->active_senders++;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+	err = mana_hwc_send_request(hwc, req_len, req, resp_len, resp);
 
-	return mana_hwc_send_request(hwc, req_len, req, resp_len, resp);
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	if (--hwc->active_senders == 0)
+		wake_up(&gc->hwc_drain_waitq);
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+	return err;
 }
 EXPORT_SYMBOL_NS(mana_gd_send_request, "NET_MANA");
 
@@ -557,12 +580,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:
@@ -710,6 +747,7 @@ static void mana_serv_reset(struct pci_dev *pdev)
 {
 	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct hw_channel_context *hwc;
+	unsigned long flags;
 	int ret;
 
 	if (!gc) {
@@ -719,14 +757,17 @@ static void mana_serv_reset(struct pci_dev *pdev)
 		return;
 	}
 
+	spin_lock_irqsave(&gc->hwc_lock, flags);
 	hwc = gc->hwc.driver_data;
 	if (!hwc) {
+		spin_unlock_irqrestore(&gc->hwc_lock, flags);
 		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;
+	mana_hwc_timeout_cancel(hwc);
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
 
 	dev_info(&pdev->dev, "MANA reset cycle start\n");
 
@@ -911,6 +952,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;
@@ -922,11 +964,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)
@@ -1101,7 +1148,9 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)
 	synchronize_rcu();
 }
 
-int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)
+static int mana_gd_test_eq_request(struct gdma_context *gc,
+				   struct hw_channel_context *hwc,
+				   struct gdma_queue *eq)
 {
 	struct gdma_generate_test_event_req req = {};
 	struct gdma_general_resp resp = {};
@@ -1119,7 +1168,12 @@ int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)
 	req.hdr.dev_id = eq->gdma_dev->dev_id;
 	req.queue_index = eq->id;
 
-	err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
+	if (hwc)
+		err = mana_hwc_send_request(hwc, sizeof(req), &req,
+					    sizeof(resp), &resp);
+	else
+		err = mana_gd_send_request(gc, sizeof(req), &req,
+					   sizeof(resp), &resp);
 	if (err) {
 		if (mana_need_log(gc, err))
 			dev_err(dev, "test_eq failed: %d\n", err);
@@ -1150,23 +1204,43 @@ 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,
+int mana_gd_test_eq(struct gdma_context *gc, struct gdma_queue *eq)
+{
+	return mana_gd_test_eq_request(gc, NULL, eq);
+}
+
+int mana_gd_test_hwc_eq(struct hw_channel_context *hwc,
+			struct gdma_queue *eq)
+{
+	return mana_gd_test_eq_request(hwc->gdma_dev->gdma_context, hwc, eq);
+}
+
+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)
@@ -1333,6 +1407,7 @@ static int mana_gd_create_dma_region(struct gdma_dev *gd,
 	if (gmi->nr_pages == 0 && !MANA_PAGE_ALIGNED(gmi->virt_addr))
 		return -EINVAL;
 
+	/* 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)
@@ -1536,9 +1611,12 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)
 	struct gdma_verify_ver_resp resp = {};
 	struct gdma_verify_ver_req req = {};
 	struct hw_channel_context *hwc;
+	u32 timeout_ms;
 	int err;
 
+	/* The setup caller must exclude concurrent HWC teardown. */
 	hwc = gc->hwc.driver_data;
+
 	mana_gd_init_req_hdr(&req.hdr, GDMA_VERIFY_VF_DRIVER_VERSION,
 			     sizeof(req), sizeof(resp));
 
@@ -1574,12 +1652,16 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)
 			   &gc->pf_cap_flags1);
 
 	if (resp.pf_cap_flags1 & GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG) {
-		err = mana_gd_query_hwc_timeout(pdev, &hwc->hwc_timeout);
+		err = mana_gd_query_hwc_timeout(pdev,
+						mana_hwc_timeout_read(hwc),
+						&timeout_ms);
 		if (err) {
 			dev_err(gc->dev, "Failed to set the hwc timeout %d\n", err);
 			return err;
 		}
-		dev_dbg(gc->dev, "set the hwc timeout to %u\n", hwc->hwc_timeout);
+		mana_hwc_timeout_update(hwc, timeout_ms);
+		dev_dbg(gc->dev, "set the hwc timeout to %u\n",
+			mana_hwc_timeout_read(hwc));
 	}
 	return 0;
 }
@@ -1607,13 +1689,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;
 	}
@@ -2532,6 +2608,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);
diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index 3bca4b683134b..c9f7db5a76a58 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -6,24 +6,99 @@
 #include <net/mana/hw_channel.h>
 #include <linux/vmalloc.h>
 
-static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)
+u32 mana_hwc_timeout_read(const struct hw_channel_context *hwc)
+{
+	return READ_ONCE(hwc->hwc_timeout);
+}
+
+void mana_hwc_timeout_update(struct hw_channel_context *hwc, u32 timeout_ms)
+{
+	u32 old_timeout;
+
+	if (!timeout_ms)
+		return;
+
+	old_timeout = mana_hwc_timeout_read(hwc);
+	while (old_timeout &&
+	       cmpxchg(&hwc->hwc_timeout, old_timeout, timeout_ms) != old_timeout)
+		old_timeout = mana_hwc_timeout_read(hwc);
+}
+
+void mana_hwc_timeout_cancel(struct hw_channel_context *hwc)
+{
+	xchg(&hwc->hwc_timeout, 0);
+}
+
+static void mana_hwc_timeout_reduce(struct hw_channel_context *hwc)
+{
+	u32 old_timeout = mana_hwc_timeout_read(hwc);
+
+	while (old_timeout > 1 &&
+	       cmpxchg(&hwc->hwc_timeout, old_timeout, 1) != old_timeout)
+		old_timeout = mana_hwc_timeout_read(hwc);
+}
+
+static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, void *resp,
+				  u32 resp_len,
+				  struct hwc_caller_ctx **caller_ctx)
 {
 	struct gdma_resource *r = &hwc->inflight_msg_res;
+	struct hwc_caller_ctx *ctx;
 	unsigned long flags;
+	bool channel_up;
+	u32 wait_ms;
 	u32 index;
 
-	down(&hwc->sema);
+	wait_ms = mana_hwc_timeout_read(hwc);
+	if (down_timeout(&hwc->sema, msecs_to_jiffies(wait_ms))) {
+		spin_lock_irqsave(&r->lock, flags);
+		channel_up = hwc->channel_up;
+		spin_unlock_irqrestore(&r->lock, flags);
+
+		/* Slot pressure is not evidence that the HWC stopped responding. */
+		return channel_up ? -EBUSY : -ENODEV;
+	}
 
 	spin_lock_irqsave(&r->lock, flags);
+	if (!hwc->channel_up) {
+		spin_unlock_irqrestore(&r->lock, flags);
+		up(&hwc->sema);
+		return -ENODEV;
+	}
 
-	index = find_first_zero_bit(hwc->inflight_msg_res.map,
-				    hwc->inflight_msg_res.size);
+	/* The semaphore admits at most r->size holders at a time, so a slot
+	 * acquired above always has a free bit waiting for it here.
+	 */
+	index = find_first_zero_bit(r->map, r->size);
+	if (WARN_ON_ONCE(index >= r->size)) {
+		spin_unlock_irqrestore(&r->lock, flags);
+		up(&hwc->sema);
+		return -EIO;
+	}
 
-	bitmap_set(hwc->inflight_msg_res.map, index, 1);
+	ctx = &hwc->caller_ctx[index];
+	reinit_completion(&ctx->comp_event);
+	/* Take both references (sender + response handler) before publishing
+	 * the slot, so an early response cannot free it under the sender.
+	 */
+	refcount_set(&ctx->refcnt, 2);
+	ctx->output_buf = resp;
+	ctx->output_buflen = resp_len;
+	ctx->error = -EINPROGRESS;
+	ctx->status_code = 0;
+	ctx->responded = false;
+	ctx->resp_pending = true;
+	ctx->msg_id = index;
+
+	/* The response path takes r->lock before ctx->lock, so publishing the
+	 * bitmap last makes every field above visible before it can consume
+	 * the response-side reference.
+	 */
+	bitmap_set(r->map, index, 1);
 
 	spin_unlock_irqrestore(&r->lock, flags);
 
-	*msg_id = index;
+	*caller_ctx = ctx;
 
 	return 0;
 }
@@ -34,12 +109,19 @@ static void mana_hwc_put_msg_index(struct hw_channel_context *hwc, u16 msg_id)
 	unsigned long flags;
 
 	spin_lock_irqsave(&r->lock, flags);
-	bitmap_clear(hwc->inflight_msg_res.map, msg_id, 1);
+	bitmap_clear(r->map, msg_id, 1);
 	spin_unlock_irqrestore(&r->lock, flags);
 
 	up(&hwc->sema);
 }
 
+static void hwc_ctx_put(struct hw_channel_context *hwc,
+			struct hwc_caller_ctx *ctx)
+{
+	if (refcount_dec_and_test(&ctx->refcnt))
+		mana_hwc_put_msg_index(hwc, ctx->msg_id);
+}
+
 static int mana_hwc_verify_resp_msg(const struct hwc_caller_ctx *caller_ctx,
 				    const struct gdma_resp_hdr *resp_msg,
 				    u32 resp_len)
@@ -80,32 +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;
 
-	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;
-	err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
-	if (err)
-		goto out;
+	spin_lock(&ctx->lock);
+	spin_unlock(&r->lock);
 
-	ctx->status_code = resp_msg->status;
+	/* Consume the response-side reference exactly once. This releases a
+	 * quarantined slot after its late response arrives.
+	 */
+	release = ctx->resp_pending;
+	ctx->resp_pending = false;
 
-	memcpy(ctx->output_buf, resp_msg, resp_len);
-out:
+	if (ctx->responded) {
+		spin_unlock(&ctx->lock);
+		mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
+		if (release)
+			hwc_ctx_put(hwc, ctx);
+		return;
+	}
+	ctx->responded = true;
+
+	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->error = err;
 
-	/* Must post rx wqe before complete(), otherwise the next rx may
-	 * hit no_wqe error.
+	/* Post RX WQE before completing; the next response may arrive
+	 * immediately and needs a posted buffer.
 	 */
 	mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
-
 	complete(&ctx->comp_event);
+	spin_unlock(&ctx->lock);
+
+	if (release)
+		hwc_ctx_put(hwc, ctx);
 }
 
 static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
@@ -123,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:
@@ -134,7 +246,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
 
 		switch (type) {
 		case HWC_INIT_DATA_CQID:
-			hwc->cq->gdma_cq->id = val;
+			WRITE_ONCE(hwc->hwc_init_cq_id, val);
 			break;
 
 		case HWC_INIT_DATA_RQID:
@@ -146,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:
@@ -158,7 +271,11 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
 			break;
 
 		case HWC_INIT_DATA_MAX_NUM_CQS:
-			gd->gdma_context->max_num_cqs = val;
+			/* Store only; establish_channel() commits it to
+			 * max_num_cqs once, so a later event cannot grow the
+			 * bound past the allocation.  Pairs with its READ_ONCE().
+			 */
+			WRITE_ONCE(hwc->hwc_init_max_num_cqs, val);
 			break;
 
 		case HWC_INIT_DATA_PDID:
@@ -192,7 +309,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
 
 		switch (type) {
 		case HWC_DATA_CFG_HWC_TIMEOUT:
-			hwc->hwc_timeout = val;
+			mana_hwc_timeout_update(hwc, val);
 			break;
 
 		case HWC_DATA_HW_LINK_CONNECT:
@@ -382,16 +499,49 @@ static void mana_hwc_comp_event(void *ctx, struct gdma_queue *q_self)
 	mana_gd_ring_cq(q_self, SET_ARM_BIT);
 }
 
-static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)
+static int mana_hwc_publish_cq(struct gdma_context *gc,
+			       struct gdma_queue *cq)
 {
-	kfree(hwc_cq->comp_buf);
+	struct gdma_queue **cq_table = READ_ONCE(gc->cq_table);
+	u32 id = READ_ONCE(cq->id);
 
-	if (hwc_cq->gdma_cq)
-		mana_gd_destroy_queue(gc, hwc_cq->gdma_cq);
+	if (!cq_table || id >= READ_ONCE(gc->max_num_cqs) ||
+	    READ_ONCE(cq_table[id]))
+		return -EINVAL;
+
+	WRITE_ONCE(cq_table[id], cq);
+
+	return 0;
+}
 
+static void mana_hwc_unpublish_cq(struct gdma_context *gc,
+				  struct gdma_queue *cq)
+{
+	struct gdma_queue **cq_table = READ_ONCE(gc->cq_table);
+	u32 id;
+
+	if (!cq_table || !cq)
+		return;
+
+	id = READ_ONCE(cq->id);
+	if (id < READ_ONCE(gc->max_num_cqs) &&
+	    READ_ONCE(cq_table[id]) == cq)
+		WRITE_ONCE(cq_table[id], NULL);
+}
+
+static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)
+{
+	/* Destroy the EQ first: it deregisters the IRQ and drains in-flight
+	 * handlers, so none can touch the CQ after it is freed.
+	 */
 	if (hwc_cq->gdma_eq)
 		mana_gd_destroy_queue(gc, hwc_cq->gdma_eq);
 
+	/* Safe to free now that the EQ handler is fenced. */
+	if (hwc_cq->gdma_cq)
+		mana_gd_destroy_queue(gc, hwc_cq->gdma_cq);
+
+	kfree(hwc_cq->comp_buf);
 	kfree(hwc_cq);
 }
 
@@ -476,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);
@@ -560,6 +711,7 @@ static int mana_hwc_create_wq(struct hw_channel_context *hwc,
 	hwc_wq->gdma_wq = queue;
 	hwc_wq->queue_depth = q_depth;
 	hwc_wq->hwc_cq = hwc_cq;
+	spin_lock_init(&hwc_wq->lock);
 
 	err = mana_hwc_alloc_dma_buf(hwc, q_depth, max_msg_size,
 				     &hwc_wq->msg_buf);
@@ -577,7 +729,7 @@ static int mana_hwc_create_wq(struct hw_channel_context *hwc,
 	return err;
 }
 
-static int mana_hwc_post_tx_wqe(const struct hwc_wq *hwc_txq,
+static int mana_hwc_post_tx_wqe(struct hwc_wq *hwc_txq,
 				struct hwc_work_request *req,
 				u32 dest_virt_rq_id, u32 dest_virt_rcq_id,
 				bool dest_pf)
@@ -616,7 +768,10 @@ static int mana_hwc_post_tx_wqe(const struct hwc_wq *hwc_txq,
 	req->wqe_req.inline_oob_data = tx_oob;
 	req->wqe_req.client_data_unit = 0;
 
+	spin_lock(&hwc_txq->lock);
 	err = mana_gd_post_and_ring(hwc_txq->gdma_wq, &req->wqe_req, NULL);
+	spin_unlock(&hwc_txq->lock);
+
 	if (err)
 		dev_err(dev, "Failed to post WQE on HWC SQ: %d\n", err);
 	return err;
@@ -635,47 +790,81 @@ static int mana_hwc_init_inflight_msg(struct hw_channel_context *hwc,
 	return err;
 }
 
-static int mana_hwc_test_channel(struct hw_channel_context *hwc, u16 q_depth,
-				 u32 max_req_msg_size, u32 max_resp_msg_size)
+static int mana_hwc_test_channel(struct hw_channel_context *hwc)
 {
-	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
 	struct hwc_wq *hwc_rxq = hwc->rxq;
 	struct hwc_work_request *req;
 	struct hwc_caller_ctx *ctx;
+	unsigned long flags;
 	int err;
 	int i;
 
 	/* Post all WQEs on the RQ */
-	for (i = 0; i < q_depth; i++) {
+	for (i = 0; i < hwc->num_inflight_msg; i++) {
 		req = &hwc_rxq->msg_buf->reqs[i];
 		err = mana_hwc_post_rx_wqe(hwc_rxq, req);
 		if (err)
 			return err;
 	}
 
-	ctx = kzalloc_objs(*ctx, q_depth);
+	ctx = kzalloc_objs(*ctx, hwc->num_inflight_msg);
 	if (!ctx)
 		return -ENOMEM;
 
-	for (i = 0; i < q_depth; ++i)
+	for (i = 0; i < hwc->num_inflight_msg; ++i) {
 		init_completion(&ctx[i].comp_event);
+		spin_lock_init(&ctx[i].lock);
+	}
 
 	hwc->caller_ctx = ctx;
 
-	return mana_gd_test_eq(gc, hwc->cq->gdma_eq);
+	/* Setup owns hwc directly; runtime publication follows this test. */
+	spin_lock_irqsave(&hwc->inflight_msg_res.lock, flags);
+	hwc->channel_up = true;
+	spin_unlock_irqrestore(&hwc->inflight_msg_res.lock, flags);
+
+	err = mana_gd_test_hwc_eq(hwc, hwc->cq->gdma_eq);
+	if (err) {
+		spin_lock_irqsave(&hwc->inflight_msg_res.lock, flags);
+		hwc->channel_up = false;
+		spin_unlock_irqrestore(&hwc->inflight_msg_res.lock, flags);
+	}
+
+	return err;
 }
 
-static int mana_hwc_establish_channel(struct gdma_context *gc, 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 hw_channel_context *hwc = gc->hwc.driver_data;
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
 	struct gdma_queue *rq = hwc->rxq->gdma_wq;
 	struct gdma_queue *sq = hwc->txq->gdma_wq;
 	struct gdma_queue *eq = hwc->cq->gdma_eq;
 	struct gdma_queue *cq = hwc->cq->gdma_cq;
+	struct gdma_queue **cq_table;
+	u32 num_cqs;
+	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,
@@ -683,26 +872,61 @@ static int mana_hwc_establish_channel(struct gdma_context *gc, u16 *q_depth,
 				 cq->mem_info.dma_handle,
 				 rq->mem_info.dma_handle,
 				 sq->mem_info.dma_handle,
-				 eq->eq.msix_index);
+				 eq->eq.msix_index, &hwc->setup_active);
 	if (err)
 		return err;
 
 	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;
+	}
 
-	/* Both were set in mana_hwc_init_event_handler(). */
-	if (WARN_ON(cq->id >= gc->max_num_cqs))
+	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
+	 * vcalloc() and a concurrent init event.
+	 */
+	num_cqs = READ_ONCE(hwc->hwc_init_max_num_cqs);
+	cq_id = READ_ONCE(hwc->hwc_init_cq_id);
+
+	/* Both operands come from untrusted HWC bootstrap events; a missing
+	 * MAX_NUM_CQS leaves num_cqs at 0.  Reject rather than WARN_ON() so a
+	 * malformed device response cannot panic a panic_on_warn guest.
+	 */
+	if (cq_id >= num_cqs) {
+		dev_err_ratelimited(hwc->dev,
+				    "HWC: bad CQ id %u >= max %u\n",
+				    cq_id, num_cqs);
 		return -EPROTO;
+	}
 
-	gc->cq_table = vcalloc(gc->max_num_cqs, sizeof(struct gdma_queue *));
-	if (!gc->cq_table)
+	/* Init events remain enabled, so commit the validated CQ ID once. */
+	WRITE_ONCE(cq->id, cq_id);
+
+	cq_table = vcalloc(num_cqs, sizeof(*cq_table));
+	if (!cq_table)
 		return -ENOMEM;
 
-	gc->cq_table[cq->id] = cq;
+	/* Publish the bound and the initialised table together; the release
+	 * pairs with smp_load_acquire() in mana_gd_process_eqe().
+	 */
+	WRITE_ONCE(gc->max_num_cqs, num_cqs);
+	/* Pairs with smp_load_acquire() in mana_gd_process_eqe(). */
+	smp_store_release(&gc->cq_table, cq_table);
+
+	err = mana_hwc_publish_cq(gc, cq);
+	if (err) {
+		dev_err_ratelimited(hwc->dev,
+				    "HWC: failed to publish CQ %u: %d\n",
+				    cq_id, err);
+		return err;
+	}
 
 	return 0;
 }
@@ -712,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;
@@ -744,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:
@@ -751,23 +979,373 @@ 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;
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	if (WARN_ON_ONCE(gc->hwc.driver_data))
+		err = -EBUSY;
+	else
+		gc->hwc.driver_data = hwc;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+	return err;
+}
+
+static struct hw_channel_context *
+mana_hwc_unpublish_channel(struct gdma_context *gc)
+{
+	struct hw_channel_context *hwc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	hwc = gc->hwc.driver_data;
+	gc->hwc.driver_data = NULL;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+	return hwc;
+}
+
+static void mana_hwc_retain_channel(struct hw_channel_context *hwc)
+{
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	if (WARN_ON_ONCE(gc->hwc.driver_data))
+		dev_err(hwc->dev, "HWC retention slot is already occupied\n");
+	else
+		gc->hwc.driver_data = hwc;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+}
+
+static bool mana_hwc_senders_drained(struct gdma_context *gc,
+				     struct hw_channel_context *hwc)
+{
+	unsigned long flags;
+	bool drained;
+
+	spin_lock_irqsave(&gc->hwc_lock, flags);
+	drained = hwc->active_senders == 0;
+	spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+	return drained;
+}
+
+static void mana_hwc_stop_channel(struct hw_channel_context *hwc)
+{
+	struct gdma_resource *r = &hwc->inflight_msg_res;
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+	unsigned long flags;
+	int i;
+
+	if (hwc->num_inflight_msg) {
+		spin_lock_irqsave(&r->lock, flags);
+		hwc->channel_up = false;
+		spin_unlock_irqrestore(&r->lock, flags);
+		/* Wake one admission waiter; each rejected waiter returns the
+		 * permit and wakes the next.
+		 */
+		up(&hwc->sema);
+	}
+	mana_hwc_timeout_cancel(hwc);
+
+	for (i = 0; hwc->caller_ctx && i < hwc->num_inflight_msg; i++) {
+		struct hwc_caller_ctx *ctx;
+		bool drop_resp_ref;
+
+		spin_lock_irqsave(&r->lock, flags);
+		if (!test_bit(i, r->map)) {
+			spin_unlock_irqrestore(&r->lock, flags);
+			continue;
+		}
+
+		ctx = &hwc->caller_ctx[i];
+		spin_lock(&ctx->lock);
+		spin_unlock(&r->lock);
+
+		if (!ctx->responded)
+			ctx->error = -ENODEV;
+		ctx->output_buf = NULL;
+		drop_resp_ref = ctx->resp_pending;
+		ctx->resp_pending = false;
+		ctx->responded = true;
+		complete(&ctx->comp_event);
+		spin_unlock_irqrestore(&ctx->lock, flags);
+
+		if (drop_resp_ref)
+			hwc_ctx_put(hwc, ctx);
+	}
+
+	wait_event(gc->hwc_drain_waitq, mana_hwc_senders_drained(gc, hwc));
+}
+
+static void mana_hwc_fence_channel(struct gdma_context *gc,
+				   struct hw_channel_context *hwc)
+{
+	if (!hwc->cq)
+		return;
+
+	if (hwc->cq->gdma_eq)
+		mana_gd_fence_eq(gc, hwc->cq->gdma_eq);
+
+	if (hwc->cq->gdma_cq)
+		mana_hwc_unpublish_cq(gc, hwc->cq->gdma_cq);
+}
+
+static int mana_hwc_teardown_queues(struct hw_channel_context *hwc)
+{
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+	int err;
+
+	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 err;
+	}
+
+	mana_hwc_destroy_queues(hwc);
+
+	return 0;
+}
+
+static bool mana_hwc_release_channel(struct hw_channel_context *hwc)
+{
+	struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+	int err;
+
+	mana_hwc_stop_channel(hwc);
+
+	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;
+
+	kfree(hwc);
+	gc->hwc.gdma_context = NULL;
+
+	return true;
+}
+
+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)
+{
+	int err;
+
+	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 depth %u: %d\n",
+			q_depth, err);
+		return 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);
+
+	return err;
+}
+
 int mana_hwc_create_channel(struct gdma_context *gc)
 {
-	u32 max_req_msg_size, max_resp_msg_size;
 	struct gdma_dev *gd = &gc->hwc;
 	struct hw_channel_context *hwc;
-	u16 q_depth_max;
 	int err;
 
+	/* Retry a retained context before assigning queues to the PF again. */
+	if (gd->driver_data) {
+		mana_hwc_destroy_channel(gc);
+		if (gd->driver_data)
+			return -ETIMEDOUT;
+	}
+
 	hwc = kzalloc_obj(*hwc);
 	if (!hwc)
 		return -ENOMEM;
 
 	gd->gdma_context = gc;
-	gd->driver_data = hwc;
 	hwc->gdma_dev = gd;
 	hwc->dev = gc->dev;
-	hwc->hwc_timeout = HW_CHANNEL_WAIT_RESOURCE_TIMEOUT_MS;
+	WRITE_ONCE(hwc->hwc_timeout, HW_CHANNEL_WAIT_RESOURCE_TIMEOUT_MS);
+	init_waitqueue_head(&gc->hwc_drain_waitq);
 
 	/* HWC's instance number is always 0. */
 	gd->dev_id.as_uint32 = 0;
@@ -776,164 +1354,194 @@ int mana_hwc_create_channel(struct gdma_context *gc)
 	gd->pdid = INVALID_PDID;
 	gd->doorbell = INVALID_DOORBELL;
 
-	/* mana_hwc_init_queues() only creates the required data structures,
-	 * and doesn't touch the HWC device.
-	 */
-	err = mana_hwc_init_queues(hwc, HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,
-				   HW_CHANNEL_MAX_REQUEST_SIZE,
-				   HW_CHANNEL_MAX_RESPONSE_SIZE);
-	if (err) {
-		dev_err(hwc->dev, "Failed to initialize HWC: %d\n", err);
-		goto out;
-	}
-
-	err = mana_hwc_establish_channel(gc, &q_depth_max, &max_req_msg_size,
-					 &max_resp_msg_size);
+	err = mana_hwc_prepare_channel(hwc);
 	if (err) {
-		dev_err(hwc->dev, "Failed to establish HWC: %d\n", err);
-		goto out;
+		mana_hwc_release_channel(hwc);
+		return err;
 	}
 
-	err = mana_hwc_test_channel(gc->hwc.driver_data,
-				    HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH,
-				    max_req_msg_size, max_resp_msg_size);
+	err = mana_hwc_publish_channel(hwc);
 	if (err) {
-		dev_err(hwc->dev, "Failed to test HWC: %d\n", err);
-		goto out;
+		mana_hwc_release_channel(hwc);
+		return err;
 	}
 
 	return 0;
-out:
-	mana_hwc_destroy_channel(gc);
-	return err;
 }
 
 void mana_hwc_destroy_channel(struct gdma_context *gc)
 {
-	struct hw_channel_context *hwc = gc->hwc.driver_data;
+	struct hw_channel_context *hwc;
 
+	hwc = mana_hwc_unpublish_channel(gc);
 	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.
-	 */
-	if (gc->max_num_cqs > 0) {
-		mana_smc_teardown_hwc(&gc->shm_channel, false);
-		gc->max_num_cqs = 0;
+	mana_hwc_release_channel(hwc);
+}
+
+static void mana_hwc_abort_request(struct hw_channel_context *hwc,
+				   struct hwc_caller_ctx *ctx)
+{
+	unsigned long flags;
+	bool drop_resp_ref;
+
+	spin_lock_irqsave(&ctx->lock, flags);
+	ctx->output_buf = NULL;
+	drop_resp_ref = ctx->resp_pending;
+	ctx->resp_pending = false;
+	ctx->responded = true;
+	spin_unlock_irqrestore(&ctx->lock, flags);
+
+	if (drop_resp_ref)
+		hwc_ctx_put(hwc, ctx);
+	hwc_ctx_put(hwc, ctx);
+}
+
+static int mana_hwc_submit_request(struct hw_channel_context *hwc,
+				   struct hwc_caller_ctx *ctx,
+				   struct hwc_work_request *tx_wr)
+{
+	unsigned long flags;
+	bool drop_resp_ref = false;
+	int err;
+
+	spin_lock_irqsave(&ctx->lock, flags);
+	if (ctx->responded) {
+		err = ctx->error ?: -ENODEV;
+	} else {
+		err = mana_hwc_post_tx_wqe(hwc->txq, tx_wr,
+					   hwc->dest_vrq_id,
+					   hwc->dest_vrcq_id, false);
+		if (err) {
+			ctx->output_buf = NULL;
+			drop_resp_ref = ctx->resp_pending;
+			ctx->resp_pending = false;
+			ctx->responded = true;
+		}
 	}
+	spin_unlock_irqrestore(&ctx->lock, flags);
 
-	if (hwc->txq)
-		mana_hwc_destroy_wq(hwc, hwc->txq);
+	if (!err)
+		return 0;
 
-	if (hwc->rxq)
-		mana_hwc_destroy_wq(hwc, hwc->rxq);
+	if (drop_resp_ref)
+		hwc_ctx_put(hwc, ctx);
+	hwc_ctx_put(hwc, ctx);
 
-	if (hwc->cq)
-		mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc->cq);
+	return err;
+}
 
-	kfree(hwc->caller_ctx);
-	hwc->caller_ctx = NULL;
+static int mana_hwc_response_result(struct hw_channel_context *hwc,
+				    u32 command, int err, u32 status)
+{
+	if (err)
+		return err;
 
-	mana_gd_free_res_map(&hwc->inflight_msg_res);
+	if (!status || status == GDMA_STATUS_MORE_ENTRIES)
+		return 0;
 
-	hwc->num_inflight_msg = 0;
+	if (status == GDMA_STATUS_CMD_UNSUPPORTED)
+		return -EOPNOTSUPP;
 
-	hwc->gdma_dev->doorbell = INVALID_DOORBELL;
-	hwc->gdma_dev->pdid = INVALID_PDID;
+	if (command != MANA_QUERY_PHY_STAT)
+		dev_err(hwc->dev, "Command 0x%x failed with status: 0x%x\n",
+			command, status);
 
-	hwc->hwc_timeout = 0;
+	return -EPROTO;
+}
 
-	kfree(hwc);
-	gc->hwc.driver_data = NULL;
-	gc->hwc.gdma_context = NULL;
+static int mana_hwc_wait_for_response(struct hw_channel_context *hwc,
+				      struct hwc_caller_ctx *ctx,
+				      u32 command)
+{
+	unsigned long flags;
+	bool abandoned = false;
+	u32 wait_ms;
+	u32 status;
+	int err;
+
+	wait_ms = mana_hwc_timeout_read(hwc);
+	if (wait_for_completion_timeout(&ctx->comp_event,
+					msecs_to_jiffies(wait_ms))) {
+		spin_lock_irqsave(&ctx->lock, flags);
+		ctx->output_buf = NULL;
+		err = ctx->error;
+		status = ctx->status_code;
+		spin_unlock_irqrestore(&ctx->lock, flags);
+		hwc_ctx_put(hwc, ctx);
+
+		return mana_hwc_response_result(hwc, command, err, status);
+	}
+
+	spin_lock_irqsave(&ctx->lock, flags);
+	ctx->output_buf = NULL;
+	err = ctx->error;
+	status = ctx->status_code;
+	if (err == -EINPROGRESS) {
+		ctx->responded = true;
+		abandoned = true;
+	}
+	spin_unlock_irqrestore(&ctx->lock, flags);
+
+	if (!abandoned) {
+		hwc_ctx_put(hwc, ctx);
+		return mana_hwc_response_result(hwc, command, err, status);
+	}
+
+	if (wait_ms)
+		dev_err(hwc->dev, "Command 0x%x timed out: %u ms\n",
+			command, wait_ms);
+
+	mana_hwc_timeout_reduce(hwc);
+	hwc_ctx_put(hwc, ctx);
 
-	vfree(gc->cq_table);
-	gc->cq_table = NULL;
+	return -ETIMEDOUT;
 }
 
 int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
 			  const void *req, u32 resp_len, void *resp)
 {
 	struct hwc_work_request *tx_wr;
-	struct hwc_wq *txq = hwc->txq;
 	struct gdma_req_hdr *req_msg;
 	struct hwc_caller_ctx *ctx;
-	u32 dest_vrcq;
-	u32 dest_vrq;
 	u32 command;
-	u16 msg_id;
+	u32 req_limit;
+	u32 resp_limit;
 	int err;
 
-	mana_hwc_get_msg_index(hwc, &msg_id);
+	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;
+	}
 
-	tx_wr = &txq->msg_buf->reqs[msg_id];
+	err = mana_hwc_get_msg_index(hwc, resp, resp_len, &ctx);
+	if (err)
+		return err;
 
+	tx_wr = &hwc->txq->msg_buf->reqs[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_abort_request(hwc, ctx);
+		return -EINVAL;
 	}
 
-	ctx = hwc->caller_ctx + msg_id;
-	ctx->output_buf = resp;
-	ctx->output_buflen = resp_len;
-
-	req_msg = (struct gdma_req_hdr *)tx_wr->buf_va;
+	req_msg = tx_wr->buf_va;
 	if (req)
 		memcpy(req_msg, req, req_len);
-
-	req_msg->req.hwc_msg_id = msg_id;
+	req_msg->req.hwc_msg_id = ctx->msg_id;
 
 	tx_wr->msg_size = req_len;
 	command = req_msg->req.msg_type;
 
-	/* The hardware reports the HWC destination queues through
-	 * HWC_INIT_DATA_DEST_RQ_ID and HWC_INIT_DATA_DEST_CQ_ID, and
-	 * always supplies values that are valid for this function, so no
-	 * PF-specific handling is needed here.
-	 */
-	dest_vrq = hwc->dest_vrq_id;
-	dest_vrcq = hwc->dest_vrcq_id;
-
-	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;
-	}
-
-	if (!wait_for_completion_timeout(&ctx->comp_event,
-					 (msecs_to_jiffies(hwc->hwc_timeout)))) {
-		if (hwc->hwc_timeout != 0)
-			dev_err(hwc->dev, "Command 0x%x timed out: %u ms\n",
-				command, hwc->hwc_timeout);
-
-		/* Reduce further waiting if HWC no response */
-		if (hwc->hwc_timeout > 1)
-			hwc->hwc_timeout = 1;
-
-		err = -ETIMEDOUT;
-		goto out;
-	}
-
-	if (ctx->error) {
-		err = ctx->error;
-		goto out;
-	}
+	err = mana_hwc_submit_request(hwc, ctx, tx_wr);
+	if (err)
+		return err;
 
-	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;
+	return mana_hwc_wait_for_response(hwc, ctx, command);
 }
diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c
index d21b5db06e509..df9d05062dd57 100644
--- a/drivers/net/ethernet/microsoft/mana/shm_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c
@@ -125,13 +125,24 @@ static int mana_smc_read_response(struct shm_channel *sc, u32 msg_type,
 void mana_smc_init(struct shm_channel *sc, struct device *dev,
 		   void __iomem *base)
 {
+	/* The first call precedes channel publication. Later calls refresh the
+	 * BAR mapping after reset or resume without reinitializing a live lock.
+	 */
+	if (!sc->transaction_lock_initialized) {
+		mutex_init(&sc->transaction_lock);
+		sc->transaction_lock_initialized = true;
+	}
+
+	mutex_lock(&sc->transaction_lock);
 	sc->dev = dev;
 	sc->base = base;
+	mutex_unlock(&sc->transaction_lock);
 }
 
-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)
+static int mana_smc_setup_hwc_locked(struct shm_channel *sc, bool reset_vf,
+				     u64 eq_addr, u64 cq_addr, u64 rq_addr,
+				     u64 sq_addr, u32 eq_msix_index,
+				     bool *submitted)
 {
 	union smc_proto_hdr *hdr;
 	u16 all_addr_h4bits = 0;
@@ -144,6 +155,9 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,
 	int err;
 	int i;
 
+	lockdep_assert_held(&sc->transaction_lock);
+	*submitted = false;
+
 	/* Ensure VF already has possession of shared memory */
 	err = mana_smc_poll_register(sc->base, false);
 	if (err) {
@@ -229,6 +243,7 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,
 	/* Write 256-message buffer to shared memory (final 32-bit write
 	 * triggers HW to set possession bit to PF).
 	 */
+	*submitted = true;
 	dword = (u32 *)shm_buf;
 	for (i = 0; i < SMC_APERTURE_DWORDS; i++)
 		writel(*dword++, sc->base + i * SMC_BASIC_UNIT);
@@ -248,11 +263,32 @@ int mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr,
 	return 0;
 }
 
-int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf)
+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, bool *submitted)
+{
+	int err;
+
+	mutex_lock(&sc->transaction_lock);
+	err = mana_smc_setup_hwc_locked(sc, reset_vf, eq_addr, cq_addr,
+					rq_addr, sq_addr, eq_msix_index,
+					submitted);
+	mutex_unlock(&sc->transaction_lock);
+
+	return err;
+}
+
+static int mana_smc_teardown_hwc_locked(struct shm_channel *sc, bool reset_vf,
+					bool *setup_active)
 {
 	union smc_proto_hdr hdr = {};
 	int err;
 
+	lockdep_assert_held(&sc->transaction_lock);
+
+	if (!*setup_active)
+		return 0;
+
 	/* Ensure already has possession of shared memory */
 	err = mana_smc_poll_register(sc->base, false);
 	if (err) {
@@ -283,5 +319,19 @@ int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf)
 		return err;
 	}
 
+	*setup_active = false;
+
 	return 0;
 }
+
+int mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf,
+			  bool *setup_active)
+{
+	int err;
+
+	mutex_lock(&sc->transaction_lock);
+	err = mana_smc_teardown_hwc_locked(sc, reset_vf, setup_active);
+	mutex_unlock(&sc->transaction_lock);
+
+	return err;
+}
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 308950f9b54b0..a624b17ec7a55 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -468,6 +468,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;
 
@@ -520,8 +529,13 @@ 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);
 
+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 1ab2d66c891e4..896effd1f716e 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
@@ -164,15 +167,32 @@ struct hwc_wq {
 	u16 queue_depth;
 
 	struct hwc_cq *hwc_cq;
+
+	/* Serializes SQ posting; unused for the RQ. */
+	spinlock_t lock;
 };
 
 struct hwc_caller_ctx {
 	struct completion comp_event;
+
+	/* The slot is initialized while unpublished under inflight_msg_res.lock.
+	 * Once its bitmap bit is set, lock protects every field below except
+	 * msg_id and refcnt. The sender owns output_buf; the response handler
+	 * may write it only while holding lock.
+	 */
+	spinlock_t lock;
 	void *output_buf;
 	u32 output_buflen;
-
-	u32 error; /* Linux error code */
+	int error;
 	u32 status_code;
+	bool responded;
+	bool resp_pending;
+
+	/* Tracks sender + response-handler ownership. The last put releases
+	 * the bitmap slot under inflight_msg_res.lock.
+	 */
+	refcount_t refcnt;
+	u16 msg_id;
 };
 
 struct hw_channel_context {
@@ -181,10 +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;
 
@@ -192,13 +219,30 @@ struct hw_channel_context {
 	struct hwc_wq *txq;
 	struct hwc_cq *cq;
 
+	/* Admission permits. Timed-out requests retain theirs until a
+	 * response or teardown releases the slot.
+	 */
 	struct semaphore sema;
 	struct gdma_resource inflight_msg_res;
 
 	u32 dest_vrq_id;
 	u32 dest_vrcq_id;
+
+	/* Zero permanently cancels waits for this channel instance. Firmware
+	 * and query updates may replace a live nonzero value; fail-fast may
+	 * only reduce a live value to one millisecond.
+	 */
 	u32 hwc_timeout;
 
+	/* Checked after slot acquisition; cleared on teardown to reject sends. */
+	bool channel_up;
+
+	/* The PF may own the HWC queues while this is true. */
+	bool setup_active;
+
+	/* mana_gd_send_request() callers, including waiters; under hwc_lock. */
+	unsigned int active_senders;
+
 	struct hwc_caller_ctx *caller_ctx;
 };
 
@@ -207,5 +251,11 @@ void mana_hwc_destroy_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);
+int mana_gd_test_hwc_eq(struct hw_channel_context *hwc,
+			struct gdma_queue *eq);
+
+u32 mana_hwc_timeout_read(const struct hw_channel_context *hwc);
+void mana_hwc_timeout_update(struct hw_channel_context *hwc, u32 timeout_ms);
+void mana_hwc_timeout_cancel(struct hw_channel_context *hwc);
 
 #endif /* _HW_CHANNEL_H */
diff --git a/include/net/mana/shm_channel.h b/include/net/mana/shm_channel.h
index dbabcfb95daf3..c8daa61fa759e 100644
--- a/include/net/mana/shm_channel.h
+++ b/include/net/mana/shm_channel.h
@@ -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 */

3/2 2026/10/07 13:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25850
							output: 35
							thoughts: 160
						

Thoughts:
**Analyzing This Syzkaller Patch Series**

Okay, so I've got a patch series here related to syzkaller fuzzing, specifically for the Microsoft MANA driver. My initial reaction is to dive right in, but a methodical approach is always best, particularly with fuzzing-related changes. First things first: let's get a handle on the scope.

I need to see what's actually been changed. The files listed are: `drivers/net/ethernet/microsoft/mana/gdma_main.c`, `drivers/net/ethernet/microsoft/mana/hw_channel.c`, `drivers/net/ethernet/microsoft/mana/shm_channel.c`, and the accompanying headers `include/net/mana/gdma.h`, `include/net/mana/hw_channel.h`, and `include/net/mana/shm_channel.h`. This tells me the changes are centered around the core data path and hardware interaction of the MANA driver. Important stuff.

Next, I need to refresh my memory on what MANA actually is. Ah, right, the Microsoft Azure Network Adapter driver. Now I'm oriented. I'll need to check the Kconfig for Microsoft MANA, to understand how it's configured and what dependencies it might have. Understanding the configuration is important for reproducing the fuzzing setup. This will help determine the context of the changes and how this might impact the overall fuzzing coverage. Now, let's look at the patch itself...



4/2 2026/10/07 13:07 tool grepper
0m Args:
{
  "Expression": "MICROSOFT_MANA",
  "PathPrefixes": [
    "drivers/net/ethernet/microsoft/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/microsoft/Kconfig=16=if NET_VENDOR_MICROSOFT\ndrivers/net/ethernet/microsoft/Kconfig-17-\ndrivers/net/ethernet/microsoft/Kconfig:18:config MICROSOFT_MANA\ndrivers/net/ethernet/microsoft/Kconfig-19-\ttristate \"Microsoft Azure Network Adapter (MANA) support\"\n--\ndrivers/net/ethernet/microsoft/Makefile-4-\ndrivers/net/ethernet/microsoft/Makefile:5:obj-$(CONFIG_MICROSOFT_MANA) += mana/\n--\ndrivers/net/ethernet/microsoft/mana/Makefile-4-\ndrivers/net/ethernet/microsoft/mana/Makefile:5:obj-$(CONFIG_MICROSOFT_MANA) += mana.o\ndrivers/net/ethernet/microsoft/mana/Makefile-6-mana-objs := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o mana_bpf.o\n"
}

5/2 2026/10/07 13:07 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26619
							output: 38
							thoughts: 17
						

Thoughts:
Here's a summary of my thought process, as requested:

**Kconfig Dive**

Okay, time to get started. My initial thought is to navigate directly to the `drivers/net/ethernet/microsoft/Kconfig` file. I'm focusing on that specific location because Kconfig files are the lifeblood of configuration within the kernel. They define the build options, dependencies, and ultimately, which features and drivers get compiled into the kernel image. By checking this Kconfig file, I'm hoping to get a quick overview of the Microsoft Ethernet driver's configurability. I anticipate finding the relevant settings: enabling/disabling the driver, any feature flags (like jumbo frames or VLAN support), and potentially any platform-specific dependencies. This is my starting point for understanding how this driver interacts with the build system and, ultimately, how it might be customized. I'm expecting to use this to better understand the driver's options.



6/2 2026/10/07 13:08 tool read-file
0m Args:
{
  "File": "drivers/net/ethernet/microsoft/Kconfig",
  "FirstLine": 15,
  "LineCount": 35
}

Results:
{
  "Contents": "  15:\t\n  16:\tif NET_VENDOR_MICROSOFT\n  17:\t\n  18:\tconfig MICROSOFT_MANA\n  19:\t\ttristate \"Microsoft Azure Network Adapter (MANA) support\"\n  20:\t\tdepends on PCI_MSI\n  21:\t\tdepends on X86_64 || (ARM64 \u0026\u0026 !CPU_BIG_ENDIAN)\n  22:\t\tdepends on PCI_HYPERV\n  23:\t\tselect AUXILIARY_BUS\n  24:\t\tselect DIMLIB\n  25:\t\tselect PAGE_POOL\n  26:\t\tselect NET_SHAPER\n  27:\t\thelp\n  28:\t\t  This driver supports Microsoft Azure Network Adapter (MANA).\n  29:\t\t  So far, the driver is only supported on X86_64.\n  30:\t\n  31:\t\t  To compile this driver as a module, choose M here.\n  32:\t\t  The module will be called mana.\n  33:\t\n  34:\tendif #NET_VENDOR_MICROSOFT\n"
}

7/2 2026/10/07 13:08 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27142
							output: 48
							thoughts: 366
						

Thoughts:
**My Assessment of Worthiness to Fuzz**

Okay, so the prompt hinges on "REACHABILITY IS THE PRIMARY GATE." I need to determine if this "Microsoft Azure Network Adapter (MANA)" thing requires actual physical hardware or Hyper-V synthetic PCI, or if I can get to it in a standard QEMU environment, or if it’s just fundamentally unreachable.

Wait, hold on. Let me go back and thoroughly review the instructions, sections 1 and 2. It’s all about reachability. The rules clearly state that if the modified code is *structurally* unreachable, it’s a no-go for fuzzing, *no matter what*.

Section 2 provides the "NEGATIVE CRITERIA." I can only return WorthFuzzing=false if the modified code falls into these categories: non-kernel/non-executable changes, structurally unreachable hardware (like those vendor-specific PCIe things that need real ASICs), or unreachable execution paths. Driver teardown stuff on hot-unplug, or code for totally different architectures, that kind of thing.

But… what *is* Microsoft MANA, exactly? I need to figure out how it handles the PCI device.

Let's check `gdma_main.c` and how the PCI device is probed.



8/2 2026/10/07 13:08 tool grepper
0m Args:
{
  "Expression": "mana_gd_probe|pci_driver",
  "PathPrefixes": [
    "drivers/net/ethernet/microsoft/mana/gdma_main.c"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/microsoft/mana/gdma_main.c=2570=static bool mana_is_pf(unsigned short dev_id)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2574-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2575:static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2576-{\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2796=static const struct pci_device_id mana_id_table[] = {\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2802-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2803:static struct pci_driver mana_driver = {\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2804-\t.name\t\t= \"mana\",\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2805-\t.id_table\t= mana_id_table,\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2806:\t.probe\t\t= mana_gd_probe,\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2807-\t.remove\t\t= mana_gd_remove,\n"
}

9/2 2026/10/07 13:08 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28223
							output: 46
							thoughts: 31
						

Thoughts:
Here's my thought process, as if I were reviewing this code:

**Diving into the GDMA Implementation**

Alright, let's get right to it. I need to understand what's happening with this `mana_id_table` within the GDMA driver for Microsoft's MANA network interface. Specifically, I'm heading straight for `drivers/net/ethernet/microsoft/mana/gdma_main.c`. That's where I expect to find the core GDMA logic and, fingers crossed, where this table is defined or used. I need to trace how the `mana_id_table` is handled within that file to see how it might be interacting with the driver. I will check for its declaration, initialization, and usage within the control flow. This table holds key information, so understanding how it's handled is paramount for the GDMA's operation and, potentially, for debugging or optimizing its performance.



10/2 2026/10/07 13:10 tool read-file
0m Args:
{
  "File": "drivers/net/ethernet/microsoft/mana/gdma_main.c",
  "FirstLine": 2795,
  "LineCount": 25
}

Results:
{
  "Contents": "2795:\t\n2796:\tstatic const struct pci_device_id mana_id_table[] = {\n2797:\t\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) },\n2798:\t\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) },\n2799:\t\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) },\n2800:\t\t{ }\n2801:\t};\n2802:\t\n2803:\tstatic struct pci_driver mana_driver = {\n2804:\t\t.name\t\t= \"mana\",\n2805:\t\t.id_table\t= mana_id_table,\n2806:\t\t.probe\t\t= mana_gd_probe,\n2807:\t\t.remove\t\t= mana_gd_remove,\n2808:\t\t.suspend\t= mana_gd_suspend,\n2809:\t\t.resume\t\t= mana_gd_resume,\n2810:\t\t.shutdown\t= mana_gd_shutdown,\n2811:\t\t.sriov_configure = mana_sriov_configure,\n2812:\t};\n2813:\t\n2814:\tstatic int __init mana_driver_init(void)\n2815:\t{\n2816:\t\tint err;\n2817:\t\n2818:\t\tINIT_LIST_HEAD(\u0026mana_dev_recovery_work.dev_list);\n2819:\t\tspin_lock_init(\u0026mana_dev_recovery_work.lock);\n"
}

11/2 2026/10/07 13:10 llm patch-evaluator
5m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28928
							output: 36
							thoughts: 36
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating Microsoft GDMA Device Identification**

Alright, I need to pinpoint the Microsoft-specific hardware IDs for the GDMA implementation. My first step is to locate the definitive definitions for `PCI_VENDOR_ID_MICROSOFT` and the corresponding device IDs. Given the context, the logical place to start is within the core network driver infrastructure, specifically within the `include/net/mana/` directory, and the `gdma.h` header file seems like the most probable location. That's where I'll be looking first. I'll need to confirm that these constants exist and, critically, confirm their values. Any nuances in how the IDs are handled, such as different revisions or sub-device configurations, are also critical.



12/2 2026/10/07 13:15 tool grepper
0m Args:
{
  "Expression": "MANA_.*_DEVICE_ID",
  "PathPrefixes": [
    "include/",
    "drivers/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/microsoft/mana/gdma_main.c=2570=static bool mana_is_pf(unsigned short dev_id)\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2571-{\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2572:\treturn dev_id == MANA_PF_DEVICE_ID || dev_id == MANA_PF2_DEVICE_ID;\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2573-}\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2575=static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2621-\tgc-\u003eis_pf = mana_is_pf(pdev-\u003edevice);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2622:\tgc-\u003eis_pf2 = (pdev-\u003edevice == MANA_PF2_DEVICE_ID);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2623-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2796=static const struct pci_device_id mana_id_table[] = {\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2797:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2798:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2799:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2800-\t{ }\n--\ninclude/net/mana/gdma.h=620=struct gdma_eqe {\n--\ninclude/net/mana/gdma.h-634-\ninclude/net/mana/gdma.h:635:#define MANA_PF_DEVICE_ID 0x00B9\ninclude/net/mana/gdma.h:636:#define MANA_PF2_DEVICE_ID 0x00C1\ninclude/net/mana/gdma.h:637:#define MANA_VF_DEVICE_ID 0x00BA\ninclude/net/mana/gdma.h-638-\n"
}

13/2 2026/10/07 13:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29866
							output: 46
							thoughts: 393
						

Thoughts:
**My Assessment of Microsoft Azure Network Adapter (MANA) Emulation**

Okay, so MANA. Let's break this down. MANA, or Microsoft Azure Network Adapter, is a PCIe NIC designed specifically for Azure and Hyper-V environments. It utilizes both physical functions (PF) and virtual functions (VF) with the Hyper-V synthetic PCI bus, making it a very specialized piece of hardware. The key question I need to answer is: can standard QEMU or Google Compute Engine (GCE) emulate this at all?

The initial assessment is a firm no. My internal database says that QEMU doesn't offer an emulated MANA NIC with those device IDs (`0x00B9`, `0x00BA`, `0x00C1`). This is essentially a vendor-specific SmartNIC, or Azure hardware NIC driver. This is also reinforced by this note about structurally unreachable hardware: it falls squarely into the category of requiring a physical ASIC or PCIe card, which standard QEMU just doesn't emulate.

Let me just double-check, for my own peace of mind, what MANA *is*. It is, in fact, the hardware SmartNIC / Azure network adapter that Microsoft developed for its Azure servers, using their proprietary Generic DMA engine (GDMA) and the Hyper-V PCI architecture. The point remains: Standard QEMU doesn't have it. It's a no-go. And GCE? Nope. GCE uses gVNIC or virtio-net, not MANA. MANA is Microsoft's proprietary NIC.

Could I somehow reach this via software-emulated devices, like USB gadgets or tun/tap interfaces? No, it's a PCI driver. It gets registered via `pci_register_driver(&mana_driver)` or `module_pci_driver`. Let me go back and look at the latter in `gdma_main.c` just to be certain. I am going to have to make a note of this.



14/2 2026/10/07 13:16 tool read-file
0m Args:
{
  "File": "drivers/net/ethernet/microsoft/mana/gdma_main.c",
  "FirstLine": 2820,
  "LineCount": 25
}

Results:
{
  "Contents": "2820:\t\tINIT_DELAYED_WORK(\u0026mana_dev_recovery_work.work, mana_recovery_delayed_func);\n2821:\t\n2822:\t\tmana_debugfs_root = debugfs_create_dir(\"mana\", NULL);\n2823:\t\n2824:\t\terr = pci_register_driver(\u0026mana_driver);\n2825:\t\tif (err) {\n2826:\t\t\tdebugfs_remove(mana_debugfs_root);\n2827:\t\t\tmana_debugfs_root = NULL;\n2828:\t\t}\n2829:\t\n2830:\t\treturn err;\n2831:\t}\n2832:\t\n2833:\tstatic void __exit mana_driver_exit(void)\n2834:\t{\n2835:\t\tstruct mana_dev_recovery *dev;\n2836:\t\tunsigned long flags;\n2837:\t\n2838:\t\tdisable_delayed_work_sync(\u0026mana_dev_recovery_work.work);\n2839:\t\n2840:\t\tspin_lock_irqsave(\u0026mana_dev_recovery_work.lock, flags);\n2841:\t\twhile (!list_empty(\u0026mana_dev_recovery_work.dev_list)) {\n2842:\t\t\tdev = list_first_entry(\u0026mana_dev_recovery_work.dev_list,\n2843:\t\t\t\t\t       struct mana_dev_recovery, list);\n2844:\t\t\tlist_del(\u0026dev-\u003elist);\n"
}

15/2 2026/10/07 13:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31126
							output: 38
							thoughts: 71
						

Thoughts:
Okay, here's my assessment of the situation:

**Investigating the "mana" Module's Debug Output**

Alright, I need to understand how this "mana" module is handling its debugging. My instinct is to check the usual suspects – debugfs, sysfs, or maybe even netdev if it's network-related, for any pseudo files. That's typically where modules expose internal state or control parameters for inspection. Ah, wait, here's something: `mana_debugfs_root = debugfs_create_dir("mana", NULL);`. Okay, a debugfs directory named "mana" is being created.

The immediate question that pops into my head is whether there are any actual debugfs *files* within that directory, or if "mana" is leveraging debugfs *at all*. I need to see what's being written into it and how it's being used for this to be really helpful. So, I should now trace all instances of `mana_debugfs_root` to see how it's being populated and utilized within the "mana" code. This is the key. Finding where `mana_debugfs_root` is used will reveal the debug information "mana" is attempting to expose.



16/2 2026/10/07 13:16 tool grepper
0m Args:
{
  "Expression": "mana_debugfs_root",
  "PathPrefixes": [
    "drivers/net/ethernet/microsoft/mana/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/microsoft/mana/gdma_main.c-18-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:19:struct dentry *mana_debugfs_root;\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-20-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2475=static int mana_gd_setup(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2480-\tgc-\u003emana_pci_debugfs = debugfs_create_dir(pci_name(pdev),\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2481:\t\t\t\t\t\t  mana_debugfs_root);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2482-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2814=static int __init mana_driver_init(void)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2821-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2822:\tmana_debugfs_root = debugfs_create_dir(\"mana\", NULL);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2823-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2825-\tif (err) {\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2826:\t\tdebugfs_remove(mana_debugfs_root);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2827:\t\tmana_debugfs_root = NULL;\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2828-\t}\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2833=static void __exit mana_driver_exit(void)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2851-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2852:\tdebugfs_remove(mana_debugfs_root);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2853-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2854:\tmana_debugfs_root = NULL;\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2855-}\n"
}

17/2 2026/10/07 13:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32180
							output: 27
							thoughts: 211
						

Thoughts:
**Debugging the `mana_pci_debugfs` Creation**

Okay, so I'm trying to track down how `mana_pci_debugfs` gets created. Based on the code, it looks like it should happen during `mana_gd_setup(pdev)`.  And `mana_gd_setup` is definitely called from the probe function: `mana_gd_probe` -> `mana_gd_setup`. That's the normal path. And, obviously, if there's no PCI device match, the probe function, and thus, the setup, is never even called.

So, if it's *not* being created when expected, the first thing I need to do is make sure I'm not missing an alternate entry point or another call to a modified function that *could* be responsible. Right?

Let's see...I've got a list of the modified functions and files: `drivers/net/ethernet/microsoft/mana/gdma_main.c`, `drivers/net/ethernet/microsoft/mana/hw_channel.c`, `drivers/net/ethernet/microsoft/mana/shm_channel.c`, and the relevant headers, `include/net/mana/gdma.h`, `include/net/mana/hw_channel.h`, and `include/net/mana/shm_channel.h`.  I need to search for callers of these *outside* the MANA subsystem itself. Any calls from other RDMA drivers, potentially? I'm going to scan the code for that.

Wait! Before I get too deep into that, I should double-check the `mana_ib` code. It's the most likely suspect for an unexpected interaction, even if unlikely. Let's see if there's anything going on there. I'll start with that first. It's always best to eliminate the most obvious possibilities first.



18/2 2026/10/07 13:17 tool grepper
0m Args:
{
  "Expression": "mana_.*request",
  "PathPrefixes": [
    "drivers/"
  ]
}

Results:
{
  "Output": "\nFull output is too long, showing 200 out of 456 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (9 files in total):\ndrivers/infiniband/hw/mana/counters.c:2\ndrivers/infiniband/hw/mana/main.c:20\ndrivers/infiniband/hw/mana/mana_ib.h:1\ndrivers/infiniband/hw/mana/mr.c:5\ndrivers/infiniband/hw/mana/qp.c:2\ndrivers/infiniband/hw/mana/wr.c:2\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:22\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:5\ndrivers/net/ethernet/microsoft/mana/mana_en.c:22\n\ndrivers/infiniband/hw/mana/counters.c=69=static int mana_ib_get_hw_device_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats)\n--\ndrivers/infiniband/hw/mana/counters.c-81-\ndrivers/infiniband/hw/mana/counters.c:82:\terr = mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), \u0026req,\ndrivers/infiniband/hw/mana/counters.c-83-\t\t\t\t   sizeof(resp), \u0026resp);\n--\ndrivers/infiniband/hw/mana/counters.c=100=static int mana_ib_get_hw_port_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,\n--\ndrivers/infiniband/hw/mana/counters.c-114-\ndrivers/infiniband/hw/mana/counters.c:115:\terr = mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), \u0026req,\ndrivers/infiniband/hw/mana/counters.c-116-\t\t\t\t   sizeof(resp), \u0026resp);\n--\ndrivers/infiniband/hw/mana/main.c=93=static int mana_gd_destroy_pd(struct mana_ib_dev *mdev, u64 pd_handle)\n--\ndrivers/infiniband/hw/mana/main.c-102-\ndrivers/infiniband/hw/mana/main.c:103:\treturn mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-104-}\n--\ndrivers/infiniband/hw/mana/main.c=106=int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)\n--\ndrivers/infiniband/hw/mana/main.c-138-\treq.flags = flags;\ndrivers/infiniband/hw/mana/main.c:139:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-140-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=182=static int mana_gd_destroy_doorbell_page(struct gdma_context *gc,\n--\ndrivers/infiniband/hw/mana/main.c-194-\ndrivers/infiniband/hw/mana/main.c:195:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-196-}\n--\ndrivers/infiniband/hw/mana/main.c=198=static int mana_gd_allocate_doorbell_page(struct gdma_context *gc,\n--\ndrivers/infiniband/hw/mana/main.c-214-\ndrivers/infiniband/hw/mana/main.c:215:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-216-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=337=mana_ib_gd_first_dma_region(struct mana_ib_dev *dev,\n--\ndrivers/infiniband/hw/mana/main.c-350-\ndrivers/infiniband/hw/mana/main.c:351:\terr = mana_gd_send_request(gc, create_req_msg_size, create_req,\ndrivers/infiniband/hw/mana/main.c-352-\t\t\t\t   sizeof(create_resp), \u0026create_resp);\n--\ndrivers/infiniband/hw/mana/main.c=371=mana_ib_gd_add_dma_region(struct mana_ib_dev *dev, struct gdma_context *gc,\n--\ndrivers/infiniband/hw/mana/main.c-383-\ndrivers/infiniband/hw/mana/main.c:384:\terr = mana_gd_send_request(gc, add_req_msg_size, add_req,\ndrivers/infiniband/hw/mana/main.c-385-\t\t\t\t   sizeof(add_resp), \u0026add_resp);\n--\ndrivers/infiniband/hw/mana/main.c=707=int mana_ib_gd_query_adapter_caps(struct mana_ib_dev *dev)\n--\ndrivers/infiniband/hw/mana/main.c-718-\ndrivers/infiniband/hw/mana/main.c:719:\terr = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), \u0026req,\ndrivers/infiniband/hw/mana/main.c-720-\t\t\t\t   sizeof(resp), \u0026resp);\n--\ndrivers/infiniband/hw/mana/main.c=750=int mana_eth_query_adapter_caps(struct mana_ib_dev *dev)\n--\ndrivers/infiniband/hw/mana/main.c-759-\ndrivers/infiniband/hw/mana/main.c:760:\terr = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), \u0026req,\ndrivers/infiniband/hw/mana/main.c-761-\t\t\t\t   sizeof(resp), \u0026resp);\n--\ndrivers/infiniband/hw/mana/main.c=889=int mana_ib_gd_create_rnic_adapter(struct mana_ib_dev *mdev)\n--\ndrivers/infiniband/hw/mana/main.c-903-\ndrivers/infiniband/hw/mana/main.c:904:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-905-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=912=int mana_ib_gd_destroy_rnic_adapter(struct mana_ib_dev *mdev)\n--\ndrivers/infiniband/hw/mana/main.c-922-\ndrivers/infiniband/hw/mana/main.c:923:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-924-}\n--\ndrivers/infiniband/hw/mana/main.c=926=int mana_ib_gd_add_gid(const struct ib_gid_attr *attr, void **context)\n--\ndrivers/infiniband/hw/mana/main.c-945-\ndrivers/infiniband/hw/mana/main.c:946:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-947-}\n--\ndrivers/infiniband/hw/mana/main.c=949=int mana_ib_gd_del_gid(const struct ib_gid_attr *attr, void **context)\n--\ndrivers/infiniband/hw/mana/main.c-968-\ndrivers/infiniband/hw/mana/main.c:969:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-970-}\n--\ndrivers/infiniband/hw/mana/main.c=972=int mana_ib_gd_config_mac(struct mana_ib_dev *mdev, enum mana_ib_addr_op op, u8 *mac)\n--\ndrivers/infiniband/hw/mana/main.c-983-\ndrivers/infiniband/hw/mana/main.c:984:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-985-}\n--\ndrivers/infiniband/hw/mana/main.c=987=int mana_ib_gd_create_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq, u32 doorbell)\n--\ndrivers/infiniband/hw/mana/main.c-1003-\ndrivers/infiniband/hw/mana/main.c:1004:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1005-\n--\ndrivers/infiniband/hw/mana/main.c=1019=int mana_ib_gd_destroy_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq)\n--\ndrivers/infiniband/hw/mana/main.c-1032-\ndrivers/infiniband/hw/mana/main.c:1033:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1034-}\n--\ndrivers/infiniband/hw/mana/main.c=1036=int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp,\n--\ndrivers/infiniband/hw/mana/main.c-1061-\ndrivers/infiniband/hw/mana/main.c:1062:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1063-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=1075=int mana_ib_gd_create_uc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp,\n--\ndrivers/infiniband/hw/mana/main.c-1100-\ndrivers/infiniband/hw/mana/main.c:1101:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1102-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=1114=int mana_ib_gd_destroy_rnic_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)\n--\ndrivers/infiniband/hw/mana/main.c-1124-\ndrivers/infiniband/hw/mana/main.c:1125:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1126-}\n--\ndrivers/infiniband/hw/mana/main.c=1128=int mana_ib_gd_create_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp,\n--\ndrivers/infiniband/hw/mana/main.c-1152-\treq.qp_type = type;\ndrivers/infiniband/hw/mana/main.c:1153:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1154-\tif (err)\n--\ndrivers/infiniband/hw/mana/main.c=1166=int mana_ib_gd_destroy_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)\n--\ndrivers/infiniband/hw/mana/main.c-1176-\ndrivers/infiniband/hw/mana/main.c:1177:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/main.c-1178-}\n--\ndrivers/infiniband/hw/mana/mana_ib.h=267=struct mana_ib_query_adapter_caps_resp {\n--\ndrivers/infiniband/hw/mana/mana_ib.h-289-\ndrivers/infiniband/hw/mana/mana_ib.h:290:enum mana_ib_adapter_features_request {\ndrivers/infiniband/hw/mana/mana_ib.h-291-\tMANA_IB_FEATURE_CLIENT_ERROR_CQE_REQUEST = BIT(1),\n--\ndrivers/infiniband/hw/mana/mr.c=36=static int mana_ib_gd_create_mr(struct mana_ib_dev *dev, struct mana_ib_mr *mr,\n--\ndrivers/infiniband/hw/mana/mr.c-74-\ndrivers/infiniband/hw/mana/mr.c:75:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/mr.c-76-\tif (err)\n--\ndrivers/infiniband/hw/mana/mr.c=86=static int mana_ib_gd_destroy_mr(struct mana_ib_dev *dev, u64 mr_handle)\n--\ndrivers/infiniband/hw/mana/mr.c-96-\ndrivers/infiniband/hw/mana/mr.c:97:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/mr.c-98-}\n--\ndrivers/infiniband/hw/mana/mr.c=297=static int mana_ib_gd_create_mw(struct mana_ib_dev *dev, struct mana_ib_pd *pd, struct ib_mw *ibmw)\n--\ndrivers/infiniband/hw/mana/mr.c-319-\ndrivers/infiniband/hw/mana/mr.c:320:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/mr.c-321-\tif (err)\n--\ndrivers/infiniband/hw/mana/mr.c=376=static int mana_ib_gd_alloc_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm,\n--\ndrivers/infiniband/hw/mana/mr.c-388-\ndrivers/infiniband/hw/mana/mr.c:389:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/mr.c-390-\tif (err)\n--\ndrivers/infiniband/hw/mana/mr.c=422=static int mana_ib_gd_destroy_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm)\n--\ndrivers/infiniband/hw/mana/mr.c-430-\ndrivers/infiniband/hw/mana/mr.c:431:\treturn mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/mr.c-432-}\n--\ndrivers/infiniband/hw/mana/qp.c=8=static int mana_ib_cfg_vport_steering(struct mana_ib_dev *dev,\n--\ndrivers/infiniband/hw/mana/qp.c-72-\ndrivers/infiniband/hw/mana/qp.c:73:\terr = mana_gd_send_request(gc, req_buf_size, req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/qp.c-74-\tkfree(req);\n--\ndrivers/infiniband/hw/mana/qp.c=783=static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,\n--\ndrivers/infiniband/hw/mana/qp.c-849-\ndrivers/infiniband/hw/mana/qp.c:850:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/infiniband/hw/mana/qp.c-851-\tif (err)\n--\ndrivers/infiniband/hw/mana/wr.c=10=static int mana_ib_post_recv_ud(struct mana_ib_qp *qp, const struct ib_recv_wr *wr)\n--\ndrivers/infiniband/hw/mana/wr.c-33-\ndrivers/infiniband/hw/mana/wr.c:34:\terr = mana_gd_post_work_request(queue, \u0026wqe_req, \u0026wqe_info);\ndrivers/infiniband/hw/mana/wr.c-35-\tif (err)\n--\ndrivers/infiniband/hw/mana/wr.c=75=static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr)\n--\ndrivers/infiniband/hw/mana/wr.c-127-\ndrivers/infiniband/hw/mana/wr.c:128:\terr = mana_gd_post_work_request(queue, \u0026wqe_req, \u0026wqe_info);\ndrivers/infiniband/hw/mana/wr.c-129-\tif (err)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=183=static int mana_gd_query_max_resources(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-206-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:207:\terr = mana_gd_send_request(gc, sizeof(req), \u0026req, sizeof(resp), \u0026resp);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-208-\tif (err || resp.hdr.status) {\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=324=static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 timeout_ms,\n--\n\n"
}

19/2 2026/10/07 13:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37869
							output: 42
							thoughts: 55
						

Thoughts:
**Analyzing the `mana_ib` Driver**

Okay, so I'm looking at `mana_ib` again. Right, it's the Microsoft Azure Network Adapter Infiniband/RDMA driver. Good, I needed that confirmation. Now, the real question is, how does this thing actually *probe*? I need to understand the initialization sequence, the hardware discovery. The logical place to start digging is the core – the "main" file. Let's dive into `drivers/infiniband/hw/mana/main.c` and see what it's doing there. Time to trace the execution flow and get a handle on this probing mechanism.



20/2 2026/10/07 13:17 tool grepper
0m Args:
{
  "Expression": "auxiliary_driver|mana_ib_probe",
  "PathPrefixes": [
    "drivers/infiniband/hw/mana/"
  ]
}

Results:
{
  "Output": "drivers/infiniband/hw/mana/device.c=82=static int mana_ib_netdev_event(struct notifier_block *this,\n--\ndrivers/infiniband/hw/mana/device.c-116-\ndrivers/infiniband/hw/mana/device.c:117:static int mana_ib_probe(struct auxiliary_device *adev,\ndrivers/infiniband/hw/mana/device.c-118-\t\t\t const struct auxiliary_device_id *id)\n--\ndrivers/infiniband/hw/mana/device.c=264=MODULE_DEVICE_TABLE(auxiliary, mana_id_table);\ndrivers/infiniband/hw/mana/device.c-265-\ndrivers/infiniband/hw/mana/device.c:266:static struct auxiliary_driver mana_driver = {\ndrivers/infiniband/hw/mana/device.c:267:\t.probe = mana_ib_probe,\ndrivers/infiniband/hw/mana/device.c-268-\t.remove = mana_ib_remove,\n--\ndrivers/infiniband/hw/mana/device.c-271-\ndrivers/infiniband/hw/mana/device.c:272:module_auxiliary_driver(mana_driver);\n"
}

21/2 2026/10/07 13:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38444
							output: 39
							thoughts: 207
						

Thoughts:
**My Assessment of the MANA and MANA_IB Relationship**

Okay, so here's the deal: `mana_ib` is essentially a helper, an auxiliary device, and it's completely reliant on `mana`, the network driver itself. Think of `mana` as the foundation, the PCI driver that's in charge. It's the one that does the initial probing, particularly of the physical or Hyper-V MANA NIC. And only *after* `mana` discovers the underlying hardware can it even begin to register these auxiliary devices for RDMA.

It's a two-step process: `mana` finds the hardware, then *boom*, it can register `mana_ib`. The takeaway? Without the core MANA PCI device present and functioning, neither `mana` nor `mana_ib` stands a chance of working. It’s like trying to build a house on quicksand – not going to happen.

Now, let me cross-reference this with those negative criteria outlined in Section 2. We're looking at things that are structurally unreachable, like vendor-specific PCIe switches, or those specialized SmartNICs and GPU drivers (mlxsw, pds_core, qed, ionic, amdgpu). These all need a dedicated physical ASIC/PCIe card, and we are not emulating such items in standard QEMU. That is not the case here.

To be absolutely sure, let's nail down what MANA *is*. Oh yeah, it's the Microsoft Azure Network Adapter. That makes sense, given the context. Time to delve into the commit messages and kernel documentation in `drivers/net/ethernet/microsoft/mana/` for further insights.



22/2 2026/10/07 13:18 tool grepper
0m Args:
{
  "Expression": "Microsoft Azure Network Adapter",
  "PathPrefixes": [
    "Documentation/",
    "drivers/net/ethernet/microsoft/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/microsoft/Kconfig=18=config MICROSOFT_MANA\ndrivers/net/ethernet/microsoft/Kconfig:19:\ttristate \"Microsoft Azure Network Adapter (MANA) support\"\ndrivers/net/ethernet/microsoft/Kconfig-20-\tdepends on PCI_MSI\n--\ndrivers/net/ethernet/microsoft/Kconfig-27-\thelp\ndrivers/net/ethernet/microsoft/Kconfig:28:\t  This driver supports Microsoft Azure Network Adapter (MANA).\ndrivers/net/ethernet/microsoft/Kconfig-29-\t  So far, the driver is only supported on X86_64.\n--\ndrivers/net/ethernet/microsoft/mana/Makefile-2-#\ndrivers/net/ethernet/microsoft/mana/Makefile:3:# Makefile for the Microsoft Azure Network Adapter driver\ndrivers/net/ethernet/microsoft/mana/Makefile-4-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2862=MODULE_LICENSE(\"Dual BSD/GPL\");\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2863:MODULE_DESCRIPTION(\"Microsoft Azure Network Adapter driver\");\n--\ndrivers/net/ethernet/microsoft/mana/mana_en.c=65=static void mana_put_rx_page(struct mana_rxq *rxq, struct page *page,\n--\ndrivers/net/ethernet/microsoft/mana/mana_en.c-73-\ndrivers/net/ethernet/microsoft/mana/mana_en.c:74:/* Microsoft Azure Network Adapter (MANA) functions */\ndrivers/net/ethernet/microsoft/mana/mana_en.c-75-\n--\ndrivers/net/ethernet/microsoft/mana/mana_en.c=4072=int mana_probe(struct gdma_dev *gd, bool resuming)\n--\ndrivers/net/ethernet/microsoft/mana/mana_en.c-4083-\tdev_info(dev,\ndrivers/net/ethernet/microsoft/mana/mana_en.c:4084:\t\t \"Microsoft Azure Network Adapter protocol version: %d.%d.%d\\n\",\ndrivers/net/ethernet/microsoft/mana/mana_en.c-4085-\t\t MANA_MAJOR_VERSION, MANA_MINOR_VERSION, MANA_MICRO_VERSION);\n"
}

23/2 2026/10/07 13:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39629
							output: 37
							thoughts: 31
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Analyzing the Commit's Purpose**

Alright, time to get down to brass tacks. First, I need to understand what this commit – or, more likely, this *series* of commits – is actually *doing*. I don't want to just dive into the code and start poking around blindly. That's inefficient and could lead me down a rabbit hole. The most efficient first step is always to go straight for the context. I'm going to quickly parse the commit message, and failing that the `git log -1` output, because the author should have summarized the changes there. This quick view should give me the high-level goals and the general approach. Once I have the gist of it, I can dig in. I might need to look at the diffs or delve deeper into the code, but I need that initial roadmap. This gives me a framework for understanding the rest of the work.



24/2 2026/10/07 13:19 tool grepper
0m Args:
{
  "Expression": "mana_hwc_",
  "PathPrefixes": [
    "drivers/net/ethernet/microsoft/mana/"
  ]
}

Results:
{
  "Output": "\nFull output is too long, showing 200 out of 525 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (2 files in total):\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:11\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:129\n\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=162=bool mana_need_log(struct gdma_context *gc, int err)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-175-\thwc = gc-\u003ehwc.driver_data;\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:176:\tif (hwc \u0026\u0026 !mana_hwc_timeout_read(hwc))\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-177-\t\tneed_log = false;\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=344=static int mana_gd_detect_devices(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-375-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:376:\t\t/* HWC is already detected in mana_hwc_create_channel(). */\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-377-\t\tif (dev_type == GDMA_DEVICE_HWC)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=392=int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-407-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:408:\terr = mana_hwc_send_request(hwc, req_len, req, resp_len, resp);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-409-\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=746=static void mana_serv_reset(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-768-\t/* HWC is not responding in this case, so don't wait */\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:769:\tmana_hwc_timeout_cancel(hwc);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-770-\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=1151=static int mana_gd_test_eq_request(struct gdma_context *gc,\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1171-\tif (hwc)\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:1172:\t\terr = mana_hwc_send_request(hwc, sizeof(req), \u0026req,\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1173-\t\t\t\t\t    sizeof(resp), \u0026resp);\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=1608=int mana_gd_verify_vf_version(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1655-\t\terr = mana_gd_query_hwc_timeout(pdev,\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:1656:\t\t\t\t\t\tmana_hwc_timeout_read(hwc),\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1657-\t\t\t\t\t\t\u0026timeout_ms);\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1661-\t\t}\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:1662:\t\tmana_hwc_timeout_update(hwc, timeout_ms);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1663-\t\tdev_dbg(gc-\u003edev, \"set the hwc timeout to %u\\n\",\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:1664:\t\t\tmana_hwc_timeout_read(hwc));\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-1665-\t}\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2475=static int mana_gd_setup(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2501-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2502:\terr = mana_hwc_create_channel(gc);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2503-\tif (err)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2537-destroy_hwc:\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2538:\tmana_hwc_destroy_channel(gc);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2539-remove_irq:\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c=2551=static void mana_gd_cleanup_device(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2554-\ndrivers/net/ethernet/microsoft/mana/gdma_main.c:2555:\tmana_hwc_destroy_channel(gc);\ndrivers/net/ethernet/microsoft/mana/gdma_main.c-2556-\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-8-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:9:u32 mana_hwc_timeout_read(const struct hw_channel_context *hwc)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-10-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-13-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:14:void mana_hwc_timeout_update(struct hw_channel_context *hwc, u32 timeout_ms)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-15-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-20-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:21:\told_timeout = mana_hwc_timeout_read(hwc);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-22-\twhile (old_timeout \u0026\u0026\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-23-\t       cmpxchg(\u0026hwc-\u003ehwc_timeout, old_timeout, timeout_ms) != old_timeout)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:24:\t\told_timeout = mana_hwc_timeout_read(hwc);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-25-}\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-26-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:27:void mana_hwc_timeout_cancel(struct hw_channel_context *hwc)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-28-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-31-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:32:static void mana_hwc_timeout_reduce(struct hw_channel_context *hwc)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-33-{\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:34:\tu32 old_timeout = mana_hwc_timeout_read(hwc);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-35-\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-37-\t       cmpxchg(\u0026hwc-\u003ehwc_timeout, old_timeout, 1) != old_timeout)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:38:\t\told_timeout = mana_hwc_timeout_read(hwc);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-39-}\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-40-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:41:static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, void *resp,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-42-\t\t\t\t  u32 resp_len,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-51-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:52:\twait_ms = mana_hwc_timeout_read(hwc);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-53-\tif (down_timeout(\u0026hwc-\u003esema, msecs_to_jiffies(wait_ms))) {\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-105-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:106:static void mana_hwc_put_msg_index(struct hw_channel_context *hwc, u16 msg_id)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-107-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c=118=static void hwc_ctx_put(struct hw_channel_context *hwc,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-121-\tif (refcount_dec_and_test(\u0026ctx-\u003erefcnt))\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:122:\t\tmana_hwc_put_msg_index(hwc, ctx-\u003emsg_id);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-123-}\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-124-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:125:static int mana_hwc_verify_resp_msg(const struct hwc_caller_ctx *caller_ctx,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-126-\t\t\t\t    const struct gdma_resp_hdr *resp_msg,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-137-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:138:static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-139-\t\t\t\tstruct hwc_work_request *req)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-160-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:161:static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-162-\t\t\t\t struct hwc_work_request *rx_req, u16 msg_id)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-173-\t\tdev_err(hwc-\u003edev, \"hwc_rx: invalid msg_id = %u\\n\", msg_id);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:174:\t\tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-175-\t\treturn;\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-189-\t\tspin_unlock(\u0026ctx-\u003elock);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:190:\t\tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-191-\t\tif (release)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-196-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:197:\terr = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-198-\tif (!err) {\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-206-\t */\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:207:\tmana_hwc_post_rx_wqe(hwc-\u003erxq, rx_req);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-208-\tcomplete(\u0026ctx-\u003ecomp_event);\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-214-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:215:static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-216-\t\t\t\t\tstruct gdma_event *event)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-311-\t\tcase HWC_DATA_CFG_HWC_TIMEOUT:\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:312:\t\t\tmana_hwc_timeout_update(hwc, val);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-313-\t\t\tbreak;\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-356-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:357:static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-358-\t\t\t\t      const struct hwc_rx_oob *rx_oob)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-403-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:404:\tmana_hwc_handle_resp(hwc, rx_oob-\u003etx_oob_data_size, rx_req, msg_id);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-405-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-406-\t/* Can no longer use 'resp', because the buffer is posted to the HW\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:407:\t * in mana_hwc_handle_resp() above.\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-408-\t */\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-411-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:412:static void mana_hwc_tx_event_handler(void *ctx, u32 gdma_txq_id,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-413-\t\t\t\t      const struct hwc_rx_oob *rx_oob)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-420-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:421:static int mana_hwc_create_gdma_wq(struct hw_channel_context *hwc,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-422-\t\t\t\t   enum gdma_queue_type type, u64 queue_size,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-436-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:437:static int mana_hwc_create_gdma_cq(struct hw_channel_context *hwc,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-438-\t\t\t\t   u64 queue_size,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-454-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:455:static int mana_hwc_create_gdma_eq(struct hw_channel_context *hwc,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-456-\t\t\t\t   u64 queue_size,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-472-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:473:static void mana_hwc_comp_event(void *ctx, struct gdma_queue *q_self)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-474-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-501-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:502:static int mana_hwc_publish_cq(struct gdma_context *gc,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-503-\t\t\t       struct gdma_queue *cq)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-516-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:517:static void mana_hwc_unpublish_cq(struct gdma_context *gc,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-518-\t\t\t\t  struct gdma_queue *cq)\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-531-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:532:static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-533-{\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-547-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:548:static int mana_hwc_create_cq(struct hw_channel_context *hwc, u16 q_depth,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-549-\t\t\t      gdma_eq_callback *callback, void *ctx,\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-572-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:573:\terr = mana_hwc_create_gdma_eq(hwc, eq_size, ctx, callback, \u0026eq);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-574-\tif (err) {\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-579-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:580:\terr = mana_hwc_create_gdma_cq(hwc, cq_size, hwc_cq, mana_hwc_comp_event,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-581-\t\t\t\t      eq, \u0026cq);\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-604-out:\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:605:\tmana_hwc_destroy_cq(hwc-\u003egdma_dev-\u003egdma_context, hwc_cq);\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-606-\treturn err;\n--\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-608-\ndrivers/net/ethernet/microsoft/mana/hw_channel.c:609:static int mana_hwc_alloc_dma_buf(struct hw_channel_context *hwc, u16 q_depth,\ndrivers/net/ethernet/microsoft/mana/hw_channel.c-610-\t\t\t\t  u32 max_msg_size,\n\n"
}

25/2 2026/10/07 13:19 llm patch-evaluator
7m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 45273
							output: 135
							thoughts: 963
						

Thoughts:
**MANA Driver Analysis: Structurally Unreachable Hardware**

Okay, let's break this down methodically. My initial thought is that `mana` refers to the Microsoft Azure Network Adapter, and, checking the file path, `drivers/net/ethernet/microsoft/mana/`, confirms this. I recall from the instructions that structurally unreachable hardware is a key negative criterion for fuzzing. This specifically mentions vendor-specific SmartNICs, which immediately flags MANA as a potential non-target.

The documentation explicitly states MANA requires physical ASIC/PCIe cards, and these are not emulated in standard QEMU. That's a significant point. A quick review of QEMU's supported network devices confirms that MANA isn't on the list, solidifying this suspicion. In fact, checking the config, `CONFIG_MICROSOFT_MANA` is dependent on `CONFIG_PCI_HYPERV`, which is the "Microsoft Hyper-V PCI frontend driver". That makes it explicit that it requires the Hyper-V hypervisor for the synthetic PCI bus. In other words, I'm thinking, it requires specific Azure MANA hardware or a Hyper-V environment to probe and function. This also makes the device unreachable in standard QEMU or GCE environments where software-emulated devices are used.

I need to confirm the changes and make sure all modified code falls under the 'structurally unreachable hardware' criteria. Looking at the diff, it's all within `drivers/net/ethernet/microsoft/mana/` and `include/net/mana/`. Specifically, I see `gdma_main.c`, `hw_channel.c`, `shm_channel.c` and their respective headers, related to GDMA hardware channels. Importantly, none of the changes touch core subsystems like memory management, locking, BPF, scheduler, networking, vfs, or syscalls. All the changes are related to managing the MANA hardware and channel specifically, and that channel can only be reached if the hardware device is probed. And that hardware cannot be probed without the specific Azure MANA hardware or Hyper-V synthetic PCI bus.

Given all this, the conclusion is clear: the MANA changes are not worth fuzzing. I'm thinking the reasoning is concise: the hardware is a vendor-specific PCIe/SmartNIC requiring Microsoft Hyper-V/Azure MANA ASIC and therefore unreachable in standard QEMU/GCE. The `WorthFuzzing` flag should be set to `false`. And the `FocusSymbols` and `EnableConfigs` should be empty lists.

Finally, I just need to translate this reasoning into the required format for the `set-results`: Empty `EnableConfigs`, empty `FocusSymbols`, a clear rationale, and `WorthFuzzing: false`. The reasoning statement should cover the fact that the changes target the GDMA hardware channel synchronization and message handling, and how the underlying hardware is inaccessible in typical testbeds. This whole process seems straightforward and aligns with the triage philosophy and guidelines.



26/2 2026/10/07 13:26 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Microsoft Azure Network Adapter (MANA) driver (drivers/net/ethernet/microsoft/mana/), specifically the GDMA hardware communication channel (HWC) synchronization and message handling. MANA is a vendor-specific SmartNIC / PCIe device deployed in Microsoft Azure / Hyper-V environments and is not emulated in standard QEMU or GCE testbeds. Because the modified code is structurally unreachable without specific physical or hypervisor-provided MANA hardware, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Microsoft Azure Network Adapter (MANA) driver (drivers/net/ethernet/microsoft/mana/), specifically the GDMA hardware communication channel (HWC) synchronization and message handling. MANA is a vendor-specific SmartNIC / PCIe device deployed in Microsoft Azure / Hyper-V environments and is not emulated in standard QEMU or GCE testbeds. Because the modified code is structurally unreachable without specific physical or hypervisor-provided MANA hardware, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

27/1 2026/10/07 13:26 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)