| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/16 18:23 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The changes are strictly confined to the Microsoft Azure Network Adapter (MANA) ethernet driver (drivers/net/ethernet/microsoft/mana/) and the MANA InfiniBand hardware driver (drivers/infiniband/hw/mana/). MANA is a vendor-specific SmartNIC that requires specialized physical or Hyper-V cloud hardware and is not emulated in standard QEMU or GCE virtualized environments. Consequently, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/10/16 18:23 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 3d315be1c4f3cd0567d3fed1f0c333902aa74145\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 16 18:23:27 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c\nindex e4414d208d4ac..ce840cee42cae 100644\n--- a/drivers/infiniband/hw/mana/main.c\n+++ b/drivers/infiniband/hw/mana/main.c\n@@ -406,28 +406,38 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem\n \tunsigned int create_req_msg_size;\n \tstruct hw_channel_context *hwc;\n \tstruct ib_block_iter biter;\n-\tsize_t max_pgs_add_cmd = 0;\n+\tsize_t max_pgs_add_cmd;\n \tsize_t max_pgs_create_cmd;\n \tstruct gdma_context *gc;\n \tsize_t num_pages_total;\n \tunsigned int tail = 0;\n+\tu32 req_limit;\n \tu64 *page_addr_list;\n \tvoid *request_buf;\n \tint err = 0;\n \n \tgc = mdev_to_gc(dev);\n \thwc = gc-\u003ehwc.driver_data;\n+\treq_limit = mana_hwc_get_max_req_size(hwc);\n+\tif (req_limit \u003c struct_size(create_req, page_addr_list, 1) ||\n+\t req_limit \u003c struct_size(add_req, page_addr_list, 1)) {\n+\t\tibdev_dbg(\u0026dev-\u003eib_dev,\n+\t\t\t \"HWC request budget %u is too small for DMA region commands\\n\",\n+\t\t\t req_limit);\n+\t\treturn -EMSGSIZE;\n+\t}\n \n \tnum_pages_total = ib_umem_num_dma_blocks(umem, page_sz);\n \n \tmax_pgs_create_cmd =\n-\t\t(hwc-\u003emax_req_msg_size - sizeof(*create_req)) / sizeof(u64);\n+\t\t(req_limit - sizeof(*create_req)) / sizeof(u64);\n+\tmax_pgs_add_cmd = (req_limit - sizeof(*add_req)) / sizeof(u64);\n \tnum_pages_to_handle =\n \t\tmin_t(size_t, num_pages_total, max_pgs_create_cmd);\n \tcreate_req_msg_size =\n \t\tstruct_size(create_req, page_addr_list, num_pages_to_handle);\n \n-\trequest_buf = kzalloc(hwc-\u003emax_req_msg_size, GFP_KERNEL);\n+\trequest_buf = kzalloc(req_limit, GFP_KERNEL);\n \tif (!request_buf)\n \t\treturn -ENOMEM;\n \n@@ -470,9 +480,6 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem\n \t\t\tif (err)\n \t\t\t\tgoto out;\n \n-\t\t\tmax_pgs_add_cmd = (hwc-\u003emax_req_msg_size -\n-\t\t\t\tsizeof(*add_req)) / sizeof(u64);\n-\n \t\t\tadd_req = request_buf;\n \t\t\tadd_req-\u003edma_region_handle = *gdma_region;\n \t\t\tadd_req-\u003ereserved3 = 0;\ndiff --git a/drivers/net/ethernet/microsoft/mana/gdma_cdx.c b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c\nindex 0d01e3d2f0d96..fa10681fd793e 100644\n--- a/drivers/net/ethernet/microsoft/mana/gdma_cdx.c\n+++ b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c\n@@ -221,6 +221,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)\n \n \tmutex_init(\u0026gc-\u003eeq_test_event_mutex);\n \tmutex_init(\u0026gc-\u003egic_mutex);\n+\tspin_lock_init(\u0026gc-\u003ehwc_lock);\n \tdev_set_drvdata(\u0026cdx_dev-\u003edev, gc);\n \tgc-\u003ebar0_pa = cdx_resource_start(cdx_dev, 0);\n \tgc-\u003ebar0_size = cdx_resource_len(cdx_dev, 0);\n@@ -279,7 +280,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)\n \tiounmap(bar0_va);\n free_gc:\n \tdev_set_drvdata(\u0026cdx_dev-\u003edev, NULL);\n-\tvfree(gc);\n+\tmana_gd_free_context(gc);\n clear_master:\n \tcdx_clear_master(cdx_dev);\n \treturn err;\n@@ -298,7 +299,7 @@ static int mana_cdx_gd_remove(struct cdx_device *cdx_dev)\n \tiounmap(gc-\u003ebar0_va);\n \n \tdev_set_drvdata(\u0026cdx_dev-\u003edev, NULL);\n-\tvfree(gc);\n+\tmana_gd_free_context(gc);\n \treturn 0;\n }\n \ndiff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c\nindex ae0ed700b3b93..8b08f28bfdb82 100644\n--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c\n+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c\n@@ -11,6 +11,7 @@\n #include \u003clinux/irqdomain.h\u003e\n #include \u003clinux/export.h\u003e\n #include \u003clinux/uaccess.h\u003e\n+#include \u003clinux/vmalloc.h\u003e\n \n #include \u003cnet/mana/mana.h\u003e\n #include \u003cnet/mana/hw_channel.h\u003e\n@@ -145,6 +146,8 @@ int mana_gd_init_registers(struct gdma_context *gc)\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@@ -152,11 +155,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 int mana_gd_query_max_resources(struct gdma_context *gc)\n@@ -309,7 +314,8 @@ int mana_gd_query_max_resources(struct gdma_context *gc)\n \treturn 0;\n }\n \n-static int mana_gd_query_hwc_timeout(struct gdma_context *gc, u32 *timeout_val)\n+static int mana_gd_query_hwc_timeout(struct gdma_context *gc, u32 timeout_ms,\n+\t\t\t\t u32 *new_timeout_ms)\n {\n \tstruct gdma_query_hwc_timeout_resp resp = {};\n \tstruct gdma_query_hwc_timeout_req req = {};\n@@ -317,12 +323,12 @@ static int mana_gd_query_hwc_timeout(struct gdma_context *gc, 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@@ -377,12 +383,48 @@ int mana_gd_detect_devices(struct gdma_context *gc)\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+int mana_gd_cancel_hwc_timeout(struct gdma_context *gc)\n+{\n+\tstruct hw_channel_context *hwc;\n+\tunsigned long flags;\n+\tint err = 0;\n+\n+\tspin_lock_irqsave(\u0026gc-\u003ehwc_lock, flags);\n+\thwc = gc-\u003ehwc.driver_data;\n+\tif (hwc)\n+\t\tmana_hwc_timeout_cancel(hwc);\n+\telse\n+\t\terr = -ENODEV;\n+\tspin_unlock_irqrestore(\u0026gc-\u003ehwc_lock, flags);\n+\n+\treturn err;\n+}\n+EXPORT_SYMBOL_NS(mana_gd_cancel_hwc_timeout, \"NET_MANA\");\n+\n int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length,\n \t\t\t struct gdma_mem_info *gmi, bool allow_scatter)\n {\n@@ -547,12 +589,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@@ -754,6 +810,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@@ -765,11 +822,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@@ -944,7 +1006,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@@ -962,7 +1026,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@@ -993,23 +1062,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@@ -1078,10 +1167,10 @@ static void mana_gd_destroy_cq(struct gdma_context *gc,\n \tif (id \u003e= gc-\u003emax_num_cqs)\n \t\treturn;\n \n-\tif (!gc-\u003ecq_table[id])\n+\tif (!READ_ONCE(gc-\u003ecq_table[id]))\n \t\treturn;\n \n-\tgc-\u003ecq_table[id] = NULL;\n+\tWRITE_ONCE(gc-\u003ecq_table[id], NULL);\n }\n \n int mana_gd_create_hwc_queue(struct gdma_dev *gd,\n@@ -1176,9 +1265,10 @@ 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+\tif (req_msg_size \u003e mana_hwc_get_max_req_size(hwc))\n \t\treturn -EINVAL;\n \n \treq = kzalloc(req_msg_size, GFP_KERNEL);\n@@ -1378,9 +1468,12 @@ int mana_gd_verify_vf_version(struct gdma_context *gc)\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@@ -1418,12 +1511,16 @@ int mana_gd_verify_vf_version(struct gdma_context *gc)\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(gc, \u0026hwc-\u003ehwc_timeout);\n+\t\terr = mana_gd_query_hwc_timeout(gc,\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@@ -1451,13 +1548,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@@ -2008,6 +2099,13 @@ void mana_gd_cleanup(struct gdma_context *gc)\n }\n EXPORT_SYMBOL_NS(mana_gd_cleanup, \"NET_MANA\");\n \n+void mana_gd_free_context(struct gdma_context *gc)\n+{\n+\tmana_hwc_free_retained_channel(gc);\n+\tvfree(gc);\n+}\n+EXPORT_SYMBOL_NS(mana_gd_free_context, \"NET_MANA\");\n+\n static int __init gdma_core_init(void)\n {\n \tmana_debugfs_root = debugfs_create_dir(\"mana\", NULL);\ndiff --git a/drivers/net/ethernet/microsoft/mana/gdma_pci.c b/drivers/net/ethernet/microsoft/mana/gdma_pci.c\nindex ed8925296c8a7..636d9b0dd5811 100644\n--- a/drivers/net/ethernet/microsoft/mana/gdma_pci.c\n+++ b/drivers/net/ethernet/microsoft/mana/gdma_pci.c\n@@ -14,7 +14,6 @@\n #include \u003clinux/xarray.h\u003e\n \n #include \u003cnet/mana/mana.h\u003e\n-#include \u003cnet/mana/hw_channel.h\u003e\n \n struct mana_dev_recovery {\n \tstruct list_head list;\n@@ -85,7 +84,6 @@ static void mana_serv_fpga(struct pci_dev *pdev)\n 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 \tint ret;\n \n \tif (!gc) {\n@@ -95,15 +93,13 @@ static void mana_serv_reset(struct pci_dev *pdev)\n \t\treturn;\n \t}\n \n-\thwc = gc-\u003ehwc.driver_data;\n-\tif (!hwc) {\n+\t/* HWC is not responding in this case, so don't wait */\n+\tret = mana_gd_cancel_hwc_timeout(gc);\n+\tif (ret) {\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-\n \tdev_info(\u0026pdev-\u003edev, \"MANA reset cycle start\\n\");\n \n \tmana_gd_suspend(pdev, PMSG_SUSPEND);\n@@ -674,6 +670,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);\n@@ -725,7 +722,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tpci_iounmap(pdev, bar0_va);\n free_gc:\n \tpci_set_drvdata(pdev, NULL);\n-\tvfree(gc);\n+\tmana_gd_free_context(gc);\n release_region:\n \tpci_release_regions(pdev);\n disable_dev:\n@@ -778,7 +775,7 @@ static void mana_gd_remove(struct pci_dev *pdev)\n \n \tpci_iounmap(pdev, gc-\u003ebar0_va);\n \n-\tvfree(gc);\n+\tmana_gd_free_context(gc);\n \n \tpci_release_regions(pdev);\n \tpci_disable_device(pdev);\ndiff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c\nindex 3bca4b683134b..c4cae1baaef4d 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,121 @@\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, u32 req_len,\n+\t\t\t\t void *resp, 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+\tu32 req_limit;\n+\tu32 resp_limit;\n+\tu32 wait_ms;\n \tu32 index;\n+\tint err;\n+\n+\treq_limit = mana_hwc_get_max_req_size(hwc);\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\tspin_lock_irqsave(\u0026r-\u003elock, flags);\n+\t\terr = hwc-\u003echannel_up ? -EMSGSIZE : -ENODEV;\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n+\n+\t\tif (err == -EMSGSIZE)\n+\t\t\tdev_err(hwc-\u003edev,\n+\t\t\t\t\"HWC: message sizes req=%u/%u resp=%u/%u exceed channel maxima\\n\",\n+\t\t\t\treq_len, req_limit, resp_len, resp_limit);\n+\t\treturn err;\n+\t}\n+\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\tif (!hwc-\u003echannel_up)\n+\t\t\terr = -ENODEV;\n+\t\telse if (atomic_read(\u0026hwc-\u003eabandoned_msgs))\n+\t\t\terr = -ETIMEDOUT;\n+\t\telse\n+\t\t\terr = -EBUSY;\n+\t\tspin_unlock_irqrestore(\u0026r-\u003elock, flags);\n \n-\tdown(\u0026hwc-\u003esema);\n+\t\treturn err;\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+\tspin_lock(\u0026ctx-\u003elock);\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(\u0026ctx-\u003elock);\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 +131,34 @@ 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 bool mana_hwc_clear_resp_pending(struct hw_channel_context *hwc,\n+\t\t\t\t\tstruct hwc_caller_ctx *ctx)\n+{\n+\tlockdep_assert_held(\u0026ctx-\u003elock);\n+\n+\tif (!ctx-\u003eresp_pending)\n+\t\treturn false;\n+\n+\tctx-\u003eresp_pending = false;\n+\tif (ctx-\u003eerror == -ETIMEDOUT)\n+\t\tatomic_dec(\u0026hwc-\u003eabandoned_msgs);\n+\n+\treturn true;\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 +199,53 @@ 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 = mana_hwc_clear_resp_pending(hwc, ctx);\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 +263,18 @@ 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\tWRITE_ONCE(hwc-\u003ehwc_init_error, -EPROTO);\n+\t\t\tcomplete(\u0026hwc-\u003ehwc_init_eqe_comp);\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 +284,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 +296,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 +309,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 +347,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 +537,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 +664,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 +749,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 +767,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 +806,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@@ -627,6 +820,7 @@ static int mana_hwc_init_inflight_msg(struct hw_channel_context *hwc,\n {\n \tint err;\n \n+\tatomic_set(\u0026hwc-\u003eabandoned_msgs, 0);\n \tsema_init(\u0026hwc-\u003esema, num_msg);\n \n \terr = mana_gd_alloc_res_map(num_msg, \u0026hwc-\u003einflight_msg_res);\n@@ -635,47 +829,82 @@ 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+\tWRITE_ONCE(hwc-\u003ehwc_init_error, 0);\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 +912,65 @@ 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+\terr = READ_ONCE(hwc-\u003ehwc_init_error);\n+\tif (err)\n+\t\treturn err;\n \n-\t/* Both were set in mana_hwc_init_event_handler(). */\n-\tif (WARN_ON(cq-\u003eid \u003e= gc-\u003emax_num_cqs))\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+\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-\tgc-\u003ecq_table = vcalloc(gc-\u003emax_num_cqs, sizeof(struct gdma_queue *));\n-\tif (!gc-\u003ecq_table)\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+\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 +980,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,30 +1015,435 @@ 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-\t/* mana_hwc_create_channel() will do the cleanup.*/\n+\t/* The setup path tears down any partially initialized queues. */\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 is private; runtime teardown has unpublished and drained senders. */\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+\tmana_hwc_clear_cq_table(gc);\n+\n+\tif (hwc-\u003ecq) {\n+\t\tmana_hwc_destroy_cq(gc, hwc-\u003ecq);\n+\t\thwc-\u003ecq = NULL;\n+\t}\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 = mana_hwc_clear_resp_pending(hwc, ctx);\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 void mana_hwc_free_retained_wq(struct hwc_wq *wq)\n+{\n+\tif (!wq)\n+\t\treturn;\n+\n+\tkfree(wq-\u003emsg_buf);\n+\tkfree(wq-\u003egdma_wq);\n+\tkfree(wq);\n+}\n+\n+/* Final disposal after IRQ and service-work teardown, not a retry path.\n+ * Free only software state: unacknowledged HWC DMA allocations stay live.\n+ */\n+void mana_hwc_free_retained_channel(struct gdma_context *gc)\n+{\n+\tstruct hw_channel_context *hwc = mana_hwc_unpublish_channel(gc);\n+\n+\tif (!hwc)\n+\t\treturn;\n+\n+\tmana_hwc_stop_channel(hwc);\n+\tmana_hwc_clear_cq_table(gc);\n+\n+\tif (hwc-\u003ecq) {\n+\t\tkfree(hwc-\u003ecq-\u003egdma_eq);\n+\t\tkfree(hwc-\u003ecq-\u003egdma_cq);\n+\t\tkfree(hwc-\u003ecq-\u003ecomp_buf);\n+\t\tkfree(hwc-\u003ecq);\n+\t}\n+\n+\tmana_hwc_free_retained_wq(hwc-\u003etxq);\n+\tmana_hwc_free_retained_wq(hwc-\u003erxq);\n+\tkfree(hwc-\u003ecaller_ctx);\n+\tmana_gd_free_res_map(\u0026hwc-\u003einflight_msg_res);\n+\tkfree(hwc);\n+\tgc-\u003ehwc.gdma_context = NULL;\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_finish_channel(struct hw_channel_context *hwc,\n+\t\t\t\t const struct mana_hwc_init_report *report)\n+{\n+\tint err;\n+\n+\t/* MAX_REQUEST sizes the RQ; MAX_RESPONSE sizes the SQ. */\n+\thwc-\u003erx_msg_size_limit = report-\u003emax_req_msg_size;\n+\thwc-\u003etx_msg_size_limit = report-\u003emax_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+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+\terr = mana_hwc_validate_report(hwc, report, 0, false, false);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn mana_hwc_finish_channel(hwc, report);\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+\tu32 timeout_ms = mana_hwc_timeout_read(hwc);\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\terr = mana_hwc_finish_channel(hwc, report);\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+\t/* Do not carry the failed test's reduced wait into the retry. */\n+\tmana_hwc_timeout_update(hwc, timeout_ms);\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\treturn mana_hwc_rebuild_channel(hwc, rebuild_depth, \u0026report);\n+\t}\n+\n+\treturn mana_hwc_finish_channel(hwc, \u0026report);\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 +1452,183 @@ 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+\terr = mana_hwc_prepare_channel(hwc);\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-\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 = mana_hwc_clear_resp_pending(hwc, ctx);\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 = mana_hwc_clear_resp_pending(hwc, ctx);\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-\u003eerror = -ETIMEDOUT;\n+\t\tctx-\u003eresponded = true;\n+\t\tatomic_inc(\u0026hwc-\u003eabandoned_msgs);\n+\t\tabandoned = true;\n+\t}\n+\tspin_unlock_irqrestore(\u0026ctx-\u003elock, flags);\n \n-\tvfree(gc-\u003ecq_table);\n-\tgc-\u003ecq_table = NULL;\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+\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 \tint err;\n \n-\tmana_hwc_get_msg_index(hwc, \u0026msg_id);\n-\n-\ttx_wr = \u0026txq-\u003emsg_buf-\u003ereqs[msg_id];\n+\terr = mana_hwc_get_msg_index(hwc, req_len, 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 c610fc1067e0d..a2e5ad3543ae9 100644\n--- a/include/net/mana/gdma.h\n+++ b/include/net/mana/gdma.h\n@@ -528,6 +528,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@@ -580,8 +589,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,\n@@ -1121,6 +1135,7 @@ int mana_gd_verify_vf_version(struct gdma_context *gc);\n int mana_gd_query_max_resources(struct gdma_context *gc);\n int mana_gd_setup(struct gdma_context *gc);\n void mana_gd_cleanup(struct gdma_context *gc);\n+void mana_gd_free_context(struct gdma_context *gc);\n int mana_gd_detect_devices(struct gdma_context *gc);\n int mana_gd_init_registers(struct gdma_context *gc);\n irqreturn_t mana_gd_intr(int irq, void *arg);\n@@ -1161,6 +1176,8 @@ void mana_gd_free_memory(struct gdma_mem_info *gmi);\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+int mana_gd_cancel_hwc_timeout(struct gdma_context *gc);\n+\n int mana_gd_destroy_dma_region(struct gdma_context *gc, u64 dma_region_handle);\n void mana_register_debugfs(void);\n void mana_unregister_debugfs(void);\ndiff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h\nindex 1ab2d66c891e4..73315aca2e003 100644\n--- a/include/net/mana/hw_channel.h\n+++ b/include/net/mana/hw_channel.h\n@@ -4,6 +4,9 @@\n #ifndef _HW_CHANNEL_H\n #define _HW_CHANNEL_H\n \n+#include \u003clinux/atomic.h\u003e\n+#include \u003clinux/minmax.h\u003e\n+\n #define DEFAULT_LOG2_THROTTLING_FOR_ERROR_EQ 4\n \n #define HW_CHANNEL_MAX_REQUEST_SIZE 0x1000\n@@ -11,6 +14,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 +170,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/* Initialize under inflight_msg_res.lock and lock, before publication.\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 +204,18 @@ 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+\tint hwc_init_error;\n \n \tstruct completion hwc_init_eqe_comp;\n \n@@ -192,20 +223,52 @@ 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+\t/* Response references still outstanding after a sender timed out. */\n+\tatomic_t abandoned_msgs;\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+/* Host-to-PF requests are bounded by the negotiated TX limit and SQ stride. */\n+static inline u32 mana_hwc_get_max_req_size(const struct hw_channel_context *hwc)\n+{\n+\treturn min(hwc-\u003etx_msg_size_limit, hwc-\u003emax_resp_msg_size);\n+}\n+\n int mana_hwc_create_channel(struct gdma_context *gc);\n void mana_hwc_destroy_channel(struct gdma_context *gc);\n+void mana_hwc_free_retained_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/16 18:23 | agent | patch-evaluator |
3mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The changes are strictly confined to the Microsoft Azure Network Adapter (MANA) ethernet driver (drivers/net/ethernet/microsoft/mana/) and the MANA InfiniBand hardware driver (drivers/infiniband/hw/mana/). MANA is a vendor-specific SmartNIC that requires specialized physical or Hyper-V cloud hardware and is not emulated in standard QEMU or GCE virtualized environments. Consequently, the modified code is structurally unreachable for 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 3d315be1c4f3cd0567d3fed1f0c333902aa74145
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 16 18:23:27 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c
index e4414d208d4ac..ce840cee42cae 100644
--- a/drivers/infiniband/hw/mana/main.c
+++ b/drivers/infiniband/hw/mana/main.c
@@ -406,28 +406,38 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem
unsigned int create_req_msg_size;
struct hw_channel_context *hwc;
struct ib_block_iter biter;
- size_t max_pgs_add_cmd = 0;
+ size_t max_pgs_add_cmd;
size_t max_pgs_create_cmd;
struct gdma_context *gc;
size_t num_pages_total;
unsigned int tail = 0;
+ u32 req_limit;
u64 *page_addr_list;
void *request_buf;
int err = 0;
gc = mdev_to_gc(dev);
hwc = gc->hwc.driver_data;
+ req_limit = mana_hwc_get_max_req_size(hwc);
+ if (req_limit < struct_size(create_req, page_addr_list, 1) ||
+ req_limit < struct_size(add_req, page_addr_list, 1)) {
+ ibdev_dbg(&dev->ib_dev,
+ "HWC request budget %u is too small for DMA region commands\n",
+ req_limit);
+ return -EMSGSIZE;
+ }
num_pages_total = ib_umem_num_dma_blocks(umem, page_sz);
max_pgs_create_cmd =
- (hwc->max_req_msg_size - sizeof(*create_req)) / sizeof(u64);
+ (req_limit - sizeof(*create_req)) / sizeof(u64);
+ max_pgs_add_cmd = (req_limit - sizeof(*add_req)) / sizeof(u64);
num_pages_to_handle =
min_t(size_t, num_pages_total, max_pgs_create_cmd);
create_req_msg_size =
struct_size(create_req, page_addr_list, num_pages_to_handle);
- request_buf = kzalloc(hwc->max_req_msg_size, GFP_KERNEL);
+ request_buf = kzalloc(req_limit, GFP_KERNEL);
if (!request_buf)
return -ENOMEM;
@@ -470,9 +480,6 @@ static int mana_ib_gd_create_dma_region(struct mana_ib_dev *dev, struct ib_umem
if (err)
goto out;
- max_pgs_add_cmd = (hwc->max_req_msg_size -
- sizeof(*add_req)) / sizeof(u64);
-
add_req = request_buf;
add_req->dma_region_handle = *gdma_region;
add_req->reserved3 = 0;
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_cdx.c b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c
index 0d01e3d2f0d96..fa10681fd793e 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_cdx.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c
@@ -221,6 +221,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)
mutex_init(&gc->eq_test_event_mutex);
mutex_init(&gc->gic_mutex);
+ spin_lock_init(&gc->hwc_lock);
dev_set_drvdata(&cdx_dev->dev, gc);
gc->bar0_pa = cdx_resource_start(cdx_dev, 0);
gc->bar0_size = cdx_resource_len(cdx_dev, 0);
@@ -279,7 +280,7 @@ static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)
iounmap(bar0_va);
free_gc:
dev_set_drvdata(&cdx_dev->dev, NULL);
- vfree(gc);
+ mana_gd_free_context(gc);
clear_master:
cdx_clear_master(cdx_dev);
return err;
@@ -298,7 +299,7 @@ static int mana_cdx_gd_remove(struct cdx_device *cdx_dev)
iounmap(gc->bar0_va);
dev_set_drvdata(&cdx_dev->dev, NULL);
- vfree(gc);
+ mana_gd_free_context(gc);
return 0;
}
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index ae0ed700b3b93..8b08f28bfdb82 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -11,6 +11,7 @@
#include <linux/irqdomain.h>
#include <linux/export.h>
#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
#include <net/mana/mana.h>
#include <net/mana/hw_channel.h>
@@ -145,6 +146,8 @@ int mana_gd_init_registers(struct gdma_context *gc)
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;
@@ -152,11 +155,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;
}
int mana_gd_query_max_resources(struct gdma_context *gc)
@@ -309,7 +314,8 @@ int mana_gd_query_max_resources(struct gdma_context *gc)
return 0;
}
-static int mana_gd_query_hwc_timeout(struct gdma_context *gc, u32 *timeout_val)
+static int mana_gd_query_hwc_timeout(struct gdma_context *gc, u32 timeout_ms,
+ u32 *new_timeout_ms)
{
struct gdma_query_hwc_timeout_resp resp = {};
struct gdma_query_hwc_timeout_req req = {};
@@ -317,12 +323,12 @@ static int mana_gd_query_hwc_timeout(struct gdma_context *gc, 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;
}
@@ -377,12 +383,48 @@ int mana_gd_detect_devices(struct gdma_context *gc)
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");
+int mana_gd_cancel_hwc_timeout(struct gdma_context *gc)
+{
+ struct hw_channel_context *hwc;
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&gc->hwc_lock, flags);
+ hwc = gc->hwc.driver_data;
+ if (hwc)
+ mana_hwc_timeout_cancel(hwc);
+ else
+ err = -ENODEV;
+ spin_unlock_irqrestore(&gc->hwc_lock, flags);
+
+ return err;
+}
+EXPORT_SYMBOL_NS(mana_gd_cancel_hwc_timeout, "NET_MANA");
+
int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length,
struct gdma_mem_info *gmi, bool allow_scatter)
{
@@ -547,12 +589,26 @@ static int mana_gd_disable_queue(struct gdma_queue *queue)
#define DOORBELL_OFFSET_EQ 0xFF8
#define DOORBELL_OFFSET_DIM 0x820
+bool mana_gd_is_valid_doorbell(struct gdma_context *gc, u32 db_id)
+{
+ if (!gc->db_page_size || gc->db_page_off >= gc->bar0_size)
+ return false;
+
+ return gc->db_page_off +
+ gc->db_page_size * ((u64)db_id + 1) <= gc->bar0_size;
+}
+
static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index,
enum gdma_queue_type q_type, u32 qid,
u32 tail_ptr, u8 num_req)
{
- 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:
@@ -754,6 +810,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)
union gdma_eqe_info eqe_info;
enum gdma_eqe_type type;
struct gdma_event event;
+ struct gdma_queue **cq_table;
struct gdma_queue *cq;
struct gdma_eqe *eqe;
u32 cq_id;
@@ -765,11 +822,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)
@@ -944,7 +1006,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 = {};
@@ -962,7 +1026,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);
@@ -993,23 +1062,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)
@@ -1078,10 +1167,10 @@ static void mana_gd_destroy_cq(struct gdma_context *gc,
if (id >= gc->max_num_cqs)
return;
- if (!gc->cq_table[id])
+ if (!READ_ONCE(gc->cq_table[id]))
return;
- gc->cq_table[id] = NULL;
+ WRITE_ONCE(gc->cq_table[id], NULL);
}
int mana_gd_create_hwc_queue(struct gdma_dev *gd,
@@ -1176,9 +1265,10 @@ 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)
+ if (req_msg_size > mana_hwc_get_max_req_size(hwc))
return -EINVAL;
req = kzalloc(req_msg_size, GFP_KERNEL);
@@ -1378,9 +1468,12 @@ int mana_gd_verify_vf_version(struct gdma_context *gc)
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));
@@ -1418,12 +1511,16 @@ int mana_gd_verify_vf_version(struct gdma_context *gc)
&gc->pf_cap_flags1);
if (resp.pf_cap_flags1 & GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG) {
- err = mana_gd_query_hwc_timeout(gc, &hwc->hwc_timeout);
+ err = mana_gd_query_hwc_timeout(gc,
+ 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;
}
@@ -1451,13 +1548,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;
}
@@ -2008,6 +2099,13 @@ void mana_gd_cleanup(struct gdma_context *gc)
}
EXPORT_SYMBOL_NS(mana_gd_cleanup, "NET_MANA");
+void mana_gd_free_context(struct gdma_context *gc)
+{
+ mana_hwc_free_retained_channel(gc);
+ vfree(gc);
+}
+EXPORT_SYMBOL_NS(mana_gd_free_context, "NET_MANA");
+
static int __init gdma_core_init(void)
{
mana_debugfs_root = debugfs_create_dir("mana", NULL);
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_pci.c b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
index ed8925296c8a7..636d9b0dd5811 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_pci.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
@@ -14,7 +14,6 @@
#include <linux/xarray.h>
#include <net/mana/mana.h>
-#include <net/mana/hw_channel.h>
struct mana_dev_recovery {
struct list_head list;
@@ -85,7 +84,6 @@ static void mana_serv_fpga(struct pci_dev *pdev)
static void mana_serv_reset(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
- struct hw_channel_context *hwc;
int ret;
if (!gc) {
@@ -95,15 +93,13 @@ static void mana_serv_reset(struct pci_dev *pdev)
return;
}
- hwc = gc->hwc.driver_data;
- if (!hwc) {
+ /* HWC is not responding in this case, so don't wait */
+ ret = mana_gd_cancel_hwc_timeout(gc);
+ if (ret) {
dev_err(&pdev->dev, "MANA service: no HWC\n");
goto out;
}
- /* HWC is not responding in this case, so don't wait */
- hwc->hwc_timeout = 0;
-
dev_info(&pdev->dev, "MANA reset cycle start\n");
mana_gd_suspend(pdev, PMSG_SUSPEND);
@@ -674,6 +670,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mutex_init(&gc->eq_test_event_mutex);
mutex_init(&gc->gic_mutex);
+ spin_lock_init(&gc->hwc_lock);
pci_set_drvdata(pdev, gc);
gc->bar0_pa = pci_resource_start(pdev, 0);
gc->bar0_size = pci_resource_len(pdev, 0);
@@ -725,7 +722,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_iounmap(pdev, bar0_va);
free_gc:
pci_set_drvdata(pdev, NULL);
- vfree(gc);
+ mana_gd_free_context(gc);
release_region:
pci_release_regions(pdev);
disable_dev:
@@ -778,7 +775,7 @@ static void mana_gd_remove(struct pci_dev *pdev)
pci_iounmap(pdev, gc->bar0_va);
- vfree(gc);
+ mana_gd_free_context(gc);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index 3bca4b683134b..c4cae1baaef4d 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -6,24 +6,121 @@
#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, u32 req_len,
+ 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;
+ u32 req_limit;
+ u32 resp_limit;
+ u32 wait_ms;
u32 index;
+ int err;
+
+ req_limit = mana_hwc_get_max_req_size(hwc);
+ resp_limit = min(hwc->rx_msg_size_limit, hwc->max_req_msg_size);
+ if (req_len > req_limit || resp_len > resp_limit) {
+ spin_lock_irqsave(&r->lock, flags);
+ err = hwc->channel_up ? -EMSGSIZE : -ENODEV;
+ spin_unlock_irqrestore(&r->lock, flags);
+
+ if (err == -EMSGSIZE)
+ 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 err;
+ }
+
+ wait_ms = mana_hwc_timeout_read(hwc);
+ if (down_timeout(&hwc->sema, msecs_to_jiffies(wait_ms))) {
+ spin_lock_irqsave(&r->lock, flags);
+ if (!hwc->channel_up)
+ err = -ENODEV;
+ else if (atomic_read(&hwc->abandoned_msgs))
+ err = -ETIMEDOUT;
+ else
+ err = -EBUSY;
+ spin_unlock_irqrestore(&r->lock, flags);
- down(&hwc->sema);
+ return err;
+ }
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];
+ spin_lock(&ctx->lock);
+ 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(&ctx->lock);
spin_unlock_irqrestore(&r->lock, flags);
- *msg_id = index;
+ *caller_ctx = ctx;
return 0;
}
@@ -34,12 +131,34 @@ 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 bool mana_hwc_clear_resp_pending(struct hw_channel_context *hwc,
+ struct hwc_caller_ctx *ctx)
+{
+ lockdep_assert_held(&ctx->lock);
+
+ if (!ctx->resp_pending)
+ return false;
+
+ ctx->resp_pending = false;
+ if (ctx->error == -ETIMEDOUT)
+ atomic_dec(&hwc->abandoned_msgs);
+
+ return true;
+}
+
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 +199,53 @@ 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 = mana_hwc_clear_resp_pending(hwc, ctx);
- 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 +263,18 @@ 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);
+ WRITE_ONCE(hwc->hwc_init_error, -EPROTO);
+ complete(&hwc->hwc_init_eqe_comp);
+ 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 +284,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 +296,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 +309,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 +347,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 +537,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 +664,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 +749,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 +767,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 +806,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;
@@ -627,6 +820,7 @@ static int mana_hwc_init_inflight_msg(struct hw_channel_context *hwc,
{
int err;
+ atomic_set(&hwc->abandoned_msgs, 0);
sema_init(&hwc->sema, num_msg);
err = mana_gd_alloc_res_map(num_msg, &hwc->inflight_msg_res);
@@ -635,47 +829,82 @@ 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;
+ WRITE_ONCE(hwc->hwc_init_error, 0);
+ 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 +912,65 @@ 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;
+ err = READ_ONCE(hwc->hwc_init_error);
+ if (err)
+ return err;
- /* Both were set in mana_hwc_init_event_handler(). */
- if (WARN_ON(cq->id >= gc->max_num_cqs))
+ if (!hwc->hwc_init_doorbell) {
+ dev_err(hwc->dev, "HWC: missing valid doorbell in init data\n");
return -EPROTO;
+ }
+
+ report->queue_depth = hwc->hwc_init_q_depth_max;
+ report->max_req_msg_size = hwc->hwc_init_max_req_msg_size;
+ report->max_resp_msg_size = hwc->hwc_init_max_resp_msg_size;
- gc->cq_table = vcalloc(gc->max_num_cqs, sizeof(struct gdma_queue *));
- if (!gc->cq_table)
+ /* 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;
+ }
+
+ /* 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 +980,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,30 +1015,435 @@ 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:
- /* mana_hwc_create_channel() will do the cleanup.*/
+ /* The setup path tears down any partially initialized queues. */
+ 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 is private; runtime teardown has unpublished and drained senders. */
+static void mana_hwc_destroy_queues(struct hw_channel_context *hwc)
+{
+ struct gdma_context *gc = hwc->gdma_dev->gdma_context;
+
+ mana_hwc_clear_cq_table(gc);
+
+ if (hwc->cq) {
+ mana_hwc_destroy_cq(gc, hwc->cq);
+ hwc->cq = NULL;
+ }
+
+ 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 = mana_hwc_clear_resp_pending(hwc, ctx);
+ 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 void mana_hwc_free_retained_wq(struct hwc_wq *wq)
+{
+ if (!wq)
+ return;
+
+ kfree(wq->msg_buf);
+ kfree(wq->gdma_wq);
+ kfree(wq);
+}
+
+/* Final disposal after IRQ and service-work teardown, not a retry path.
+ * Free only software state: unacknowledged HWC DMA allocations stay live.
+ */
+void mana_hwc_free_retained_channel(struct gdma_context *gc)
+{
+ struct hw_channel_context *hwc = mana_hwc_unpublish_channel(gc);
+
+ if (!hwc)
+ return;
+
+ mana_hwc_stop_channel(hwc);
+ mana_hwc_clear_cq_table(gc);
+
+ if (hwc->cq) {
+ kfree(hwc->cq->gdma_eq);
+ kfree(hwc->cq->gdma_cq);
+ kfree(hwc->cq->comp_buf);
+ kfree(hwc->cq);
+ }
+
+ mana_hwc_free_retained_wq(hwc->txq);
+ mana_hwc_free_retained_wq(hwc->rxq);
+ kfree(hwc->caller_ctx);
+ mana_gd_free_res_map(&hwc->inflight_msg_res);
+ kfree(hwc);
+ gc->hwc.gdma_context = NULL;
+}
+
+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_finish_channel(struct hw_channel_context *hwc,
+ const struct mana_hwc_init_report *report)
+{
+ int 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;
+}
+
+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;
+
+ err = mana_hwc_validate_report(hwc, report, 0, false, false);
+ if (err)
+ return err;
+
+ return mana_hwc_finish_channel(hwc, report);
+}
+
+static int mana_hwc_rebuild_channel(struct hw_channel_context *hwc,
+ u16 q_depth,
+ struct mana_hwc_init_report *report)
+{
+ u32 timeout_ms = mana_hwc_timeout_read(hwc);
+ int cleanup_err;
+ int err;
+
+ err = mana_hwc_teardown_queues(hwc);
+ if (err)
+ return err;
+
+ err = mana_hwc_build_channel(hwc, q_depth, report);
+ if (!err)
+ err = mana_hwc_validate_report(hwc, report, q_depth, true, true);
+ if (!err)
+ err = mana_hwc_finish_channel(hwc, report);
+ if (!err)
+ return 0;
+
+ dev_warn(hwc->dev,
+ "HWC depth %u rebuild failed, restoring bootstrap: %d\n",
+ q_depth, err);
+
+ cleanup_err = mana_hwc_teardown_queues(hwc);
+ if (cleanup_err)
+ return cleanup_err;
+
+ /* Do not carry the failed test's reduced wait into the retry. */
+ mana_hwc_timeout_update(hwc, timeout_ms);
+
+ 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;
+ return mana_hwc_rebuild_channel(hwc, rebuild_depth, &report);
+ }
+
+ return mana_hwc_finish_channel(hwc, &report);
+}
+
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 +1452,183 @@ 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);
+ err = mana_hwc_prepare_channel(hwc);
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);
- 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 = mana_hwc_clear_resp_pending(hwc, ctx);
+ 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 = mana_hwc_clear_resp_pending(hwc, ctx);
+ 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->error = -ETIMEDOUT;
+ ctx->responded = true;
+ atomic_inc(&hwc->abandoned_msgs);
+ abandoned = true;
+ }
+ spin_unlock_irqrestore(&ctx->lock, flags);
- vfree(gc->cq_table);
- gc->cq_table = NULL;
+ 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);
+
+ 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;
int err;
- mana_hwc_get_msg_index(hwc, &msg_id);
-
- tx_wr = &txq->msg_buf->reqs[msg_id];
+ err = mana_hwc_get_msg_index(hwc, req_len, 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 c610fc1067e0d..a2e5ad3543ae9 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -528,6 +528,15 @@ struct gdma_context {
/* Hardware communication channel (HWC) */
struct gdma_dev hwc;
+ /* Sender drain; the final wakeup runs under hwc_lock. */
+ wait_queue_head_t hwc_drain_waitq;
+
+ /* Protects runtime HWC publication and active sender references.
+ * Setup owns an unpublished HWC directly; timeout updates use atomic
+ * access helpers and do not require this lock.
+ */
+ spinlock_t hwc_lock;
+
/* Azure network adapter */
struct gdma_dev mana;
@@ -580,8 +589,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,
@@ -1121,6 +1135,7 @@ int mana_gd_verify_vf_version(struct gdma_context *gc);
int mana_gd_query_max_resources(struct gdma_context *gc);
int mana_gd_setup(struct gdma_context *gc);
void mana_gd_cleanup(struct gdma_context *gc);
+void mana_gd_free_context(struct gdma_context *gc);
int mana_gd_detect_devices(struct gdma_context *gc);
int mana_gd_init_registers(struct gdma_context *gc);
irqreturn_t mana_gd_intr(int irq, void *arg);
@@ -1161,6 +1176,8 @@ void mana_gd_free_memory(struct gdma_mem_info *gmi);
int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,
u32 resp_len, void *resp);
+int mana_gd_cancel_hwc_timeout(struct gdma_context *gc);
+
int mana_gd_destroy_dma_region(struct gdma_context *gc, u64 dma_region_handle);
void mana_register_debugfs(void);
void mana_unregister_debugfs(void);
diff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h
index 1ab2d66c891e4..73315aca2e003 100644
--- a/include/net/mana/hw_channel.h
+++ b/include/net/mana/hw_channel.h
@@ -4,6 +4,9 @@
#ifndef _HW_CHANNEL_H
#define _HW_CHANNEL_H
+#include <linux/atomic.h>
+#include <linux/minmax.h>
+
#define DEFAULT_LOG2_THROTTLING_FOR_ERROR_EQ 4
#define HW_CHANNEL_MAX_REQUEST_SIZE 0x1000
@@ -11,6 +14,9 @@
#define HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH 1
+/* Largest supported rebuild depth; larger reports retain bootstrap queues. */
+#define HW_CHANNEL_MAX_QUEUE_DEPTH 128
+
#define HWC_INIT_DATA_CQID 1
#define HWC_INIT_DATA_RQID 2
#define HWC_INIT_DATA_SQID 3
@@ -164,15 +170,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;
+
+ /* Initialize under inflight_msg_res.lock and lock, before publication.
+ * 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 +204,18 @@ struct hw_channel_context {
u16 num_inflight_msg;
u32 max_req_msg_size;
+ u32 max_resp_msg_size;
+ /* Immutable reported maxima for the final RQ and SQ directions. */
+ u32 rx_msg_size_limit;
+ u32 tx_msg_size_limit;
- u16 hwc_init_q_depth_max;
+ u32 hwc_init_q_depth_max;
u32 hwc_init_max_req_msg_size;
u32 hwc_init_max_resp_msg_size;
+ u32 hwc_init_max_num_cqs;
+ u32 hwc_init_cq_id;
+ bool hwc_init_doorbell;
+ int hwc_init_error;
struct completion hwc_init_eqe_comp;
@@ -192,20 +223,52 @@ 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;
+ /* Response references still outstanding after a sender timed out. */
+ atomic_t abandoned_msgs;
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;
};
+/* Host-to-PF requests are bounded by the negotiated TX limit and SQ stride. */
+static inline u32 mana_hwc_get_max_req_size(const struct hw_channel_context *hwc)
+{
+ return min(hwc->tx_msg_size_limit, hwc->max_resp_msg_size);
+}
+
int mana_hwc_create_channel(struct gdma_context *gc);
void mana_hwc_destroy_channel(struct gdma_context *gc);
+void mana_hwc_free_retained_channel(struct gdma_context *gc);
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/16 18:23 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29926 output: 20 thoughts: 525 Thoughts: **Analyzing the Patch for Fuzzing Potential** Okay, let's break this down. The first step is to see if this patch is even *fuzzable* with syzkaller. I'm seeing changes across a handful of files: `drivers/infiniband/hw/mana/main.c`, `drivers/net/ethernet/microsoft/mana/gdma_cdx.c`, and several others within the `drivers/net/ethernet/microsoft/mana/` directory, plus some associated header files in `include/net/mana/`. Right away, the mention of "MANA" - Microsoft Azure Network Adapter - piques my interest. It sounds like either a PCI driver or a CDX driver, something used specifically on Microsoft Azure VMs, maybe. But the crucial question is: is this hardware *emulated* in QEMU or a standard virtualized environment, or is it strictly tied to vendor-specific cloud hardware or a physical PCI card that isn't replicated in QEMU? Hold on, let me check the guidelines. Remember the instruction's section 2. "REACHABILITY IS THE PRIMARY GATE" — if the code isn't *reachable* in a standard environment, it's a no-go for fuzzing. "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" – that's the killer. Is MANA one of those? I need to confirm if QEMU emulates the Microsoft MANA hardware or if it's dependent on a physical card that QEMU doesn't emulate. I have to check the kernel sources and track down where MANA attaches. Once I know that, I'll know if I can move forward with this patch or not. |
| 4/2 | 2026/10/16 18:24 | tool | grepper |
0mArgs:{
"Expression": "PCI_DEVICE.*MANA"
}Results: {
"Output": "drivers/net/ethernet/microsoft/mana/gdma_pci.c=859=static const struct pci_device_id mana_id_table[] = {\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c:860:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c:861:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c:862:\t{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) },\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c-863-\t{ }\n--\ndrivers/tty/serial/8250/8250_pci.c-72-#define PCI_DEVICE_ID_AGESTAR_9375\t0x6872\ndrivers/tty/serial/8250/8250_pci.c:73:#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a\ndrivers/tty/serial/8250/8250_pci.c-74-#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e\n--\ndrivers/tty/serial/8250/8250_pci.c=2023=static struct pci_serial_quirk pci_serial_quirks[] = {\n--\ndrivers/tty/serial/8250/8250_pci.c-2818-\t\t.vendor\t\t= PCI_VENDOR_ID_BROADCOM,\ndrivers/tty/serial/8250/8250_pci.c:2819:\t\t.device\t\t= PCI_DEVICE_ID_BROADCOM_TRUMANAGE,\ndrivers/tty/serial/8250/8250_pci.c-2820-\t\t.subvendor\t= PCI_ANY_ID,\n--\ndrivers/tty/serial/8250/8250_pci.c=4269=static const struct pci_device_id serial_pci_tbl[] = {\n--\ndrivers/tty/serial/8250/8250_pci.c-5920-\t{\ndrivers/tty/serial/8250/8250_pci.c:5921:\t\tPCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE),\ndrivers/tty/serial/8250/8250_pci.c-5922-\t\t.driver_data = pbn_brcm_trumanage,\n"
} |
| 5/2 | 2026/10/16 18:24 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31609 output: 31 thoughts: 151 Thoughts: **Analyzing the MANA Device Configuration** Okay, let's dive into this. I need to quickly ascertain the relationship between `PCI_VENDOR_ID_MICROSOFT`, `MANA_PF_DEVICE_ID`, and the `cdx_dev` variable. First, let's clarify – `CDX` is the AMD/Xilinx bus, the "Compute Device Xilinx" bus, so that's a key piece of context. Now, focusing on the PCI devices, I see the identifiers: `MANA_PF_DEVICE_ID` (0x00ba), `MANA_PF2_DEVICE_ID` (0x00bc), and `MANA_VF_DEVICE_ID` (0x00b9). The critical question here is whether this Microsoft-related "MANA" component is purely hardware-based – specifically PCIe hardware – or if there's a software instantiation possible. Could we have a virtual or software-defined MANA device driver, even a mock driver for testing or emulation purposes? I need to check all of the possibilities before I can move forward. |
| 6/2 | 2026/10/16 18:24 | tool | grepper |
0mArgs:{
"Expression": "mana_ib",
"PathPrefixes": [
"drivers/infiniband/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 1684 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (12 files in total):\ndrivers/infiniband/hw/mana/Makefile:2\ndrivers/infiniband/hw/mana/ah.c:7\ndrivers/infiniband/hw/mana/counters.c:17\ndrivers/infiniband/hw/mana/counters.h:6\ndrivers/infiniband/hw/mana/cq.c:45\ndrivers/infiniband/hw/mana/device.c:84\ndrivers/infiniband/hw/mana/main.c:100\ndrivers/infiniband/hw/mana/mana_ib.h:114\ndrivers/infiniband/hw/mana/mr.c:59\ndrivers/infiniband/hw/mana/qp.c:138\ndrivers/infiniband/hw/mana/wq.c:15\ndrivers/infiniband/hw/mana/wr.c:14\n\ndrivers/infiniband/hw/mana/Makefile-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/infiniband/hw/mana/Makefile:2:obj-$(CONFIG_MANA_INFINIBAND) += mana_ib.o\ndrivers/infiniband/hw/mana/Makefile-3-\ndrivers/infiniband/hw/mana/Makefile:4:mana_ib-y := device.o main.o wq.o qp.o cq.o mr.o ah.o wr.o counters.o\n--\ndrivers/infiniband/hw/mana/ah.c-5-\ndrivers/infiniband/hw/mana/ah.c:6:#include \"mana_ib.h\"\ndrivers/infiniband/hw/mana/ah.c-7-\ndrivers/infiniband/hw/mana/ah.c:8:int mana_ib_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr,\ndrivers/infiniband/hw/mana/ah.c-9-\t\t struct ib_udata *udata)\ndrivers/infiniband/hw/mana/ah.c-10-{\ndrivers/infiniband/hw/mana/ah.c:11:\tstruct mana_ib_dev *mdev = container_of(ibah-\u003edevice, struct mana_ib_dev, ib_dev);\ndrivers/infiniband/hw/mana/ah.c:12:\tstruct mana_ib_ah *ah = container_of(ibah, struct mana_ib_ah, ibah);\ndrivers/infiniband/hw/mana/ah.c-13-\tstruct rdma_ah_attr *ah_attr = attr-\u003eah_attr;\n--\ndrivers/infiniband/hw/mana/ah.c-49-\ndrivers/infiniband/hw/mana/ah.c:50:int mana_ib_destroy_ah(struct ib_ah *ibah, u32 flags)\ndrivers/infiniband/hw/mana/ah.c-51-{\ndrivers/infiniband/hw/mana/ah.c:52:\tstruct mana_ib_dev *mdev = container_of(ibah-\u003edevice, struct mana_ib_dev, ib_dev);\ndrivers/infiniband/hw/mana/ah.c:53:\tstruct mana_ib_ah *ah = container_of(ibah, struct mana_ib_ah, ibah);\ndrivers/infiniband/hw/mana/ah.c-54-\n--\ndrivers/infiniband/hw/mana/counters.c-7-\ndrivers/infiniband/hw/mana/counters.c:8:static const struct rdma_stat_desc mana_ib_port_stats_desc[] = {\ndrivers/infiniband/hw/mana/counters.c-9-\t[MANA_IB_REQUESTER_TIMEOUT].name = \"requester_timeout\",\n--\ndrivers/infiniband/hw/mana/counters.c-44-\ndrivers/infiniband/hw/mana/counters.c:45:static const struct rdma_stat_desc mana_ib_device_stats_desc[] = {\ndrivers/infiniband/hw/mana/counters.c-46-\t[MANA_IB_SENT_CNPS].name = \"sent_cnps\",\n--\ndrivers/infiniband/hw/mana/counters.c-53-\ndrivers/infiniband/hw/mana/counters.c:54:struct rdma_hw_stats *mana_ib_alloc_hw_device_stats(struct ib_device *ibdev)\ndrivers/infiniband/hw/mana/counters.c-55-{\ndrivers/infiniband/hw/mana/counters.c:56:\treturn rdma_alloc_hw_stats_struct(mana_ib_device_stats_desc,\ndrivers/infiniband/hw/mana/counters.c:57:\t\t\t\t\t ARRAY_SIZE(mana_ib_device_stats_desc),\ndrivers/infiniband/hw/mana/counters.c-58-\t\t\t\t\t RDMA_HW_STATS_DEFAULT_LIFESPAN);\n--\ndrivers/infiniband/hw/mana/counters.c-60-\ndrivers/infiniband/hw/mana/counters.c:61:struct rdma_hw_stats *mana_ib_alloc_hw_port_stats(struct ib_device *ibdev,\ndrivers/infiniband/hw/mana/counters.c-62-\t\t\t\t\t\t u32 port_num)\ndrivers/infiniband/hw/mana/counters.c-63-{\ndrivers/infiniband/hw/mana/counters.c:64:\treturn rdma_alloc_hw_stats_struct(mana_ib_port_stats_desc,\ndrivers/infiniband/hw/mana/counters.c:65:\t\t\t\t\t ARRAY_SIZE(mana_ib_port_stats_desc),\ndrivers/infiniband/hw/mana/counters.c-66-\t\t\t\t\t RDMA_HW_STATS_DEFAULT_LIFESPAN);\n--\ndrivers/infiniband/hw/mana/counters.c-68-\ndrivers/infiniband/hw/mana/counters.c:69:static int mana_ib_get_hw_device_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats)\ndrivers/infiniband/hw/mana/counters.c-70-{\ndrivers/infiniband/hw/mana/counters.c:71:\tstruct mana_ib_dev *mdev = container_of(ibdev, struct mana_ib_dev,\ndrivers/infiniband/hw/mana/counters.c-72-\t\t\t\t\t\tib_dev);\n--\ndrivers/infiniband/hw/mana/counters.c-96-\ndrivers/infiniband/hw/mana/counters.c:97:\treturn ARRAY_SIZE(mana_ib_device_stats_desc);\ndrivers/infiniband/hw/mana/counters.c-98-}\ndrivers/infiniband/hw/mana/counters.c-99-\ndrivers/infiniband/hw/mana/counters.c:100:static int mana_ib_get_hw_port_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,\ndrivers/infiniband/hw/mana/counters.c-101-\t\t\t\t u32 port_num)\ndrivers/infiniband/hw/mana/counters.c-102-{\ndrivers/infiniband/hw/mana/counters.c:103:\tstruct mana_ib_dev *mdev = container_of(ibdev, struct mana_ib_dev,\ndrivers/infiniband/hw/mana/counters.c-104-\t\t\t\t\t\tib_dev);\n--\ndrivers/infiniband/hw/mana/counters.c-168-\ndrivers/infiniband/hw/mana/counters.c:169:\treturn ARRAY_SIZE(mana_ib_port_stats_desc);\ndrivers/infiniband/hw/mana/counters.c-170-}\ndrivers/infiniband/hw/mana/counters.c-171-\ndrivers/infiniband/hw/mana/counters.c:172:int mana_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,\ndrivers/infiniband/hw/mana/counters.c-173-\t\t\t u32 port_num, int index)\n--\ndrivers/infiniband/hw/mana/counters.c-175-\tif (!port_num)\ndrivers/infiniband/hw/mana/counters.c:176:\t\treturn mana_ib_get_hw_device_stats(ibdev, stats);\ndrivers/infiniband/hw/mana/counters.c-177-\telse\ndrivers/infiniband/hw/mana/counters.c:178:\t\treturn mana_ib_get_hw_port_stats(ibdev, stats, port_num);\ndrivers/infiniband/hw/mana/counters.c-179-}\n--\ndrivers/infiniband/hw/mana/counters.h-8-\ndrivers/infiniband/hw/mana/counters.h:9:#include \"mana_ib.h\"\ndrivers/infiniband/hw/mana/counters.h-10-\ndrivers/infiniband/hw/mana/counters.h:11:enum mana_ib_port_counters {\ndrivers/infiniband/hw/mana/counters.h-12-\tMANA_IB_REQUESTER_TIMEOUT,\n--\ndrivers/infiniband/hw/mana/counters.h-47-\ndrivers/infiniband/hw/mana/counters.h:48:enum mana_ib_device_counters {\ndrivers/infiniband/hw/mana/counters.h-49-\tMANA_IB_SENT_CNPS,\n--\ndrivers/infiniband/hw/mana/counters.h-56-\ndrivers/infiniband/hw/mana/counters.h:57:struct rdma_hw_stats *mana_ib_alloc_hw_port_stats(struct ib_device *ibdev,\ndrivers/infiniband/hw/mana/counters.h-58-\t\t\t\t\t\t u32 port_num);\ndrivers/infiniband/hw/mana/counters.h:59:struct rdma_hw_stats *mana_ib_alloc_hw_device_stats(struct ib_device *ibdev);\ndrivers/infiniband/hw/mana/counters.h:60:int mana_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,\ndrivers/infiniband/hw/mana/counters.h-61-\t\t\t u32 port_num, int index);\n--\ndrivers/infiniband/hw/mana/cq.c-5-\ndrivers/infiniband/hw/mana/cq.c:6:#include \"mana_ib.h\"\ndrivers/infiniband/hw/mana/cq.c-7-\ndrivers/infiniband/hw/mana/cq.c:8:int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,\ndrivers/infiniband/hw/mana/cq.c-9-\t\t struct uverbs_attr_bundle *attrs)\n--\ndrivers/infiniband/hw/mana/cq.c-11-\tstruct ib_udata *udata = \u0026attrs-\u003edriver_udata;\ndrivers/infiniband/hw/mana/cq.c:12:\tstruct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);\ndrivers/infiniband/hw/mana/cq.c:13:\tstruct mana_ib_create_cq_resp resp = {};\ndrivers/infiniband/hw/mana/cq.c:14:\tstruct mana_ib_ucontext *mana_ucontext;\ndrivers/infiniband/hw/mana/cq.c-15-\tstruct ib_device *ibdev = ibcq-\u003edevice;\ndrivers/infiniband/hw/mana/cq.c:16:\tstruct mana_ib_create_cq ucmd;\ndrivers/infiniband/hw/mana/cq.c:17:\tstruct mana_ib_dev *mdev;\ndrivers/infiniband/hw/mana/cq.c-18-\tbool is_rnic_cq;\n--\ndrivers/infiniband/hw/mana/cq.c-22-\ndrivers/infiniband/hw/mana/cq.c:23:\tmdev = container_of(ibdev, struct mana_ib_dev, ib_dev);\ndrivers/infiniband/hw/mana/cq.c-24-\n--\ndrivers/infiniband/hw/mana/cq.c-26-\tcq-\u003ecq_handle = INVALID_MANA_HANDLE;\ndrivers/infiniband/hw/mana/cq.c:27:\tis_rnic_cq = mana_ib_is_rnic(mdev);\ndrivers/infiniband/hw/mana/cq.c-28-\n--\ndrivers/infiniband/hw/mana/cq.c-41-\t\tcq-\u003ecqe = attr-\u003ecqe;\ndrivers/infiniband/hw/mana/cq.c:42:\t\terr = mana_ib_create_queue(mdev, ucmd.buf_addr, cq-\u003ecqe * COMP_ENTRY_SIZE,\ndrivers/infiniband/hw/mana/cq.c-43-\t\t\t\t\t \u0026cq-\u003equeue);\n--\ndrivers/infiniband/hw/mana/cq.c-48-\ndrivers/infiniband/hw/mana/cq.c:49:\t\tmana_ucontext = rdma_udata_to_drv_context(udata, struct mana_ib_ucontext,\ndrivers/infiniband/hw/mana/cq.c-50-\t\t\t\t\t\t\t ibucontext);\n--\ndrivers/infiniband/hw/mana/cq.c-58-\t\tcq-\u003ecqe = buf_size / COMP_ENTRY_SIZE;\ndrivers/infiniband/hw/mana/cq.c:59:\t\terr = mana_ib_create_kernel_queue(mdev, buf_size, GDMA_CQ, \u0026cq-\u003equeue);\ndrivers/infiniband/hw/mana/cq.c-60-\t\tif (err) {\n--\ndrivers/infiniband/hw/mana/cq.c-67-\tif (is_rnic_cq) {\ndrivers/infiniband/hw/mana/cq.c:68:\t\terr = mana_ib_gd_create_cq(mdev, cq, doorbell);\ndrivers/infiniband/hw/mana/cq.c-69-\t\tif (err) {\n--\ndrivers/infiniband/hw/mana/cq.c-73-\ndrivers/infiniband/hw/mana/cq.c:74:\t\terr = mana_ib_install_cq_cb(mdev, cq);\ndrivers/infiniband/hw/mana/cq.c-75-\t\tif (err) {\n--\ndrivers/infiniband/hw/mana/cq.c-94-err_remove_cq_cb:\ndrivers/infiniband/hw/mana/cq.c:95:\tmana_ib_remove_cq_cb(mdev, cq);\ndrivers/infiniband/hw/mana/cq.c-96-err_destroy_rnic_cq:\ndrivers/infiniband/hw/mana/cq.c:97:\tmana_ib_gd_destroy_cq(mdev, cq);\ndrivers/infiniband/hw/mana/cq.c-98-err_destroy_queue:\ndrivers/infiniband/hw/mana/cq.c:99:\tmana_ib_destroy_queue(mdev, \u0026cq-\u003equeue);\ndrivers/infiniband/hw/mana/cq.c-100-\n--\ndrivers/infiniband/hw/mana/cq.c-103-\ndrivers/infiniband/hw/mana/cq.c:104:int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)\ndrivers/infiniband/hw/mana/cq.c-105-{\ndrivers/infiniband/hw/mana/cq.c:106:\tstruct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);\ndrivers/infiniband/hw/mana/cq.c-107-\tstruct ib_device *ibdev = ibcq-\u003edevice;\ndrivers/infiniband/hw/mana/cq.c:108:\tstruct mana_ib_dev *mdev;\ndrivers/infiniband/hw/mana/cq.c-109-\tint err;\n--\ndrivers/infiniband/hw/mana/cq.c-114-\ndrivers/infiniband/hw/mana/cq.c:115:\tmdev = container_of(ibdev, struct mana_ib_dev, ib_dev);\ndrivers/infiniband/hw/mana/cq.c-116-\ndrivers/infiniband/hw/mana/cq.c:117:\tmana_ib_remove_cq_cb(mdev, cq);\ndrivers/infiniband/hw/mana/cq.c-118-\n--\ndrivers/infiniband/hw/mana/cq.c-121-\t */\ndrivers/infiniband/hw/mana/cq.c:122:\tmana_ib_gd_destroy_cq(mdev, cq);\ndrivers/infiniband/hw/mana/cq.c-123-\ndrivers/infiniband/hw/mana/cq.c:124:\tmana_ib_destroy_queue(mdev, \u0026cq-\u003equeue);\ndrivers/infiniband/hw/mana/cq.c-125-\n--\ndrivers/infiniband/hw/mana/cq.c-128-\ndrivers/infiniband/hw/mana/cq.c:129:static void mana_ib_cq_handler(void *ctx, struct gdma_queue *gdma_cq)\ndrivers/infiniband/hw/mana/cq.c-130-{\ndrivers/infiniband/hw/mana/cq.c:131:\tstruct mana_ib_cq *cq = ctx;\ndrivers/infiniband/hw/mana/cq.c-132-\n--\ndrivers/infiniband/hw/mana/cq.c-136-\ndrivers/infiniband/hw/mana/cq.c:137:int mana_ib_install_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq)\ndrivers/infiniband/hw/mana/cq.c-138-{\n--\ndrivers/infiniband/hw/mana/cq.c-155-\tgdma_cq-\u003etype = GDMA_CQ;\ndrivers/infiniband/hw/mana/cq.c:156:\tgdma_cq-\u003ecq.callback = mana_ib_cq_handler;\ndrivers/infiniband/hw/mana/cq.c-157-\tgdma_cq-\u003eid = cq-\u003equeue.id;\n--\ndrivers/infiniband/hw/mana/cq.c-161-\ndrivers/infiniband/hw/mana/cq.c:162:void mana_ib_remove_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq)\ndrivers/infiniband/hw/mana/cq.c-163-{\n--\ndrivers/infiniband/hw/mana/cq.c-176-\ndrivers/infiniband/hw/mana/cq.c:177:int mana_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)\ndrivers/infiniband/hw/mana/cq.c-178-{\ndrivers/infiniband/hw/mana/cq.c:179:\tstruct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);\ndrivers/infiniband/hw/mana/cq.c-180-\tstruct gdma_queue *gdma_cq = cq-\u003equeue.kmem;\n--\ndrivers/infiniband/hw/mana/cq.c-188-\ndrivers/infiniband/hw/mana/cq.c:189:static inline void handle_ud_sq_cqe(struct mana_ib_qp *qp, struct gdma_comp *cqe)\ndrivers/infiniband/hw/mana/cq.c-190-{\n--\ndrivers/infiniband/hw/mana/cq.c-204-\ndrivers/infiniband/hw/mana/cq.c:205:static inline void handle_ud_rq_cqe(struct mana_ib_qp *qp, struct gdma_comp *cqe)\ndrivers/infiniband/hw/mana/cq.c-206-{\n--\ndrivers/infiniband/hw/mana/cq.c-222-\ndrivers/infiniband/hw/mana/cq.c:223:static void mana_handle_cqe(struct mana_ib_dev *mdev, struct gdma_comp *cqe)\ndrivers/infiniband/hw/mana/cq.c-224-{\ndrivers/infiniband/hw/mana/cq.c:225:\tstruct mana_ib_qp *qp = mana_get_qp_ref(mdev, cqe-\u003ewq_num, cqe-\u003eis_sq);\ndrivers/infiniband/hw/mana/cq.c-226-\n--\n\n"
} |
| 7/2 | 2026/10/16 18:24 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37470 output: 28 thoughts: 41 Thoughts: **Investigating the `mana_ib` Probe** Okay, so I'm digging into how the `mana_ib` device gets recognized within the system. My initial question is whether it's treated as a standard auxiliary device, or if its presence and functionality are specifically orchestrated by the Ethernet driver itself. The key is in how it's *registered*. Therefore, I need to start looking for where and how `mana_ib` is registered within the kernel. Time to go hunting for its registration point. |
| 8/2 | 2026/10/16 18:24 | tool | grepper |
0mError:no matches Args: {
"Expression": "mana_ib.*register",
"PathPrefixes": [
"drivers/"
]
}Results: {
"error": "no matches"
} |
| 9/2 | 2026/10/16 18:24 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37661 output: 37 thoughts: 15 Thoughts: Here's a summary of my thought process: **Focusing on Initialization: Finding `mana_ib`** Alright, I need to get to the bottom of this. My immediate thought is to pinpoint where the `mana_ib` component is being initialized or probed within the system. This seems like the logical first step to understand its lifecycle and potential points of interaction or failure. I need to zero in on the relevant initialization or probe routines. The goal is to see exactly how and where `mana_ib` is set up. From there I can explore any variables associated with initialization to see the starting conditions for that part. |
| 10/2 | 2026/10/16 18:24 | tool | grepper |
0mArgs:{
"Expression": "module_init|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=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"
} |
| 11/2 | 2026/10/16 18:24 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38072 output: 38 thoughts: 47 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating the `mana_ib` Auxiliary Driver** Alright, so `mana_ib`… it seems to be an `auxiliary_driver` that gets probed when a mana netdev creates an auxiliary device. The question that immediately springs to mind is *where* that auxiliary device is actually being registered. It's a key piece of information for understanding the overall device lifecycle and resource management. Knowing where the registration happens will illuminate the link between the mana netdev and the auxiliary device's instantiation. Let's dive into the obvious starting point: the `drivers/net/ethernet/microsoft/mana/` directory. That's the most likely location to find the device registration code and understand the relationship between the two. Let's see what we can find there. |
| 12/2 | 2026/10/16 18:24 | tool | grepper |
0mArgs:{
"Expression": "auxiliary_device_add",
"PathPrefixes": [
"drivers/net/ethernet/microsoft/mana/"
]
}Results: {
"Output": "drivers/net/ethernet/microsoft/mana/mana_en.c=3960=static int add_adev(struct gdma_dev *gd, const char *name)\n--\ndrivers/net/ethernet/microsoft/mana/mana_en.c-3989-\t/* Keep match names independent of the common module's name. */\ndrivers/net/ethernet/microsoft/mana/mana_en.c:3990:\tret = __auxiliary_device_add(adev,\ndrivers/net/ethernet/microsoft/mana/mana_en.c-3991-\t\t\t\t gd-\u003egdma_context-\u003ebus_ops-\u003eadev_prefix);\n"
} |
| 13/2 | 2026/10/16 18:24 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38508 output: 45 thoughts: 44 Thoughts: Here's my analysis of the provided information, tailored for an expert like yourself: **Investigating the Data Transfer Landscape** Okay, so `mana_en` is the key enabler, and it looks like either `gdma_pci` or `gdma_cdx` are the mechanisms driving it. That's the critical link I need to understand. My next step is clear: I need to deeply investigate both `gdma_pci` and `gdma_cdx`. I need to understand their roles, their strengths, their weaknesses, and how they interact with `mana_en`. This means diving into their respective configurations, registers, and likely, their DMA engines. The interplay of these components is where the performance bottlenecks and optimizations will lie. I'll need to look at the data flow, the interrupt handling, and any potential resource conflicts. I'll be focused on understanding how data gets from the source to the destination, and how these two components facilitate that transfer when `mana_en` is involved. |
| 14/2 | 2026/10/16 18:25 | tool | grepper |
0mArgs:{
"Expression": "module_pci_driver|pci_register_driver",
"PathPrefixes": [
"drivers/net/ethernet/microsoft/mana/"
]
}Results: {
"Output": "drivers/net/ethernet/microsoft/mana/gdma_pci.c=879=static int __init mana_driver_init(void)\n--\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c-885-\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c:886:\treturn pci_register_driver(\u0026mana_driver);\ndrivers/net/ethernet/microsoft/mana/gdma_pci.c-887-}\n"
} |
| 15/2 | 2026/10/16 18:25 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38948 output: 45 thoughts: 8 Thoughts: Okay, here's the summary, assuming I'm a seasoned professional reviewing my own thought process: **Diving into the `cdx` Analysis** Alright, time to get to it. Let's start with `cdx`. I know this is the crucial starting point for this analysis. Need to make sure all the building blocks are aligned here before I move forward. A lot hinges on what's available here, the data quality and all the different features. Gotta make sure it is all lining up. |
| 16/2 | 2026/10/16 18:25 | tool | grepper |
0mArgs:{
"Expression": "module_cdx_driver|cdx_driver_register",
"PathPrefixes": [
"drivers/net/ethernet/microsoft/mana/"
]
}Results: {
"Output": "drivers/net/ethernet/microsoft/mana/gdma_cdx.c=326=static struct cdx_driver gdma_cdx_driver = {\n--\ndrivers/net/ethernet/microsoft/mana/gdma_cdx.c-335-\ndrivers/net/ethernet/microsoft/mana/gdma_cdx.c:336:module_driver(gdma_cdx_driver, cdx_driver_register, cdx_driver_unregister);\ndrivers/net/ethernet/microsoft/mana/gdma_cdx.c-337-\n"
} |
| 17/2 | 2026/10/16 18:25 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39260 output: 26 thoughts: 613 Thoughts: **My Assessment of the WorthFuzzing Potential of Microsoft MANA** Okay, let's break this down. First, I need to figure out if syzkaller or standard virtualized environments like GCE or QEMU can even *access* Microsoft MANA hardware. Right, the triage philosophy... gotta stick to it. I need to make sure I don't waste time on code that's fundamentally unreachable. Specifically, I need to check against the negative criteria. It says "Structurally unreachable hardware" like vendor-specific PCIe switches or SmartNICs, requiring physical ASICs not emulated in standard QEMU. Is MANA one of those? Hang on, what *is* MANA? Ah, right, Microsoft Azure Network Adapter! Bingo! It's a Microsoft SmartNIC/ASIC used in their Azure datacenters. So, can QEMU emulate that? Let's verify. I know the standard QEMU devices... e1000, virtio-net, etc. Nope, no mention of any Microsoft MANA devices with the vendor ID of Microsoft or those device IDs in the emulated device list. And that makes sense. But wait, what about the CDX bus? No, the CDX bus is AMD/Xilinx FPGA devices, not MANA. Totally different. Also, even if it were, we're on amd64, and CDX is for arm64. Okay, let's step back. Can we maybe fuzz MANA *through* some software-emulated interface? Netlink, tun/tap, something like that? The `mana_ib` driver… that's in `drivers/infiniband/hw/mana/`. See that `hw/` path? That's a *hardware* InfiniBand/RDMA driver for Microsoft MANA NICs, not something soft-emulated like rxe or siw. I'm going to check the Kconfig dependencies for `mana_ib` just to be completely sure, but it looks like we're hitting that negative criteria. Time to move on. |
| 18/2 | 2026/10/16 18:26 | tool | grepper |
0mArgs:{
"Expression": "config MANA",
"PathPrefixes": [
"drivers/"
]
}Results: {
"Output": "drivers/infiniband/hw/mana/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\ndrivers/infiniband/hw/mana/Kconfig:2:config MANA_INFINIBAND\ndrivers/infiniband/hw/mana/Kconfig-3-\ttristate \"Microsoft Azure Network Adapter support\"\n--\ndrivers/power/supply/Kconfig=293=config CHARGER_SBS\n--\ndrivers/power/supply/Kconfig-299-\ndrivers/power/supply/Kconfig:300:config MANAGER_SBS\ndrivers/power/supply/Kconfig-301-\ttristate \"Smart Battery System Manager\"\n"
} |
| 19/2 | 2026/10/16 18:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40518 output: 39 thoughts: 35 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Kconfig Location Check** Alright, before diving too deep into the code, I need to get a lay of the land, especially regarding the configuration options. I'm going to quickly verify where the relevant Kconfig files live. Seems like `drivers/infiniband/hw/mana/Kconfig` and `drivers/net/ethernet/microsoft/mana/Kconfig` are the places to start. I'm making sure these are the correct locations for the Infiniband and Ethernet sections, respectively. This will give me the ground truth on what knobs are available for these devices, and how they relate to each other. I'll need to know which options are set depending on driver configuration. |
| 20/2 | 2026/10/16 18:26 | tool | read-file |
0mArgs:{
"File": "drivers/infiniband/hw/mana/Kconfig",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig MANA_INFINIBAND\n 3:\t\ttristate \"Microsoft Azure Network Adapter support\"\n 4:\t\tdepends on NETDEVICES \u0026\u0026 ETHERNET \u0026\u0026 PCI \u0026\u0026 MICROSOFT_MANA\n 5:\t\thelp\n 6:\t\t This driver provides low-level RDMA support for Microsoft Azure\n 7:\t\t Network Adapter (MANA). MANA supports RDMA features that can be used\n 8:\t\t for workloads (e.g. DPDK, MPI etc) that uses RDMA verbs to directly\n 9:\t\t access hardware from user-mode processes in Microsoft Azure cloud\n 10:\t\t environment.\n"
} |
| 21/2 | 2026/10/16 18:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40945 output: 36 thoughts: 19 Thoughts: Here's my take on those thoughts: **Analyzing the Mana Kconfig File** Okay, time to dive into `drivers/net/ethernet/microsoft/mana/Kconfig`. I'm going to quickly review this Kconfig file. I want to understand the configuration options and dependencies associated with the Mana network driver. Specifically, I'm looking for the build-time configuration settings that might affect the driver's features, performance, and compatibility. I'll pay close attention to the dependencies – which other kernel components or options are required or incompatible with the Mana driver? I'll probably scan for any specific hardware platform support defined within the file too. I am going to try and map the relevant settings to how they affect overall functionality. |
| 22/2 | 2026/10/16 18:26 | tool | grepper |
0mArgs:{
"Expression": "config MICROSOFT_MANA",
"PathPrefixes": [
"drivers/net/ethernet/microsoft/"
]
}Results: {
"Output": "drivers/net/ethernet/microsoft/Kconfig=18=config MICROSOFT_GDMA_CORE\n--\ndrivers/net/ethernet/microsoft/Kconfig-24-\ndrivers/net/ethernet/microsoft/Kconfig:25:config MICROSOFT_MANA\ndrivers/net/ethernet/microsoft/Kconfig-26-\ttristate \"Microsoft Azure Network Adapter (MANA) support\"\n--\ndrivers/net/ethernet/microsoft/Kconfig-37-\ndrivers/net/ethernet/microsoft/Kconfig:38:config MICROSOFT_MANA_CDX\ndrivers/net/ethernet/microsoft/Kconfig-39-\ttristate \"Microsoft Azure Network Adapter (MANA) support for the CDX bus\"\n"
} |
| 23/2 | 2026/10/16 18:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41341 output: 37 thoughts: 17 Thoughts: Here's a first-person summary, tailored for an expert audience, based on the prompt: **Diving into the Microsoft Ethernet Kconfig** Okay, let's pull up the `drivers/net/ethernet/microsoft/Kconfig` file. Given my experience, I'm immediately looking for the key architecture choices and dependencies. I expect a well-structured Kconfig file, of course, but I'm interested in how they've handled feature configuration. Specifically, I'm trying to see if it cleanly separates the base driver from any vendor-specific extensions and device-specific options. Are there any clever tricks to handle variations in the hardware or different chipsets? Are they using any of the standard kernel macros efficiently? Are there any obvious areas that might lead to portability issues down the line? I'm anticipating looking for some good conditional logic, nested menus, and clear help text that reflects the design decisions made by the original developers. I'll pay attention to things like the default configuration – is it reasonable, or are they leaning towards something overly aggressive for a default? I'm hoping to see a file that's easy to review and doesn't contain too many complex dependencies. In the best-case scenario, it will provide a very good model for the architecture. |
| 24/2 | 2026/10/16 18:26 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/microsoft/Kconfig",
"FirstLine": 1,
"LineCount": 50
}Results: {
"Contents": " 1:\t#\n 2:\t# Microsoft Azure network device configuration\n 3:\t#\n 4:\t\n 5:\tconfig NET_VENDOR_MICROSOFT\n 6:\t\tbool \"Microsoft Network Devices\"\n 7:\t\tdefault y\n 8:\t\thelp\n 9:\t\t If you have a network (Ethernet) device belonging to this class, say Y.\n 10:\t\n 11:\t\t Note that the answer to this question doesn't directly affect the\n 12:\t\t kernel: saying N will just cause the configurator to skip the\n 13:\t\t question about Microsoft network devices. If you say Y, you will be\n 14:\t\t asked for your specific device in the following question.\n 15:\t\n 16:\tif NET_VENDOR_MICROSOFT\n 17:\t\n 18:\tconfig MICROSOFT_GDMA_CORE\n 19:\t\ttristate\n 20:\t\tselect AUXILIARY_BUS\n 21:\t\tselect DIMLIB\n 22:\t\tselect PAGE_POOL\n 23:\t\tselect NET_SHAPER\n 24:\t\n 25:\tconfig MICROSOFT_MANA\n 26:\t\ttristate \"Microsoft Azure Network Adapter (MANA) support\"\n 27:\t\tdepends on PCI_MSI\n 28:\t\tdepends on X86_64 || (ARM64 \u0026\u0026 !CPU_BIG_ENDIAN)\n 29:\t\tdepends on PCI_HYPERV\n 30:\t\tselect MICROSOFT_GDMA_CORE\n 31:\t\thelp\n 32:\t\t This driver supports Microsoft Azure Network Adapter (MANA).\n 33:\t\t So far, the driver is only supported on X86_64.\n 34:\t\n 35:\t\t To compile this driver as a module, choose M here.\n 36:\t\t The module will be called mana.\n 37:\t\n 38:\tconfig MICROSOFT_MANA_CDX\n 39:\t\ttristate \"Microsoft Azure Network Adapter (MANA) support for the CDX bus\"\n 40:\t\tdepends on CDX_BUS\n 41:\t\tdepends on GENERIC_MSI_IRQ\n 42:\t\tdepends on 64BIT \u0026\u0026 !CPU_BIG_ENDIAN\n 43:\t\tselect MICROSOFT_GDMA_CORE\n 44:\t\thelp\n 45:\t\t This driver supports the Microsoft Azure Network Adapter (MANA) on\n 46:\t\t systems where it is integrated into the SoC and presented on the CDX\n 47:\t\t bus rather than discovered over PCI.\n 48:\t\n 49:\t\t To compile this driver as a module, choose M here.\n 50:\t\t The module will be called mana_cdx.\n"
} |
| 25/2 | 2026/10/16 18:26 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42341 output: 129 thoughts: 1074 Thoughts: **Fuzzing Verdict: WorthFuzzing=false for Microsoft MANA Driver Updates** Okay, let's break this down. First, the code in question relies heavily on `CONFIG_MICROSOFT_MANA`, which, as we know, is dependent on `PCI_HYPERV`. This tells me straight away that we're dealing with Microsoft's Hyper-V/Azure environment and the MANA SmartNIC. `CONFIG_MICROSOFT_MANA_CDX` further narrows it down to the CDX bus, and `CONFIG_MANA_INFINIBAND` ties in InfiniBand support, all explicitly tied to `MICROSOFT_MANA`. Now, looking at Section 2 of the instructions, the critical point is "structurally unreachable hardware." It explicitly lists vendor-specific PCIe switches and SmartNICs—like the ones we have here!—that *require* physical hardware not emulated by standard QEMU. The instructions are very clear: If the code is unreachable in standard environments, *do not* fuzz it. This is a primary gate. I've examined the code changes. Every single file modified is located within `drivers/infiniband/hw/mana/`, `drivers/net/ethernet/microsoft/mana/`, and the associated `include/net/mana/` directories. This is exclusively MANA-specific code—GDMA, PCI, hardware channels, shared memory, and the InfiniBand driver. There's *nothing* in generic core subsystems that would suggest a path to execution outside of a MANA-enabled environment. Therefore, this code is undeniably structurally unreachable in QEMU and GCE, which are our standard fuzzing platforms. We need the physical MANA hardware (or Hyper-V/Azure providing MANA VFs/PFs) for this code to run. The examples in Section 2 are a perfect match. We have Microsoft's MANA, a vendor-specific SmartNIC. We're directly dealing with code that would trigger a return value of false for WorthFuzzing=false. I've checked the guidelines for cases when WorthFuzzing = false. We don't need any FocusSymbols or EnableConfigs, since this code isn't reachable in our environment. Finally, the reasoning format. I need to be concise. In essence: "The patch touches code exclusively within the Microsoft Azure Network Adapter (MANA) Ethernet driver (`drivers/net/ethernet/microsoft/mana/`) and its corresponding InfiniBand/RDMA hardware driver (`drivers/infiniband/hw/mana/`). MANA is a vendor-specific SmartNIC device requiring specialized hardware (or Hyper-V MANA virtual functions) that is not emulated in standard QEMU or GCE virtualized fuzzing environments. Consequently, the modified code is structurally unreachable during fuzzing." Looks good. I can proceed to set the results. |
| 26/2 | 2026/10/16 18:26 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The changes are strictly confined to the Microsoft Azure Network Adapter (MANA) ethernet driver (drivers/net/ethernet/microsoft/mana/) and the MANA InfiniBand hardware driver (drivers/infiniband/hw/mana/). MANA is a vendor-specific SmartNIC that requires specialized physical or Hyper-V cloud hardware and is not emulated in standard QEMU or GCE virtualized environments. Consequently, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The changes are strictly confined to the Microsoft Azure Network Adapter (MANA) ethernet driver (drivers/net/ethernet/microsoft/mana/) and the MANA InfiniBand hardware driver (drivers/infiniband/hw/mana/). MANA is a vendor-specific SmartNIC that requires specialized physical or Hyper-V cloud hardware and is not emulated in standard QEMU or GCE virtualized environments. Consequently, the modified code is structurally unreachable for fuzzing.",
"WorthFuzzing": false
} |
| 27/1 | 2026/10/16 18:26 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|