mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next 0/4] net: mana: Add support for the CDX bus
@ 2026-09-24 17:30 Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 1/4] net: mana: Introduce gdma_bus_ops for bus-specific operations Manish Awasthi
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Manish Awasthi @ 2026-09-24 17:30 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov
  Cc: horms, linux-hyperv, netdev, linux-kernel, linux-rdma, bpf,
	nipun.gupta, nikhil.agarwal, gargaditya, ernis, kees, paulros,
	mawasthi

This series adds CDX bus support to the MANA driver.

The shared GDMA and Ethernet code currently builds together with the PCI
transport. To add another bus without duplicating the driver or adding
configuration checks throughout the shared code, this series separates the
bus-specific operations and builds each transport independently.

Patch 1 introduces gdma_bus_ops and routes bus-specific interrupt, reset,
service, and setup operations through it.

Patch 2 moves the PCI-specific code from gdma_main.c into gdma_pci.c
without changing PCI behavior.

Patch 3 builds the shared code as gdma_core.ko and the PCI transport as
mana.ko. The PCI module keeps its existing name, device table, and
mana.eth/mana.rdma auxiliary-device identities.

Patch 4 adds the CDX transport as mana_cdx.ko for device ID 0x00C2.

Manish Awasthi (4):
  net: mana: Introduce gdma_bus_ops for bus-specific operations
  net: mana: Move PCI transport code into gdma_pci.c
  net: mana: Build the PCI transport as a separate module
  net: mana: Add support for CDX device ID 0x00C2

 drivers/net/ethernet/microsoft/Kconfig        |   26 +-
 drivers/net/ethernet/microsoft/Makefile       |    2 +-
 drivers/net/ethernet/microsoft/mana/Makefile  |    9 +-
 .../net/ethernet/microsoft/mana/gdma_cdx.c    |  340 ++++++
 .../net/ethernet/microsoft/mana/gdma_main.c   | 1063 +++--------------
 .../net/ethernet/microsoft/mana/gdma_pci.c    |  917 ++++++++++++++
 drivers/net/ethernet/microsoft/mana/mana_en.c |   18 +-
 include/net/mana/gdma.h                       |  106 +-
 8 files changed, 1552 insertions(+), 929 deletions(-)
 create mode 100644 drivers/net/ethernet/microsoft/mana/gdma_cdx.c
 create mode 100644 drivers/net/ethernet/microsoft/mana/gdma_pci.c


base-commit: 87b80c2f6b05cad9f0ff9136709c62a0f59923e3
-- 
2.54.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next 1/4] net: mana: Introduce gdma_bus_ops for bus-specific operations
  2026-09-24 17:30 [PATCH net-next 0/4] net: mana: Add support for the CDX bus Manish Awasthi
@ 2026-09-24 17:30 ` Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 2/4] net: mana: Move PCI transport code into gdma_pci.c Manish Awasthi
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Manish Awasthi @ 2026-09-24 17:30 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov
  Cc: horms, linux-hyperv, netdev, linux-kernel, linux-rdma, bpf,
	nipun.gupta, nikhil.agarwal, gargaditya, ernis, kees, paulros,
	mawasthi

Introduce per-device gdma_bus_ops for interrupt allocation, IRQ lookup,
reset, servicing, and bus-specific capabilities. Route the shared GDMA
code through these callbacks and provide the PCI implementation.

Move common device bring-up and teardown into mana_gd_setup() and
mana_gd_cleanup(), including hardware-channel initialization, device
enumeration, interrupt-pool sizing, and servicing workqueue management.
Record the vPort count for transport-specific queue sizing and keep shared
IRQ context bookkeeping in the core.

This prepares the driver for additional bus transports without duplicating
the core or adding configuration checks throughout it.

Signed-off-by: Manish Awasthi <mawasthi@linux.microsoft.com>
---
 .../net/ethernet/microsoft/mana/gdma_main.c   | 907 ++++++++++--------
 drivers/net/ethernet/microsoft/mana/mana_en.c |  10 +-
 include/net/mana/gdma.h                       | 102 +-
 3 files changed, 630 insertions(+), 389 deletions(-)

diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 8e9bfc1d6a2a..b076760b2dc5 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -2,49 +2,42 @@
 /* Copyright (c) 2021, Microsoft Corporation. */
 
 #include <linux/bitfield.h>
+#include <linux/cpumask.h>
 #include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/sizes.h>
 #include <linux/utsname.h>
 #include <linux/version.h>
 #include <linux/msi.h>
 #include <linux/irqdomain.h>
 #include <linux/export.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/topology.h>
 #include <linux/uaccess.h>
+#include <linux/xarray.h>
 
 #include <net/mana/mana.h>
 #include <net/mana/hw_channel.h>
 
 struct dentry *mana_debugfs_root;
 
-struct mana_dev_recovery {
-	struct list_head list;
-	struct pci_dev *pdev;
-	enum gdma_eqe_type type;
-};
-
-static struct mana_dev_recovery_work {
-	struct list_head dev_list;
-	struct delayed_work work;
-
-	/* Lock for dev_list above */
-	spinlock_t lock;
-} mana_dev_recovery_work;
-
-static u32 mana_gd_r32(struct gdma_context *g, u64 offset)
+/*
+ * True if the underlying bus can allocate MSI-X vectors after probe time.
+ * Buses that size their vector pool at probe install no callback.
+ */
+static bool mana_gd_msix_can_alloc_dyn(struct gdma_context *gc)
 {
-	return readl(g->bar0_va + offset);
-}
+	if (!gc->bus_ops || !gc->bus_ops->msix_can_alloc_dyn)
+		return false;
 
-static u64 mana_gd_r64(struct gdma_context *g, u64 offset)
-{
-	return readq(g->bar0_va + offset);
+	return gc->bus_ops->msix_can_alloc_dyn(gc);
 }
 
-static int mana_gd_init_pf_regs(struct pci_dev *pdev)
+static int mana_gd_init_pf_regs(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	u64 remaining_barsize;
 	u64 sriov_base_off;
 	u64 sriov_shm_off;
@@ -102,9 +95,8 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev)
 	return 0;
 }
 
-static int mana_gd_init_vf_regs(struct pci_dev *pdev)
+static int mana_gd_init_vf_regs(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	u64 shm_off;
 
 	gc->db_page_size = mana_gd_r32(gc, GDMA_REG_DB_PAGE_SIZE) & 0xFFFF;
@@ -148,14 +140,12 @@ static int mana_gd_init_vf_regs(struct pci_dev *pdev)
 	return 0;
 }
 
-static int mana_gd_init_registers(struct pci_dev *pdev)
+int mana_gd_init_registers(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-
 	if (gc->is_pf && !gc->is_pf2)
-		return mana_gd_init_pf_regs(pdev);
+		return mana_gd_init_pf_regs(gc);
 	else
-		return mana_gd_init_vf_regs(pdev);
+		return mana_gd_init_vf_regs(gc);
 }
 
 /* Suppress logging when we set timeout to zero */
@@ -176,9 +166,8 @@ bool mana_need_log(struct gdma_context *gc, int err)
 	return true;
 }
 
-static int mana_gd_query_max_resources(struct pci_dev *pdev)
+int mana_gd_query_max_resources(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct gdma_query_max_resources_resp resp = {};
 	struct gdma_general_req req = {};
 	unsigned int max_num_queues;
@@ -194,7 +183,7 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 	 * MSI-X allocation; on non-dyn platforms msi_sharing is
 	 * unconditionally true (set in mana_gd_setup_hwc_irqs).
 	 */
-	if (pci_msix_can_alloc_dyn(to_pci_dev(gc->dev)))
+	if (mana_gd_msix_can_alloc_dyn(gc))
 		gc->msi_sharing = false;
 
 	mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES,
@@ -207,7 +196,11 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 		return err ? err : -EPROTO;
 	}
 
-	if (!pci_msix_can_alloc_dyn(pdev)) {
+	if (!mana_gd_msix_can_alloc_dyn(gc)) {
+		/* Buses that size their vector pool at probe time cannot grow
+		 * it afterwards, so never raise num_msix_usable above what has
+		 * already been allocated.
+		 */
 		if (gc->num_msix_usable > resp.max_msix)
 			gc->num_msix_usable = resp.max_msix;
 	} else {
@@ -221,20 +214,24 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 
 	/* MSI-X vectors are allocated by index into the device MSI-X table, so
 	 * never ask for more than the table holds. It can be smaller than both
-	 * resp.max_msix and the CPU count.
+	 * resp.max_msix and the CPU count. A bus that cannot report a table
+	 * size installs no callback and skips the clamp.
 	 */
-	err = pci_msix_vec_count(pdev);
-	if (err <= 0) {
-		dev_err(gc->dev, "Failed to query MSI-X table size: %d\n", err);
-		return err < 0 ? err : -ENOSPC;
-	}
-	msix_vec_count = err;
+	if (gc->bus_ops && gc->bus_ops->msix_vec_count) {
+		err = gc->bus_ops->msix_vec_count(gc);
+		if (err <= 0) {
+			dev_err(gc->dev,
+				"Failed to query MSI-X table size: %d\n", err);
+			return err < 0 ? err : -ENOSPC;
+		}
+		msix_vec_count = err;
 
-	if (gc->num_msix_usable > msix_vec_count) {
-		dev_info(gc->dev,
-			 "Limiting MSI-X vectors from %u to table size %u\n",
-			 gc->num_msix_usable, msix_vec_count);
-		gc->num_msix_usable = msix_vec_count;
+		if (gc->num_msix_usable > msix_vec_count) {
+			dev_info(gc->dev,
+				 "Limiting MSI-X vectors from %u to table size %u\n",
+				 gc->num_msix_usable, msix_vec_count);
+			gc->num_msix_usable = msix_vec_count;
+		}
 	}
 
 	if (gc->num_msix_usable <= 1)
@@ -286,6 +283,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 	if (num_ports > MAX_PORTS_IN_MANA_DEV)
 		num_ports = MAX_PORTS_IN_MANA_DEV;
 
+	gc->num_ports = num_ports;
+
 	/*
 	 * Adjust the per-vPort max queue count to allow dedicated
 	 * MSIx for each vPort. Prefer at least MANA_DEF_NUM_QUEUES,
@@ -317,9 +316,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
 	return 0;
 }
 
-static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)
+static int mana_gd_query_hwc_timeout(struct gdma_context *gc, u32 *timeout_val)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct gdma_query_hwc_timeout_resp resp = {};
 	struct gdma_query_hwc_timeout_req req = {};
 	int err;
@@ -336,9 +334,8 @@ static int mana_gd_query_hwc_timeout(struct pci_dev *pdev, u32 *timeout_val)
 	return 0;
 }
 
-static int mana_gd_detect_devices(struct pci_dev *pdev)
+int mana_gd_detect_devices(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct gdma_list_devices_resp resp = {};
 	struct gdma_general_req req = {};
 	struct gdma_dev_id dev;
@@ -657,187 +654,31 @@ void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld,
 }
 EXPORT_SYMBOL_NS(mana_gd_ring_dim, "NET_MANA");
 
-#define MANA_SERVICE_PERIOD 10
-
-static void mana_serv_rescan(struct pci_dev *pdev)
-{
-	struct pci_bus *parent;
-
-	pci_lock_rescan_remove();
-
-	parent = pdev->bus;
-	if (!parent) {
-		dev_err(&pdev->dev, "MANA service: no parent bus\n");
-		goto out;
-	}
-
-	pci_stop_and_remove_bus_device(pdev);
-	pci_rescan_bus(parent);
-
-out:
-	pci_unlock_rescan_remove();
-}
-
-static void mana_serv_fpga(struct pci_dev *pdev)
-{
-	struct pci_bus *bus, *parent;
-
-	pci_lock_rescan_remove();
-
-	bus = pdev->bus;
-	if (!bus) {
-		dev_err(&pdev->dev, "MANA service: no bus\n");
-		goto out;
-	}
-
-	parent = bus->parent;
-	if (!parent) {
-		dev_err(&pdev->dev, "MANA service: no parent bus\n");
-		goto out;
-	}
-
-	pci_stop_and_remove_bus_device(bus->self);
-
-	msleep(MANA_SERVICE_PERIOD * 1000);
-
-	pci_rescan_bus(parent);
-
-out:
-	pci_unlock_rescan_remove();
-}
-
-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) {
-		/* Perform PCI rescan on device if GC is not set up */
-		dev_err(&pdev->dev, "MANA service: GC not setup, rescanning\n");
-		mana_serv_rescan(pdev);
-		return;
-	}
-
-	hwc = gc->hwc.driver_data;
-	if (!hwc) {
-		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);
-
-	msleep(MANA_SERVICE_PERIOD * 1000);
-
-	ret = mana_gd_resume(pdev);
-	if (ret == -ETIMEDOUT || ret == -EPROTO) {
-		/* Perform PCI rescan on device if we failed on HWC */
-		dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n");
-		mana_serv_rescan(pdev);
-		return;
-	}
-
-	if (ret)
-		dev_info(&pdev->dev, "MANA reset cycle failed err %d\n", ret);
-	else
-		dev_info(&pdev->dev, "MANA reset cycle completed\n");
-
-out:
-	clear_bit(GC_IN_SERVICE, &gc->flags);
-}
-
-static void mana_do_service(enum gdma_eqe_type type, struct pci_dev *pdev)
-{
-	switch (type) {
-	case GDMA_EQE_HWC_FPGA_RECONFIG:
-		mana_serv_fpga(pdev);
-		break;
-
-	case GDMA_EQE_HWC_RESET_REQUEST:
-		mana_serv_reset(pdev);
-		break;
-
-	default:
-		dev_err(&pdev->dev, "MANA service: unknown type %d\n", type);
-		break;
-	}
-}
-
-static void mana_recovery_delayed_func(struct work_struct *w)
-{
-	struct mana_dev_recovery_work *work;
-	struct mana_dev_recovery *dev;
-	unsigned long flags;
-
-	work = container_of(w, struct mana_dev_recovery_work, work.work);
-
-	spin_lock_irqsave(&work->lock, flags);
-
-	while (!list_empty(&work->dev_list)) {
-		dev = list_first_entry(&work->dev_list,
-				       struct mana_dev_recovery, list);
-		list_del(&dev->list);
-		spin_unlock_irqrestore(&work->lock, flags);
-
-		mana_do_service(dev->type, dev->pdev);
-		pci_dev_put(dev->pdev);
-		kfree(dev);
-
-		spin_lock_irqsave(&work->lock, flags);
-	}
-
-	spin_unlock_irqrestore(&work->lock, flags);
-}
-
-static void mana_serv_func(struct work_struct *w)
+/*
+ * Queue device servicing or recovery on buses that support it.
+ *
+ * Servicing tears the device down and brings it back up, so it depends on
+ * bus-level facilities the GDMA core does not have. Buses that provide no
+ * servicing path install no callback and the request is rejected.
+ */
+int mana_schedule_serv_work(struct gdma_context *gc, enum gdma_eqe_type type)
 {
-	struct mana_serv_work *mns_wk;
-	struct pci_dev *pdev;
-
-	mns_wk = container_of(w, struct mana_serv_work, serv_work);
-	pdev = mns_wk->pdev;
-
-	if (pdev)
-		mana_do_service(mns_wk->type, pdev);
+	if (!gc->bus_ops || !gc->bus_ops->schedule_serv_work)
+		return -EOPNOTSUPP;
 
-	pci_dev_put(pdev);
-	kfree(mns_wk);
-	module_put(THIS_MODULE);
+	return gc->bus_ops->schedule_serv_work(gc, type);
 }
 
-int mana_schedule_serv_work(struct gdma_context *gc, enum gdma_eqe_type type)
+/* The servicing workqueue is owned by the GDMA core because the queueing
+ * sites live here and in mana_en.c, which every transport shares. Each bus
+ * driver creates it during setup and destroys it during cleanup.
+ */
+int mana_gd_alloc_service_wq(struct gdma_context *gc)
 {
-	struct mana_serv_work *mns_wk;
-
-	if (test_and_set_bit(GC_IN_SERVICE, &gc->flags)) {
-		dev_info(gc->dev, "Already in service\n");
-		return -EBUSY;
-	}
-
-	if (!try_module_get(THIS_MODULE)) {
-		dev_info(gc->dev, "Module is unloading\n");
-		clear_bit(GC_IN_SERVICE, &gc->flags);
-		return -ENODEV;
-	}
-
-	mns_wk = kzalloc_obj(*mns_wk, GFP_ATOMIC);
-	if (!mns_wk) {
-		module_put(THIS_MODULE);
-		clear_bit(GC_IN_SERVICE, &gc->flags);
+	gc->service_wq = alloc_ordered_workqueue("gdma_service_wq", 0);
+	if (!gc->service_wq)
 		return -ENOMEM;
-	}
 
-	dev_info(gc->dev, "Start MANA service type:%d\n", type);
-	mns_wk->pdev = to_pci_dev(gc->dev);
-	mns_wk->type = type;
-	pci_dev_get(mns_wk->pdev);
-	INIT_WORK(&mns_wk->serv_work, mana_serv_func);
-	schedule_work(&mns_wk->serv_work);
 	return 0;
 }
 
@@ -904,6 +745,15 @@ ssize_t mana_gd_read_ring(struct gdma_queue *q, char __user *buf,
 	return copied;
 }
 
+void mana_gd_free_service_wq(struct gdma_context *gc)
+{
+	if (!gc->service_wq)
+		return;
+
+	destroy_workqueue(gc->service_wq);
+	gc->service_wq = NULL;
+}
+
 static void mana_gd_process_eqe(struct gdma_queue *eq)
 {
 	u32 head = eq->head % (eq->queue_size / GDMA_EQE_SIZE);
@@ -975,7 +825,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)
 	}
 }
 
-static void mana_gd_process_eq_events(void *arg)
+void mana_gd_process_eq_events(void *arg)
 {
 	u32 owner_bits, new_bits, old_bits;
 	union gdma_eqe_info eqe_info;
@@ -1530,9 +1380,8 @@ void mana_gd_destroy_queue(struct gdma_context *gc, struct gdma_queue *queue)
 }
 EXPORT_SYMBOL_NS(mana_gd_destroy_queue, "NET_MANA");
 
-int mana_gd_verify_vf_version(struct pci_dev *pdev)
+int mana_gd_verify_vf_version(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
 	struct gdma_verify_ver_resp resp = {};
 	struct gdma_verify_ver_req req = {};
 	struct hw_channel_context *hwc;
@@ -1546,6 +1395,8 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)
 	req.protocol_ver_max = GDMA_PROTOCOL_LAST;
 
 	req.gd_drv_cap_flags1 = GDMA_DRV_CAP_FLAGS1;
+	if (gc->bus_ops)
+		req.gd_drv_cap_flags1 |= gc->bus_ops->drv_cap_flags1;
 	req.gd_drv_cap_flags2 = GDMA_DRV_CAP_FLAGS2;
 	req.gd_drv_cap_flags3 = GDMA_DRV_CAP_FLAGS3;
 	req.gd_drv_cap_flags4 = GDMA_DRV_CAP_FLAGS4;
@@ -1574,7 +1425,7 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev)
 			   &gc->pf_cap_flags1);
 
 	if (resp.pf_cap_flags1 & GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG) {
-		err = mana_gd_query_hwc_timeout(pdev, &hwc->hwc_timeout);
+		err = mana_gd_query_hwc_timeout(gc, &hwc->hwc_timeout);
 		if (err) {
 			dev_err(gc->dev, "Failed to set the hwc timeout %d\n", err);
 			return err;
@@ -1873,7 +1724,7 @@ int mana_gd_poll_cq(struct gdma_queue *cq, struct gdma_comp *comp, int num_cqe)
 }
 EXPORT_SYMBOL_NS(mana_gd_poll_cq, "NET_MANA");
 
-static irqreturn_t mana_gd_intr(int irq, void *arg)
+irqreturn_t mana_gd_intr(int irq, void *arg)
 {
 	struct gdma_irq_context *gic = arg;
 	struct list_head *eq_list = &gic->eq_list;
@@ -1888,11 +1739,25 @@ static irqreturn_t mana_gd_intr(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+/*
+ * Reset the device using whatever mechanism the bus provides.
+ */
+int mana_gd_dev_reset(struct gdma_context *gc)
+{
+	if (!gc->bus_ops || !gc->bus_ops->dev_reset)
+		return -EOPNOTSUPP;
+
+	return gc->bus_ops->dev_reset(gc);
+}
+
+/*
+ * Release a reference on the IRQ context backing an MSI vector, freeing
+ * the vector once the last user is gone.
+ */
 void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi)
 {
-	struct pci_dev *dev = to_pci_dev(gc->dev);
+	const struct gdma_bus_ops *ops = gc->bus_ops;
 	struct gdma_irq_context *gic;
-	struct msi_map irq_map;
 	int irq;
 
 	mutex_lock(&gc->gic_mutex);
@@ -1917,11 +1782,8 @@ void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi)
 	irq_update_affinity_hint(irq, NULL);
 	free_irq(irq, gic);
 
-	if (gic->dyn_msix) {
-		irq_map.virq = irq;
-		irq_map.index = msi;
-		pci_msix_free_irq(dev, irq_map);
-	}
+	if (gic->dyn_msix)
+		ops->msix_free(gc, msi, irq);
 
 	xa_erase(&gc->irq_contexts, msi);
 	kfree(gic);
@@ -1944,12 +1806,10 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
 					 bool use_msi_bitmap,
 					 int *msi_requested)
 {
-	struct pci_dev *dev = to_pci_dev(gc->dev);
+	const struct gdma_bus_ops *ops = gc->bus_ops;
 	struct gdma_irq_context *gic;
-	struct msi_map irq_map = { };
-	int irq;
-	int msi;
-	int err;
+	bool dyn_msix = false;
+	int msi, irq, err;
 
 	mutex_lock(&gc->gic_mutex);
 
@@ -1975,27 +1835,34 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
 		goto out;
 	}
 
-	irq = pci_irq_vector(dev, msi);
+	irq = ops->msix_virq(gc, msi);
 	if (irq == -EINVAL) {
-		irq_map = pci_msix_alloc_irq_at(dev, msi, NULL);
-		if (!irq_map.virq) {
-			err = irq_map.index;
-			dev_err(gc->dev,
-				"Failed to alloc irq_map msi %d err %d\n",
-				msi, err);
-			gic = ERR_PTR(err);
+		/* A bus that sizes its vector pool up front has nothing left
+		 * to hand out once every vector has been claimed.
+		 */
+		if (!ops->msix_alloc_at) {
+			dev_err(gc->dev, "No IRQ for MSI %d\n", msi);
+			gic = ERR_PTR(-ENOENT);
+			goto out;
+		}
+
+		irq = ops->msix_alloc_at(gc, &msi);
+		if (irq < 0) {
+			dev_err(gc->dev, "Failed to alloc irq msi %d err %d\n",
+				*msi_requested, irq);
+			gic = ERR_PTR(irq);
 			goto out;
 		}
-		irq = irq_map.virq;
-		msi = irq_map.index;
+
+		dyn_msix = true;
 		*msi_requested = msi;
 	}
 
 	gic = kzalloc_obj(*gic);
 	if (!gic) {
 		gic = ERR_PTR(-ENOMEM);
-		if (irq_map.virq)
-			pci_msix_free_irq(dev, irq_map);
+		if (dyn_msix)
+			ops->msix_free(gc, msi, irq);
 		goto out;
 	}
 
@@ -2006,11 +1873,11 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
 	spin_lock_init(&gic->lock);
 
 	if (!gic->msi)
-		snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
-			 pci_name(dev));
+		snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@%s:%s",
+			 ops->bus_name, dev_name(gc->dev));
 	else
-		snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@pci:%s",
-			 gic->msi, pci_name(dev));
+		snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@%s:%s",
+			 gic->msi, ops->bus_name, dev_name(gc->dev));
 
 	err = request_irq(irq, mana_gd_intr, 0, gic->name, gic);
 	if (err) {
@@ -2018,12 +1885,12 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
 			irq, gic->name);
 		kfree(gic);
 		gic = ERR_PTR(err);
-		if (irq_map.virq)
-			pci_msix_free_irq(dev, irq_map);
+		if (dyn_msix)
+			ops->msix_free(gc, msi, irq);
 		goto out;
 	}
 
-	gic->dyn_msix = !!irq_map.virq;
+	gic->dyn_msix = dyn_msix;
 	refcount_set(&gic->refcount, 1);
 	gic->bitmap_refs = use_msi_bitmap ? 1 : 0;
 
@@ -2034,8 +1901,8 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
 		free_irq(irq, gic);
 		kfree(gic);
 		gic = ERR_PTR(err);
-		if (irq_map.virq)
-			pci_msix_free_irq(dev, irq_map);
+		if (dyn_msix)
+			ops->msix_free(gc, msi, irq);
 		goto out;
 	}
 
@@ -2067,20 +1934,340 @@ void mana_gd_free_res_map(struct gdma_resource *r)
 	r->size = 0;
 }
 
-/*
- * Spread on CPUs with the following heuristics:
- *
- * 1. No more than one IRQ per CPU, if possible;
- * 2. NUMA locality is the second priority;
- * 3. Sibling dislocality is the last priority.
- *
- * Let's consider this topology:
- *
- * Node            0               1
- * Core        0       1       2       3
- * CPU       0   1   2   3   4   5   6   7
- *
- * The most performant IRQ distribution based on the above topology
+/* Bring up the GDMA context on a probed device: create the debugfs directory,
+ * map the shared-memory registers, start the hardware channel and size the
+ * interrupt pool. Every step here is common to all buses; the ones that are
+ * not are reached through gdma_bus_ops.
+ */
+int mana_gd_setup(struct gdma_context *gc)
+{
+	int err;
+
+	gc->mana_pci_debugfs = debugfs_create_dir(dev_name(gc->dev),
+						  mana_debugfs_root);
+
+	err = mana_gd_init_registers(gc);
+	if (err)
+		goto remove_debugfs;
+
+	mana_smc_init(&gc->shm_channel, gc->dev, gc->shm_base);
+
+	err = mana_gd_alloc_service_wq(gc);
+	if (err)
+		goto remove_debugfs;
+
+	err = gc->bus_ops->setup_hwc_irqs(gc);
+	if (err) {
+		dev_err(gc->dev, "Failed to setup IRQs for HWC creation: %d\n",
+			err);
+		goto free_workqueue;
+	}
+
+	err = mana_hwc_create_channel(gc);
+	if (err)
+		goto remove_irq;
+
+	err = mana_gd_verify_vf_version(gc);
+	if (err)
+		goto destroy_hwc;
+
+	err = mana_gd_detect_devices(gc);
+	if (err)
+		goto destroy_hwc;
+
+	err = mana_gd_query_max_resources(gc);
+	if (err)
+		goto destroy_hwc;
+
+	err = gc->bus_ops->setup_remaining_irqs(gc);
+	if (err)
+		goto destroy_hwc;
+
+	dev_dbg(gc->dev, "mana gdma setup successful\n");
+	return 0;
+
+destroy_hwc:
+	mana_hwc_destroy_channel(gc);
+remove_irq:
+	gc->bus_ops->remove_irqs(gc);
+free_workqueue:
+	mana_gd_free_service_wq(gc);
+remove_debugfs:
+	debugfs_remove_recursive(gc->mana_pci_debugfs);
+	gc->mana_pci_debugfs = NULL;
+	dev_err(gc->dev, "%s failed (error %d)\n", __func__, err);
+	return err;
+}
+
+void mana_gd_cleanup(struct gdma_context *gc)
+{
+	mana_hwc_destroy_channel(gc);
+
+	gc->bus_ops->remove_irqs(gc);
+
+	mana_gd_free_service_wq(gc);
+
+	debugfs_remove_recursive(gc->mana_pci_debugfs);
+	gc->mana_pci_debugfs = NULL;
+
+	dev_dbg(gc->dev, "mana gdma cleanup successful\n");
+}
+
+struct mana_dev_recovery {
+	struct list_head list;
+	struct pci_dev *pdev;
+	enum gdma_eqe_type type;
+};
+
+static struct mana_dev_recovery_work {
+	struct list_head dev_list;
+	struct delayed_work work;
+
+	/* Lock for dev_list above */
+	spinlock_t lock;
+} mana_dev_recovery_work;
+
+static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state);
+static int mana_gd_resume(struct pci_dev *pdev);
+
+#define MANA_SERVICE_PERIOD 10
+
+static void mana_serv_rescan(struct pci_dev *pdev)
+{
+	struct pci_bus *parent;
+
+	pci_lock_rescan_remove();
+
+	parent = pdev->bus;
+	if (!parent) {
+		dev_err(&pdev->dev, "MANA service: no parent bus\n");
+		goto out;
+	}
+
+	pci_stop_and_remove_bus_device(pdev);
+	pci_rescan_bus(parent);
+
+out:
+	pci_unlock_rescan_remove();
+}
+
+static void mana_serv_fpga(struct pci_dev *pdev)
+{
+	struct pci_bus *bus, *parent;
+
+	pci_lock_rescan_remove();
+
+	bus = pdev->bus;
+	if (!bus) {
+		dev_err(&pdev->dev, "MANA service: no bus\n");
+		goto out;
+	}
+
+	parent = bus->parent;
+	if (!parent) {
+		dev_err(&pdev->dev, "MANA service: no parent bus\n");
+		goto out;
+	}
+
+	pci_stop_and_remove_bus_device(bus->self);
+
+	msleep(MANA_SERVICE_PERIOD * 1000);
+
+	pci_rescan_bus(parent);
+
+out:
+	pci_unlock_rescan_remove();
+}
+
+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) {
+		/* Perform PCI rescan on device if GC is not set up */
+		dev_err(&pdev->dev, "MANA service: GC not setup, rescanning\n");
+		mana_serv_rescan(pdev);
+		return;
+	}
+
+	hwc = gc->hwc.driver_data;
+	if (!hwc) {
+		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);
+
+	msleep(MANA_SERVICE_PERIOD * 1000);
+
+	ret = mana_gd_resume(pdev);
+	if (ret == -ETIMEDOUT || ret == -EPROTO) {
+		/* Perform PCI rescan on device if we failed on HWC */
+		dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n");
+		mana_serv_rescan(pdev);
+		return;
+	}
+
+	if (ret)
+		dev_info(&pdev->dev, "MANA reset cycle failed err %d\n", ret);
+	else
+		dev_info(&pdev->dev, "MANA reset cycle completed\n");
+
+out:
+	clear_bit(GC_IN_SERVICE, &gc->flags);
+}
+
+static void mana_do_service(enum gdma_eqe_type type, struct pci_dev *pdev)
+{
+	switch (type) {
+	case GDMA_EQE_HWC_FPGA_RECONFIG:
+		mana_serv_fpga(pdev);
+		break;
+
+	case GDMA_EQE_HWC_RESET_REQUEST:
+		mana_serv_reset(pdev);
+		break;
+
+	default:
+		dev_err(&pdev->dev, "MANA service: unknown type %d\n", type);
+		break;
+	}
+}
+
+static void mana_recovery_delayed_func(struct work_struct *w)
+{
+	struct mana_dev_recovery_work *work;
+	struct mana_dev_recovery *dev;
+	unsigned long flags;
+
+	work = container_of(w, struct mana_dev_recovery_work, work.work);
+
+	spin_lock_irqsave(&work->lock, flags);
+
+	while (!list_empty(&work->dev_list)) {
+		dev = list_first_entry(&work->dev_list,
+				       struct mana_dev_recovery, list);
+		list_del(&dev->list);
+		spin_unlock_irqrestore(&work->lock, flags);
+
+		mana_do_service(dev->type, dev->pdev);
+		pci_dev_put(dev->pdev);
+		kfree(dev);
+
+		spin_lock_irqsave(&work->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&work->lock, flags);
+}
+
+static void mana_serv_func(struct work_struct *w)
+{
+	struct mana_serv_work *mns_wk;
+	struct pci_dev *pdev;
+
+	mns_wk = container_of(w, struct mana_serv_work, serv_work);
+	pdev = mns_wk->pdev;
+
+	if (pdev)
+		mana_do_service(mns_wk->type, pdev);
+
+	pci_dev_put(pdev);
+	kfree(mns_wk);
+	module_put(THIS_MODULE);
+}
+
+static int mana_pci_schedule_serv_work(struct gdma_context *gc,
+				       enum gdma_eqe_type type)
+{
+	struct mana_serv_work *mns_wk;
+
+	if (test_and_set_bit(GC_IN_SERVICE, &gc->flags)) {
+		dev_info(gc->dev, "Already in service\n");
+		return -EBUSY;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		dev_info(gc->dev, "Module is unloading\n");
+		clear_bit(GC_IN_SERVICE, &gc->flags);
+		return -ENODEV;
+	}
+
+	mns_wk = kzalloc(sizeof(*mns_wk), GFP_ATOMIC);
+	if (!mns_wk) {
+		module_put(THIS_MODULE);
+		clear_bit(GC_IN_SERVICE, &gc->flags);
+		return -ENOMEM;
+	}
+
+	dev_info(gc->dev, "Start MANA service type:%d\n", type);
+	mns_wk->pdev = to_pci_dev(gc->dev);
+	mns_wk->type = type;
+	pci_dev_get(mns_wk->pdev);
+	INIT_WORK(&mns_wk->serv_work, mana_serv_func);
+	schedule_work(&mns_wk->serv_work);
+	return 0;
+}
+
+static bool mana_pci_msix_can_alloc_dyn(struct gdma_context *gc)
+{
+	return pci_msix_can_alloc_dyn(to_pci_dev(gc->dev));
+}
+
+static int mana_pci_msix_virq(struct gdma_context *gc, int msi)
+{
+	return pci_irq_vector(to_pci_dev(gc->dev), msi);
+}
+
+static int mana_pci_msix_alloc_at(struct gdma_context *gc, int *msi)
+{
+	struct msi_map irq_map;
+
+	irq_map = pci_msix_alloc_irq_at(to_pci_dev(gc->dev), *msi, NULL);
+	if (!irq_map.virq)
+		return irq_map.index;
+
+	*msi = irq_map.index;
+	return irq_map.virq;
+}
+
+static void mana_pci_msix_free(struct gdma_context *gc, int msi, int irq)
+{
+	struct msi_map irq_map = { .virq = irq, .index = msi };
+
+	pci_msix_free_irq(to_pci_dev(gc->dev), irq_map);
+}
+
+static int mana_pci_msix_vec_count(struct gdma_context *gc)
+{
+	return pci_msix_vec_count(to_pci_dev(gc->dev));
+}
+
+static int mana_pci_dev_reset(struct gdma_context *gc)
+{
+	return pcie_flr(to_pci_dev(gc->dev));
+}
+
+/*
+ * Spread on CPUs with the following heuristics:
+ *
+ * 1. No more than one IRQ per CPU, if possible;
+ * 2. NUMA locality is the second priority;
+ * 3. Sibling dislocality is the last priority.
+ *
+ * Let's consider this topology:
+ *
+ * Node            0               1
+ * Core        0       1       2       3
+ * CPU       0   1   2   3   4   5   6   7
+ *
+ * The most performant IRQ distribution based on the above topology
  * and heuristics may look like this:
  *
  * IRQ     Nodes   Cores   CPUs
@@ -2396,101 +2583,52 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev)
 	gc->num_msix_usable = 0;
 }
 
-static int mana_gd_setup(struct pci_dev *pdev)
+static int mana_pci_setup_hwc_irqs(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	int err;
-
-	gc->mana_pci_debugfs = debugfs_create_dir(pci_name(pdev),
-						  mana_debugfs_root);
-
-	err = mana_gd_init_registers(pdev);
-	if (err)
-		goto remove_debugfs;
-
-	mana_smc_init(&gc->shm_channel, gc->dev, gc->shm_base);
-
-	gc->service_wq = alloc_ordered_workqueue("gdma_service_wq", 0);
-	if (!gc->service_wq) {
-		err = -ENOMEM;
-		goto remove_debugfs;
-	}
-
-	err = mana_gd_setup_hwc_irqs(pdev);
-	if (err) {
-		dev_err(gc->dev, "Failed to setup IRQs for HWC creation: %d\n",
-			err);
-		goto free_workqueue;
-	}
-
-	err = mana_hwc_create_channel(gc);
-	if (err)
-		goto remove_irq;
-
-	err = mana_gd_verify_vf_version(pdev);
-	if (err)
-		goto destroy_hwc;
-
-	err = mana_gd_detect_devices(pdev);
-	if (err)
-		goto destroy_hwc;
+	return mana_gd_setup_hwc_irqs(to_pci_dev(gc->dev));
+}
 
-	err = mana_gd_query_max_resources(pdev);
-	if (err)
-		goto destroy_hwc;
+static int mana_pci_setup_remaining_irqs(struct gdma_context *gc)
+{
+	int err;
 
-	err = mana_gd_setup_remaining_irqs(pdev);
+	err = mana_gd_setup_remaining_irqs(to_pci_dev(gc->dev));
 	if (err) {
 		dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
-		goto destroy_hwc;
+		return err;
 	}
 
 	if (!gc->msi_sharing) {
 		gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL);
-		if (!gc->msi_bitmap) {
-			err = -ENOMEM;
-			goto destroy_hwc;
-		}
+		if (!gc->msi_bitmap)
+			return -ENOMEM;
 		/* Set bit for HWC */
 		set_bit(0, gc->msi_bitmap);
 	}
 
-	dev_dbg(&pdev->dev, "mana gdma setup successful\n");
 	return 0;
-
-destroy_hwc:
-	mana_hwc_destroy_channel(gc);
-remove_irq:
-	mana_gd_remove_irqs(pdev);
-free_workqueue:
-	destroy_workqueue(gc->service_wq);
-	gc->service_wq = NULL;
-remove_debugfs:
-	debugfs_remove_recursive(gc->mana_pci_debugfs);
-	gc->mana_pci_debugfs = NULL;
-	dev_err(&pdev->dev, "%s failed (error %d)\n", __func__, err);
-	return err;
 }
 
-static void mana_gd_cleanup_device(struct pci_dev *pdev)
+static void mana_pci_remove_irqs(struct gdma_context *gc)
 {
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-
-	mana_hwc_destroy_channel(gc);
-
-	mana_gd_remove_irqs(pdev);
-
-	if (gc->service_wq) {
-		destroy_workqueue(gc->service_wq);
-		gc->service_wq = NULL;
-	}
-
-	debugfs_remove_recursive(gc->mana_pci_debugfs);
-	gc->mana_pci_debugfs = NULL;
-
-	dev_dbg(&pdev->dev, "mana gdma cleanup successful\n");
+	mana_gd_remove_irqs(to_pci_dev(gc->dev));
 }
 
+static const struct gdma_bus_ops mana_pci_bus_ops = {
+	.bus_name		= "pci",
+	.msix_can_alloc_dyn	= mana_pci_msix_can_alloc_dyn,
+	.msix_virq		= mana_pci_msix_virq,
+	.msix_alloc_at		= mana_pci_msix_alloc_at,
+	.msix_free		= mana_pci_msix_free,
+	.msix_vec_count		= mana_pci_msix_vec_count,
+	.setup_hwc_irqs		= mana_pci_setup_hwc_irqs,
+	.setup_remaining_irqs	= mana_pci_setup_remaining_irqs,
+	.remove_irqs		= mana_pci_remove_irqs,
+	.dev_reset		= mana_pci_dev_reset,
+	.schedule_serv_work	= mana_pci_schedule_serv_work,
+	.drv_cap_flags1		= GDMA_DRV_CAP_FLAGS1_PCI,
+};
+
 static bool mana_is_pf(unsigned short dev_id)
 {
 	return dev_id == MANA_PF_DEVICE_ID || dev_id == MANA_PF2_DEVICE_ID;
@@ -2546,9 +2684,10 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	gc->bar0_va = bar0_va;
 	gc->dev = &pdev->dev;
+	gc->bus_ops = &mana_pci_bus_ops;
 	xa_init(&gc->irq_contexts);
 
-	err = mana_gd_setup(pdev);
+	err = mana_gd_setup(gc);
 	if (err)
 		goto unmap_bar;
 
@@ -2576,7 +2715,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 cleanup_mana:
 	mana_remove(&gc->mana, false);
 cleanup_gd:
-	mana_gd_cleanup_device(pdev);
+	mana_gd_cleanup(gc);
 unmap_bar:
 	xa_destroy(&gc->irq_contexts);
 	pci_iounmap(pdev, bar0_va);
@@ -2629,7 +2768,7 @@ static void mana_gd_remove(struct pci_dev *pdev)
 	mana_rdma_remove(&gc->mana_ib);
 	mana_remove(&gc->mana, false);
 
-	mana_gd_cleanup_device(pdev);
+	mana_gd_cleanup(gc);
 
 	xa_destroy(&gc->irq_contexts);
 
@@ -2644,24 +2783,24 @@ static void mana_gd_remove(struct pci_dev *pdev)
 }
 
 /* The 'state' parameter is not used. */
-int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state)
+static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state)
 {
 	struct gdma_context *gc = pci_get_drvdata(pdev);
 
 	mana_rdma_remove(&gc->mana_ib);
 	mana_remove(&gc->mana, true);
 
-	mana_gd_cleanup_device(pdev);
+	mana_gd_cleanup(gc);
 
 	return 0;
 }
 
-int mana_gd_resume(struct pci_dev *pdev)
+static int mana_gd_resume(struct pci_dev *pdev)
 {
 	struct gdma_context *gc = pci_get_drvdata(pdev);
 	int err;
 
-	err = mana_gd_setup(pdev);
+	err = mana_gd_setup(gc);
 	if (err)
 		return err;
 
@@ -2676,7 +2815,7 @@ int mana_gd_resume(struct pci_dev *pdev)
 	return err;
 
 cleanup_gd:
-	mana_gd_cleanup_device(pdev);
+	mana_gd_cleanup(gc);
 	return err;
 }
 
@@ -2690,7 +2829,7 @@ static void mana_gd_shutdown(struct pci_dev *pdev)
 	mana_rdma_remove(&gc->mana_ib);
 	mana_remove(&gc->mana, true);
 
-	mana_gd_cleanup_device(pdev);
+	mana_gd_cleanup(gc);
 
 	pci_disable_device(pdev);
 }
@@ -2734,26 +2873,19 @@ static struct pci_driver mana_driver = {
 	.sriov_configure = mana_sriov_configure,
 };
 
-static int __init mana_driver_init(void)
-{
-	int err;
+MODULE_DEVICE_TABLE(pci, mana_id_table);
 
+static int mana_pci_driver_register(void)
+{
 	INIT_LIST_HEAD(&mana_dev_recovery_work.dev_list);
 	spin_lock_init(&mana_dev_recovery_work.lock);
-	INIT_DELAYED_WORK(&mana_dev_recovery_work.work, mana_recovery_delayed_func);
-
-	mana_debugfs_root = debugfs_create_dir("mana", NULL);
-
-	err = pci_register_driver(&mana_driver);
-	if (err) {
-		debugfs_remove(mana_debugfs_root);
-		mana_debugfs_root = NULL;
-	}
+	INIT_DELAYED_WORK(&mana_dev_recovery_work.work,
+			  mana_recovery_delayed_func);
 
-	return err;
+	return pci_register_driver(&mana_driver);
 }
 
-static void __exit mana_driver_exit(void)
+static void mana_pci_driver_unregister(void)
 {
 	struct mana_dev_recovery *dev;
 	unsigned long flags;
@@ -2771,6 +2903,29 @@ static void __exit mana_driver_exit(void)
 	spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
 
 	pci_unregister_driver(&mana_driver);
+}
+
+static int __init mana_driver_init(void)
+{
+	int err;
+
+	mana_debugfs_root = debugfs_create_dir("mana", NULL);
+
+	err = mana_pci_driver_register();
+	if (err)
+		goto err_debugfs;
+
+	return 0;
+
+err_debugfs:
+	debugfs_remove(mana_debugfs_root);
+	mana_debugfs_root = NULL;
+	return err;
+}
+
+static void __exit mana_driver_exit(void)
+{
+	mana_pci_driver_unregister();
 
 	debugfs_remove(mana_debugfs_root);
 
@@ -2780,7 +2935,5 @@ static void __exit mana_driver_exit(void)
 module_init(mana_driver_init);
 module_exit(mana_driver_exit);
 
-MODULE_DEVICE_TABLE(pci, mana_id_table);
-
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("Microsoft Azure Network Adapter driver");
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 591fb4191d90..62b89b3d6072 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -9,7 +9,6 @@
 #include <linux/ethtool.h>
 #include <linux/filter.h>
 #include <linux/mm.h>
-#include <linux/pci.h>
 #include <linux/export.h>
 #include <linux/skbuff.h>
 
@@ -20,6 +19,7 @@
 #include <net/xdp.h>
 
 #include <net/mana/mana.h>
+
 #include <net/mana/mana_auxiliary.h>
 #include <net/mana/hw_channel.h>
 
@@ -3725,10 +3725,9 @@ static int mana_dealloc_queues(struct net_device *ndev)
 				tsleep <<= 1;
 			}
 			if (atomic_read(&txq->pending_sends)) {
-				err =
-				    pcie_flr(to_pci_dev(gd->gdma_context->dev));
+				err = mana_gd_dev_reset(gd->gdma_context);
 				if (err) {
-					netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
+					netdev_err(ndev, "device reset failed %d with %d pkts pending in txq %u\n",
 						   err,
 					    atomic_read(&txq->pending_sends),
 					    txq->gdma_txq_id);
@@ -4031,6 +4030,9 @@ int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type even
 		return 0;
 	}
 
+	if (!gc->service_wq)
+		return -EOPNOTSUPP;
+
 	serv_work = kzalloc_obj(*serv_work, GFP_ATOMIC);
 	if (!serv_work)
 		return -ENOMEM;
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 308950f9b54b..cdec5558304f 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -422,14 +422,72 @@ enum gdma_context_flags {
 	GC_IN_SERVICE		= 1,
 };
 
+struct gdma_context;
+
+/**
+ * struct gdma_bus_ops - transport-specific operations
+ *
+ * Lets the bus-agnostic GDMA core drive PCI and CDX devices without
+ * testing configuration symbols. Each bus driver installs its own
+ * instance in gdma_context.bus_ops at probe time.
+ *
+ * @bus_name: Bus token used to build the per-vector IRQ names.
+ * @msix_can_alloc_dyn: True if the bus can allocate MSI-X vectors after
+ *	probe time. Buses that size their vector pool at probe and cannot
+ *	grow it later leave this NULL.
+ * @msix_virq: Return the Linux IRQ number backing an MSI vector, or
+ *	-EINVAL if the vector has not been allocated yet.
+ * @msix_alloc_at: Allocate an MSI vector on demand and return its IRQ
+ *	number, updating @msi with the index actually assigned. Buses that
+ *	allocate their whole pool up front leave this NULL.
+ * @msix_free: Release a vector obtained from @msix_alloc_at. Only needed
+ *	by buses that implement @msix_alloc_at.
+ * @msix_vec_count: Return the number of entries in the device MSI-X table,
+ *	used to clamp the vector count the core asks for. Buses that cannot
+ *	report a table size leave this NULL and the clamp is skipped.
+ * @setup_hwc_irqs: Set up the interrupts the hardware channel needs before
+ *	it is created.
+ * @setup_remaining_irqs: Set up the remaining interrupts once the device
+ *	resource limits are known.
+ * @remove_irqs: Release the interrupts set up by @setup_hwc_irqs and
+ *	@setup_remaining_irqs.
+ * @dev_reset: Reset the device using the bus-level reset mechanism.
+ * @schedule_serv_work: Queue device servicing or recovery. Buses without
+ *	a servicing path leave this NULL.
+ * @drv_cap_flags1: Extra capability bits this transport can honour, OR'ed
+ *	into GDMA_DRV_CAP_FLAGS1 when the driver version is negotiated.
+ */
+struct gdma_bus_ops {
+	const char *bus_name;
+	bool (*msix_can_alloc_dyn)(struct gdma_context *gc);
+	int (*msix_virq)(struct gdma_context *gc, int msi);
+	int (*msix_alloc_at)(struct gdma_context *gc, int *msi);
+	void (*msix_free)(struct gdma_context *gc, int msi, int irq);
+	int (*msix_vec_count)(struct gdma_context *gc);
+	int (*setup_hwc_irqs)(struct gdma_context *gc);
+	int (*setup_remaining_irqs)(struct gdma_context *gc);
+	void (*remove_irqs)(struct gdma_context *gc);
+	int (*dev_reset)(struct gdma_context *gc);
+	int (*schedule_serv_work)(struct gdma_context *gc,
+				  enum gdma_eqe_type type);
+	u64 drv_cap_flags1;
+};
+
 struct gdma_context {
 	struct device		*dev;
 	struct dentry		*mana_pci_debugfs;
 
+	/* Transport-specific operations, installed by the bus driver */
+	const struct gdma_bus_ops *bus_ops;
+
 	/* Hardware max number of queues */
 	unsigned int		max_num_queues;
 	/* Per-vPort max number of queues */
 	unsigned int		max_num_queues_vport;
+	/* Number of vPorts reported by the device, capped at
+	 * MAX_PORTS_IN_MANA_DEV
+	 */
+	u16			num_ports;
 	unsigned int		max_num_msix;
 	unsigned int		num_msix_usable;
 	struct xarray		irq_contexts;
@@ -529,6 +587,10 @@ ssize_t mana_gd_read_ring(struct gdma_queue *q, char __user *buf,
 
 int mana_schedule_serv_work(struct gdma_context *gc, enum gdma_eqe_type type);
 
+int mana_gd_alloc_service_wq(struct gdma_context *gc);
+
+void mana_gd_free_service_wq(struct gdma_context *gc);
+
 void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld,
 		      u32 mod_comps, bool mod_comps_vld);
 
@@ -686,21 +748,28 @@ enum {
 /* Driver supports non-contiguous queue buffers */
 #define GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS BIT(30)
 
+/* Capabilities in the PCI-only group below rely on dynamic MSI-X allocation
+ * and on the servicing and reset paths reached through
+ * mana_schedule_serv_work(). Transports that provide neither leave
+ * gdma_bus_ops.drv_cap_flags1 unset so the group is not advertised.
+ */
+#define GDMA_DRV_CAP_FLAGS1_PCI \
+	(GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT | \
+	 GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \
+	 GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \
+	 GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \
+	 GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY)
+
 #define GDMA_DRV_CAP_FLAGS1 \
 	(GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \
 	 GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
 	 GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG | \
 	 GDMA_DRV_CAP_FLAG_1_VARIABLE_INDIRECTION_TABLE_SUPPORT | \
 	 GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP | \
-	 GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT | \
-	 GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \
-	 GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \
 	 GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE | \
 	 GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY | \
 	 GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \
-	 GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \
 	 GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \
-	 GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \
 	 GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT | \
 	 GDMA_DRV_CAP_FLAG_1_DYN_INTERRUPT_MODERATION | \
 	 GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS)
@@ -1044,7 +1113,24 @@ struct gdma_destroy_dm_resp {
 	struct gdma_resp_hdr hdr;
 }; /* HW Data */
 
-int mana_gd_verify_vf_version(struct pci_dev *pdev);
+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);
+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);
+void mana_gd_process_eq_events(void *arg);
+
+static inline u32 mana_gd_r32(struct gdma_context *g, u64 offset)
+{
+	return readl(g->bar0_va + offset);
+}
+
+static inline u64 mana_gd_r64(struct gdma_context *g, u64 offset)
+{
+	return readq(g->bar0_va + offset);
+}
 
 int mana_gd_register_device(struct gdma_dev *gd);
 int mana_gd_deregister_device(struct gdma_dev *gd);
@@ -1077,8 +1163,6 @@ void mana_unregister_debugfs(void);
 
 int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type event);
 
-int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state);
-int mana_gd_resume(struct pci_dev *pdev);
 
 bool mana_need_log(struct gdma_context *gc, int err);
 
@@ -1089,4 +1173,6 @@ void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi);
 int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
 			     u32 proto_minor_ver, u32 proto_micro_ver,
 			     u16 *max_num_vports, u8 *bm_hostmode);
+int mana_gd_dev_reset(struct gdma_context *gc);
+
 #endif /* _GDMA_H */
-- 
2.54.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next 2/4] net: mana: Move PCI transport code into gdma_pci.c
  2026-09-24 17:30 [PATCH net-next 0/4] net: mana: Add support for the CDX bus Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 1/4] net: mana: Introduce gdma_bus_ops for bus-specific operations Manish Awasthi
@ 2026-09-24 17:30 ` Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 3/4] net: mana: Build the PCI transport as a separate module Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 4/4] net: mana: Add support for CDX device ID 0x00C2 Manish Awasthi
  3 siblings, 0 replies; 5+ messages in thread
From: Manish Awasthi @ 2026-09-24 17:30 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov
  Cc: horms, linux-hyperv, netdev, linux-kernel, linux-rdma, bpf,
	nipun.gupta, nikhil.agarwal, gargaditya, ernis, kees, paulros,
	mawasthi

Move PCI transport code and driver callbacks from gdma_main.c into
gdma_pci.c. Keep shared IRQ bookkeeping in the core and update build
wiring.

Signed-off-by: Manish Awasthi <mawasthi@linux.microsoft.com>
---
 drivers/net/ethernet/microsoft/mana/Makefile  |   3 +-
 .../net/ethernet/microsoft/mana/gdma_main.c   | 899 -----------------
 .../net/ethernet/microsoft/mana/gdma_pci.c    | 909 ++++++++++++++++++
 include/net/mana/gdma.h                       |   3 +
 4 files changed, 914 insertions(+), 900 deletions(-)
 create mode 100644 drivers/net/ethernet/microsoft/mana/gdma_pci.c

diff --git a/drivers/net/ethernet/microsoft/mana/Makefile b/drivers/net/ethernet/microsoft/mana/Makefile
index e16a4221f571..6705bf65c92c 100644
--- a/drivers/net/ethernet/microsoft/mana/Makefile
+++ b/drivers/net/ethernet/microsoft/mana/Makefile
@@ -3,4 +3,5 @@
 # Makefile for the Microsoft Azure Network Adapter driver
 
 obj-$(CONFIG_MICROSOFT_MANA) += mana.o
-mana-objs := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o mana_bpf.o
+mana-y := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o mana_bpf.o \
+	  gdma_pci.o
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index b076760b2dc5..531170e58a62 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -2,10 +2,7 @@
 /* Copyright (c) 2021, Microsoft Corporation. */
 
 #include <linux/bitfield.h>
-#include <linux/cpumask.h>
 #include <linux/debugfs.h>
-#include <linux/delay.h>
-#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/sizes.h>
 #include <linux/utsname.h>
@@ -13,11 +10,7 @@
 #include <linux/msi.h>
 #include <linux/irqdomain.h>
 #include <linux/export.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/topology.h>
 #include <linux/uaccess.h>
-#include <linux/xarray.h>
 
 #include <net/mana/mana.h>
 #include <net/mana/hw_channel.h>
@@ -2013,898 +2006,6 @@ void mana_gd_cleanup(struct gdma_context *gc)
 	dev_dbg(gc->dev, "mana gdma cleanup successful\n");
 }
 
-struct mana_dev_recovery {
-	struct list_head list;
-	struct pci_dev *pdev;
-	enum gdma_eqe_type type;
-};
-
-static struct mana_dev_recovery_work {
-	struct list_head dev_list;
-	struct delayed_work work;
-
-	/* Lock for dev_list above */
-	spinlock_t lock;
-} mana_dev_recovery_work;
-
-static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state);
-static int mana_gd_resume(struct pci_dev *pdev);
-
-#define MANA_SERVICE_PERIOD 10
-
-static void mana_serv_rescan(struct pci_dev *pdev)
-{
-	struct pci_bus *parent;
-
-	pci_lock_rescan_remove();
-
-	parent = pdev->bus;
-	if (!parent) {
-		dev_err(&pdev->dev, "MANA service: no parent bus\n");
-		goto out;
-	}
-
-	pci_stop_and_remove_bus_device(pdev);
-	pci_rescan_bus(parent);
-
-out:
-	pci_unlock_rescan_remove();
-}
-
-static void mana_serv_fpga(struct pci_dev *pdev)
-{
-	struct pci_bus *bus, *parent;
-
-	pci_lock_rescan_remove();
-
-	bus = pdev->bus;
-	if (!bus) {
-		dev_err(&pdev->dev, "MANA service: no bus\n");
-		goto out;
-	}
-
-	parent = bus->parent;
-	if (!parent) {
-		dev_err(&pdev->dev, "MANA service: no parent bus\n");
-		goto out;
-	}
-
-	pci_stop_and_remove_bus_device(bus->self);
-
-	msleep(MANA_SERVICE_PERIOD * 1000);
-
-	pci_rescan_bus(parent);
-
-out:
-	pci_unlock_rescan_remove();
-}
-
-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) {
-		/* Perform PCI rescan on device if GC is not set up */
-		dev_err(&pdev->dev, "MANA service: GC not setup, rescanning\n");
-		mana_serv_rescan(pdev);
-		return;
-	}
-
-	hwc = gc->hwc.driver_data;
-	if (!hwc) {
-		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);
-
-	msleep(MANA_SERVICE_PERIOD * 1000);
-
-	ret = mana_gd_resume(pdev);
-	if (ret == -ETIMEDOUT || ret == -EPROTO) {
-		/* Perform PCI rescan on device if we failed on HWC */
-		dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n");
-		mana_serv_rescan(pdev);
-		return;
-	}
-
-	if (ret)
-		dev_info(&pdev->dev, "MANA reset cycle failed err %d\n", ret);
-	else
-		dev_info(&pdev->dev, "MANA reset cycle completed\n");
-
-out:
-	clear_bit(GC_IN_SERVICE, &gc->flags);
-}
-
-static void mana_do_service(enum gdma_eqe_type type, struct pci_dev *pdev)
-{
-	switch (type) {
-	case GDMA_EQE_HWC_FPGA_RECONFIG:
-		mana_serv_fpga(pdev);
-		break;
-
-	case GDMA_EQE_HWC_RESET_REQUEST:
-		mana_serv_reset(pdev);
-		break;
-
-	default:
-		dev_err(&pdev->dev, "MANA service: unknown type %d\n", type);
-		break;
-	}
-}
-
-static void mana_recovery_delayed_func(struct work_struct *w)
-{
-	struct mana_dev_recovery_work *work;
-	struct mana_dev_recovery *dev;
-	unsigned long flags;
-
-	work = container_of(w, struct mana_dev_recovery_work, work.work);
-
-	spin_lock_irqsave(&work->lock, flags);
-
-	while (!list_empty(&work->dev_list)) {
-		dev = list_first_entry(&work->dev_list,
-				       struct mana_dev_recovery, list);
-		list_del(&dev->list);
-		spin_unlock_irqrestore(&work->lock, flags);
-
-		mana_do_service(dev->type, dev->pdev);
-		pci_dev_put(dev->pdev);
-		kfree(dev);
-
-		spin_lock_irqsave(&work->lock, flags);
-	}
-
-	spin_unlock_irqrestore(&work->lock, flags);
-}
-
-static void mana_serv_func(struct work_struct *w)
-{
-	struct mana_serv_work *mns_wk;
-	struct pci_dev *pdev;
-
-	mns_wk = container_of(w, struct mana_serv_work, serv_work);
-	pdev = mns_wk->pdev;
-
-	if (pdev)
-		mana_do_service(mns_wk->type, pdev);
-
-	pci_dev_put(pdev);
-	kfree(mns_wk);
-	module_put(THIS_MODULE);
-}
-
-static int mana_pci_schedule_serv_work(struct gdma_context *gc,
-				       enum gdma_eqe_type type)
-{
-	struct mana_serv_work *mns_wk;
-
-	if (test_and_set_bit(GC_IN_SERVICE, &gc->flags)) {
-		dev_info(gc->dev, "Already in service\n");
-		return -EBUSY;
-	}
-
-	if (!try_module_get(THIS_MODULE)) {
-		dev_info(gc->dev, "Module is unloading\n");
-		clear_bit(GC_IN_SERVICE, &gc->flags);
-		return -ENODEV;
-	}
-
-	mns_wk = kzalloc(sizeof(*mns_wk), GFP_ATOMIC);
-	if (!mns_wk) {
-		module_put(THIS_MODULE);
-		clear_bit(GC_IN_SERVICE, &gc->flags);
-		return -ENOMEM;
-	}
-
-	dev_info(gc->dev, "Start MANA service type:%d\n", type);
-	mns_wk->pdev = to_pci_dev(gc->dev);
-	mns_wk->type = type;
-	pci_dev_get(mns_wk->pdev);
-	INIT_WORK(&mns_wk->serv_work, mana_serv_func);
-	schedule_work(&mns_wk->serv_work);
-	return 0;
-}
-
-static bool mana_pci_msix_can_alloc_dyn(struct gdma_context *gc)
-{
-	return pci_msix_can_alloc_dyn(to_pci_dev(gc->dev));
-}
-
-static int mana_pci_msix_virq(struct gdma_context *gc, int msi)
-{
-	return pci_irq_vector(to_pci_dev(gc->dev), msi);
-}
-
-static int mana_pci_msix_alloc_at(struct gdma_context *gc, int *msi)
-{
-	struct msi_map irq_map;
-
-	irq_map = pci_msix_alloc_irq_at(to_pci_dev(gc->dev), *msi, NULL);
-	if (!irq_map.virq)
-		return irq_map.index;
-
-	*msi = irq_map.index;
-	return irq_map.virq;
-}
-
-static void mana_pci_msix_free(struct gdma_context *gc, int msi, int irq)
-{
-	struct msi_map irq_map = { .virq = irq, .index = msi };
-
-	pci_msix_free_irq(to_pci_dev(gc->dev), irq_map);
-}
-
-static int mana_pci_msix_vec_count(struct gdma_context *gc)
-{
-	return pci_msix_vec_count(to_pci_dev(gc->dev));
-}
-
-static int mana_pci_dev_reset(struct gdma_context *gc)
-{
-	return pcie_flr(to_pci_dev(gc->dev));
-}
-
-/*
- * Spread on CPUs with the following heuristics:
- *
- * 1. No more than one IRQ per CPU, if possible;
- * 2. NUMA locality is the second priority;
- * 3. Sibling dislocality is the last priority.
- *
- * Let's consider this topology:
- *
- * Node            0               1
- * Core        0       1       2       3
- * CPU       0   1   2   3   4   5   6   7
- *
- * The most performant IRQ distribution based on the above topology
- * and heuristics may look like this:
- *
- * IRQ     Nodes   Cores   CPUs
- * 0       1       0       0-1
- * 1       1       1       2-3
- * 2       1       0       0-1
- * 3       1       1       2-3
- * 4       2       2       4-5
- * 5       2       3       6-7
- * 6       2       2       4-5
- * 7       2       3       6-7
- *
- * The heuristics is implemented as follows.
- *
- * The outer for_each() loop resets the 'weight' to the actual number
- * of CPUs in the hop. Then inner for_each() loop decrements it by the
- * number of sibling groups (cores) while assigning first set of IRQs
- * to each group. IRQs 0 and 1 above are distributed this way.
- *
- * Now, because NUMA locality is more important, we should walk the
- * same set of siblings and assign 2nd set of IRQs (2 and 3), and it's
- * implemented by the medium while() loop. We do like this unless the
- * number of IRQs assigned on this hop will not become equal to number
- * of CPUs in the hop (weight == 0). Then we switch to the next hop and
- * do the same thing.
- */
-
-static int mana_irq_setup_numa_aware(unsigned int *irqs, unsigned int len,
-				     int node, bool skip_first_cpu)
-{
-	const struct cpumask *next, *prev = cpu_none_mask;
-	cpumask_var_t cpus __free(free_cpumask_var);
-	int cpu, weight;
-
-	if (!alloc_cpumask_var(&cpus, GFP_KERNEL))
-		return -ENOMEM;
-
-	rcu_read_lock();
-	for_each_numa_hop_mask(next, node) {
-		weight = cpumask_weight_andnot(next, prev);
-		while (weight > 0) {
-			cpumask_andnot(cpus, next, prev);
-			for_each_cpu(cpu, cpus) {
-				cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu));
-				--weight;
-
-				if (unlikely(skip_first_cpu)) {
-					skip_first_cpu = false;
-					continue;
-				}
-
-				if (len-- == 0)
-					goto done;
-
-				irq_set_affinity_and_hint(*irqs++, topology_sibling_cpumask(cpu));
-			}
-		}
-		prev = next;
-	}
-done:
-	rcu_read_unlock();
-	return 0;
-}
-
-/* must be called with cpus_read_lock() held */
-static void mana_irq_setup_linear(unsigned int *irqs, unsigned int len)
-{
-	int cpu;
-
-	for_each_online_cpu(cpu) {
-		if (len == 0)
-			break;
-
-		irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu));
-		len--;
-	}
-}
-
-static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	struct gdma_irq_context *gic;
-	int *irqs, err, i, msi;
-
-	irqs = kmalloc_objs(int, nvec);
-	if (!irqs)
-		return -ENOMEM;
-
-	/*
-	 * In this function, num_msix_usable = HWC IRQ + Queue IRQ.
-	 * nvec is only Queue IRQ (HWC already setup).
-	 * While processing the next pci irq vector, we start with index 1,
-	 * as IRQ vector at index 0 is already processed for HWC.
-	 * However, the population of irqs array starts with index 0, to be
-	 * further used in mana_irq_setup_numa_aware()
-	 */
-	for (i = 1; i <= nvec; i++) {
-		msi = i;
-		gic = mana_gd_get_gic(gc, false, &msi);
-		if (IS_ERR(gic)) {
-			err = PTR_ERR(gic);
-			goto free_irq;
-		}
-
-		irqs[i - 1] = gic->irq;
-	}
-
-	/*
-	 * When calling mana_irq_setup_numa_aware() for dynamically added IRQs,
-	 * if number of CPUs is more than or equal to allocated MSI-X, we need to
-	 * skip the first CPU sibling group since they are already affinitized to
-	 * HWC IRQ
-	 */
-	cpus_read_lock();
-	if (gc->num_msix_usable <= num_online_cpus()) {
-		err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node,
-						true);
-		if (err) {
-			cpus_read_unlock();
-			goto free_irq;
-		}
-	} else {
-		/*
-		 * When num_msix_usable are more than num_online_cpus, our
-		 * queue IRQs should be equal to num of online vCPUs.
-		 * We try to make sure queue IRQs spread across all vCPUs.
-		 * In such a case NUMA or CPU core affinity does not matter.
-		 * Note: in this case the total mana IRQ should always be
-		 * num_online_cpus + 1. The first HWC IRQ is already handled
-		 * in HWC setup calls
-		 * However, if CPUs went offline since num_msix_usable was
-		 * computed, queue IRQs will be more than num_online_cpus().
-		 * In such cases remaining extra IRQs will retain their default
-		 * affinity.
-		 */
-		int first_unassigned = num_online_cpus();
-
-		if (nvec > first_unassigned) {
-			char buf[32];
-
-			if (first_unassigned == nvec - 1)
-				snprintf(buf, sizeof(buf), "%d",
-					 first_unassigned);
-			else
-				snprintf(buf, sizeof(buf), "%d-%d",
-					 first_unassigned, nvec - 1);
-
-			dev_dbg(&pdev->dev,
-				"MANA IRQ indices #%s will retain the default CPU affinity\n",
-				buf);
-		}
-
-		mana_irq_setup_linear(irqs, nvec);
-	}
-
-	cpus_read_unlock();
-	kfree(irqs);
-	return 0;
-
-free_irq:
-	for (i -= 1; i > 0; i--)
-		mana_gd_put_gic(gc, false, i);
-	kfree(irqs);
-	return err;
-}
-
-static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	struct gdma_irq_context *gic;
-	int *irqs, *start_irqs;
-	unsigned int cpu;
-	int err, i, msi;
-
-	irqs = kmalloc_objs(int, nvec);
-	if (!irqs)
-		return -ENOMEM;
-
-	start_irqs = irqs;
-
-	for (i = 0; i < nvec; i++) {
-		msi = i;
-		gic = mana_gd_get_gic(gc, false, &msi);
-		if (IS_ERR(gic)) {
-			err = PTR_ERR(gic);
-			goto free_irq;
-		}
-
-		irqs[i] = gic->irq;
-	}
-
-	/* If number of IRQ is one extra than number of online CPUs,
-	 * then we need to assign IRQ0 (hwc irq) and IRQ1 to
-	 * same CPU.
-	 * Else we will use different CPUs for IRQ0 and IRQ1.
-	 * Also we are using cpumask_local_spread instead of
-	 * cpumask_first for the node, because the node can be
-	 * mem only.
-	 */
-	cpus_read_lock();
-	if (nvec > num_online_cpus()) {
-		cpu = cpumask_local_spread(0, gc->numa_node);
-		irq_set_affinity_and_hint(irqs[0], cpumask_of(cpu));
-		irqs++;
-		nvec -= 1;
-	}
-
-	err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node, false);
-	if (err) {
-		cpus_read_unlock();
-		goto free_irq;
-	}
-
-	cpus_read_unlock();
-	kfree(start_irqs);
-	return 0;
-
-free_irq:
-	for (i -= 1; i >= 0; i--)
-		mana_gd_put_gic(gc, false, i);
-
-	kfree(start_irqs);
-	return err;
-}
-
-static int mana_gd_setup_hwc_irqs(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	unsigned int max_irqs, min_irqs;
-	int nvec, err;
-
-	if (pci_msix_can_alloc_dyn(pdev)) {
-		max_irqs = 1;
-		min_irqs = 1;
-	} else {
-		/* Need 1 interrupt for HWC */
-		max_irqs = min(num_online_cpus(), MANA_MAX_NUM_QUEUES) + 1;
-		min_irqs = 2;
-		gc->msi_sharing = true;
-	}
-
-	nvec = pci_alloc_irq_vectors(pdev, min_irqs, max_irqs, PCI_IRQ_MSIX);
-	if (nvec < 0)
-		return nvec;
-
-	err = mana_gd_setup_irqs(pdev, nvec);
-	if (err) {
-		pci_free_irq_vectors(pdev);
-		return err;
-	}
-
-	gc->num_msix_usable = nvec;
-	gc->max_num_msix = nvec;
-
-	return 0;
-}
-
-static int mana_gd_setup_remaining_irqs(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	struct msi_map irq_map;
-	int max_irqs, i, err;
-
-	if (!pci_msix_can_alloc_dyn(pdev))
-		/* remain irqs are already allocated with HWC IRQ */
-		return 0;
-
-	/* allocate only remaining IRQs*/
-	max_irqs = gc->num_msix_usable - 1;
-
-	for (i = 1; i <= max_irqs; i++) {
-		irq_map = pci_msix_alloc_irq_at(pdev, i, NULL);
-		if (!irq_map.virq) {
-			err = irq_map.index;
-			/* caller will handle cleaning up all allocated
-			 * irqs, after HWC is destroyed
-			 */
-			return err;
-		}
-	}
-
-	err = mana_gd_setup_dyn_irqs(pdev, max_irqs);
-	if (err)
-		return err;
-
-	gc->max_num_msix = gc->max_num_msix + max_irqs;
-
-	return 0;
-}
-
-static void mana_gd_remove_irqs(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	int i;
-
-	if (gc->max_num_msix < 1)
-		return;
-
-	for (i = 0; i < gc->max_num_msix; i++) {
-		if (!xa_load(&gc->irq_contexts, i))
-			continue;
-
-		mana_gd_put_gic(gc, false, i);
-	}
-
-	WARN_ON(!xa_empty(&gc->irq_contexts));
-
-	pci_free_irq_vectors(pdev);
-
-	bitmap_free(gc->msi_bitmap);
-	gc->msi_bitmap = NULL;
-	gc->max_num_msix = 0;
-	gc->num_msix_usable = 0;
-}
-
-static int mana_pci_setup_hwc_irqs(struct gdma_context *gc)
-{
-	return mana_gd_setup_hwc_irqs(to_pci_dev(gc->dev));
-}
-
-static int mana_pci_setup_remaining_irqs(struct gdma_context *gc)
-{
-	int err;
-
-	err = mana_gd_setup_remaining_irqs(to_pci_dev(gc->dev));
-	if (err) {
-		dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
-		return err;
-	}
-
-	if (!gc->msi_sharing) {
-		gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL);
-		if (!gc->msi_bitmap)
-			return -ENOMEM;
-		/* Set bit for HWC */
-		set_bit(0, gc->msi_bitmap);
-	}
-
-	return 0;
-}
-
-static void mana_pci_remove_irqs(struct gdma_context *gc)
-{
-	mana_gd_remove_irqs(to_pci_dev(gc->dev));
-}
-
-static const struct gdma_bus_ops mana_pci_bus_ops = {
-	.bus_name		= "pci",
-	.msix_can_alloc_dyn	= mana_pci_msix_can_alloc_dyn,
-	.msix_virq		= mana_pci_msix_virq,
-	.msix_alloc_at		= mana_pci_msix_alloc_at,
-	.msix_free		= mana_pci_msix_free,
-	.msix_vec_count		= mana_pci_msix_vec_count,
-	.setup_hwc_irqs		= mana_pci_setup_hwc_irqs,
-	.setup_remaining_irqs	= mana_pci_setup_remaining_irqs,
-	.remove_irqs		= mana_pci_remove_irqs,
-	.dev_reset		= mana_pci_dev_reset,
-	.schedule_serv_work	= mana_pci_schedule_serv_work,
-	.drv_cap_flags1		= GDMA_DRV_CAP_FLAGS1_PCI,
-};
-
-static bool mana_is_pf(unsigned short dev_id)
-{
-	return dev_id == MANA_PF_DEVICE_ID || dev_id == MANA_PF2_DEVICE_ID;
-}
-
-static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
-	struct gdma_context *gc;
-	void __iomem *bar0_va;
-	int bar = 0;
-	int err;
-
-	/* Each port has 2 CQs, each CQ has at most 1 EQE at a time */
-	BUILD_BUG_ON(2 * MAX_PORTS_IN_MANA_DEV * GDMA_EQE_SIZE > EQ_SIZE);
-
-	err = pci_enable_device(pdev);
-	if (err) {
-		dev_err(&pdev->dev, "Failed to enable pci device (err=%d)\n", err);
-		return -ENXIO;
-	}
-
-	pci_set_master(pdev);
-
-	err = pci_request_regions(pdev, "mana");
-	if (err)
-		goto disable_dev;
-
-	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
-	if (err) {
-		dev_err(&pdev->dev, "DMA set mask failed: %d\n", err);
-		goto release_region;
-	}
-	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
-
-	err = -ENOMEM;
-	gc = vzalloc(sizeof(*gc));
-	if (!gc)
-		goto release_region;
-
-	mutex_init(&gc->eq_test_event_mutex);
-	mutex_init(&gc->gic_mutex);
-	pci_set_drvdata(pdev, gc);
-	gc->bar0_pa = pci_resource_start(pdev, 0);
-	gc->bar0_size = pci_resource_len(pdev, 0);
-
-	bar0_va = pci_iomap(pdev, bar, 0);
-	if (!bar0_va)
-		goto free_gc;
-
-	gc->numa_node = dev_to_node(&pdev->dev);
-	gc->is_pf = mana_is_pf(pdev->device);
-	gc->is_pf2 = (pdev->device == MANA_PF2_DEVICE_ID);
-
-	gc->bar0_va = bar0_va;
-	gc->dev = &pdev->dev;
-	gc->bus_ops = &mana_pci_bus_ops;
-	xa_init(&gc->irq_contexts);
-
-	err = mana_gd_setup(gc);
-	if (err)
-		goto unmap_bar;
-
-	err = mana_probe(&gc->mana, false);
-	if (err)
-		goto cleanup_gd;
-
-	err = mana_rdma_probe(&gc->mana_ib);
-	if (err)
-		goto cleanup_mana;
-
-	/*
-	 * If a hardware reset event has occurred over HWC during probe,
-	 * rollback and perform hardware reset procedure.
-	 */
-	if (test_and_set_bit(GC_PROBE_SUCCEEDED, &gc->flags)) {
-		err = -EPROTO;
-		goto cleanup_mana_rdma;
-	}
-
-	return 0;
-
-cleanup_mana_rdma:
-	mana_rdma_remove(&gc->mana_ib);
-cleanup_mana:
-	mana_remove(&gc->mana, false);
-cleanup_gd:
-	mana_gd_cleanup(gc);
-unmap_bar:
-	xa_destroy(&gc->irq_contexts);
-	pci_iounmap(pdev, bar0_va);
-free_gc:
-	pci_set_drvdata(pdev, NULL);
-	vfree(gc);
-release_region:
-	pci_release_regions(pdev);
-disable_dev:
-	pci_disable_device(pdev);
-	dev_err(&pdev->dev, "gdma probe failed: err = %d\n", err);
-
-	/*
-	 * Hardware could be in recovery mode and the HWC returns TIMEDOUT or
-	 * EPROTO from mana_gd_setup(), mana_probe() or mana_rdma_probe(), or
-	 * we received a hardware reset event over HWC interrupt. In this case,
-	 * perform the device recovery procedure after MANA_SERVICE_PERIOD
-	 * seconds.
-	 */
-	if (err == -ETIMEDOUT || err == -EPROTO) {
-		struct mana_dev_recovery *dev;
-		unsigned long flags;
-
-		dev_info(&pdev->dev, "Start MANA recovery mode\n");
-
-		dev = kzalloc_obj(*dev);
-		if (!dev)
-			return err;
-
-		dev->pdev = pci_dev_get(pdev);
-		dev->type = GDMA_EQE_HWC_RESET_REQUEST;
-
-		spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
-		list_add_tail(&dev->list, &mana_dev_recovery_work.dev_list);
-		spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
-
-		schedule_delayed_work(&mana_dev_recovery_work.work,
-				      secs_to_jiffies(MANA_SERVICE_PERIOD));
-	}
-
-	return err;
-}
-
-static void mana_gd_remove(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-
-	pci_disable_sriov(pdev);
-
-	mana_rdma_remove(&gc->mana_ib);
-	mana_remove(&gc->mana, false);
-
-	mana_gd_cleanup(gc);
-
-	xa_destroy(&gc->irq_contexts);
-
-	pci_iounmap(pdev, gc->bar0_va);
-
-	vfree(gc);
-
-	pci_release_regions(pdev);
-	pci_disable_device(pdev);
-
-	dev_dbg(&pdev->dev, "mana gdma remove successful\n");
-}
-
-/* The 'state' parameter is not used. */
-static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-
-	mana_rdma_remove(&gc->mana_ib);
-	mana_remove(&gc->mana, true);
-
-	mana_gd_cleanup(gc);
-
-	return 0;
-}
-
-static int mana_gd_resume(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-	int err;
-
-	err = mana_gd_setup(gc);
-	if (err)
-		return err;
-
-	err = mana_probe(&gc->mana, true);
-	if (err)
-		goto cleanup_gd;
-
-	err = mana_rdma_probe(&gc->mana_ib);
-	if (err)
-		mana_rdma_remove(&gc->mana_ib);
-
-	return err;
-
-cleanup_gd:
-	mana_gd_cleanup(gc);
-	return err;
-}
-
-/* Quiesce the device for kexec. This is also called upon reboot/shutdown. */
-static void mana_gd_shutdown(struct pci_dev *pdev)
-{
-	struct gdma_context *gc = pci_get_drvdata(pdev);
-
-	dev_info(&pdev->dev, "Shutdown was called\n");
-
-	mana_rdma_remove(&gc->mana_ib);
-	mana_remove(&gc->mana, true);
-
-	mana_gd_cleanup(gc);
-
-	pci_disable_device(pdev);
-}
-
-static int mana_sriov_configure(struct pci_dev *pdev, int numvfs)
-{
-	int err = 0;
-
-	dev_info(&pdev->dev, "Requested num VFs: %d\n", numvfs);
-
-	if (numvfs > 0) {
-		err = pci_enable_sriov(pdev, numvfs);
-	} else {
-		if (pci_vfs_assigned(pdev)) {
-			dev_warn(&pdev->dev,
-				 "Cannot disable SR-IOV while VFs are assigned\n");
-			return -EPERM;
-		}
-
-		pci_disable_sriov(pdev);
-	}
-
-	return err ? err : numvfs;
-}
-
-static const struct pci_device_id mana_id_table[] = {
-	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) },
-	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) },
-	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) },
-	{ }
-};
-
-static struct pci_driver mana_driver = {
-	.name		= "mana",
-	.id_table	= mana_id_table,
-	.probe		= mana_gd_probe,
-	.remove		= mana_gd_remove,
-	.suspend	= mana_gd_suspend,
-	.resume		= mana_gd_resume,
-	.shutdown	= mana_gd_shutdown,
-	.sriov_configure = mana_sriov_configure,
-};
-
-MODULE_DEVICE_TABLE(pci, mana_id_table);
-
-static int mana_pci_driver_register(void)
-{
-	INIT_LIST_HEAD(&mana_dev_recovery_work.dev_list);
-	spin_lock_init(&mana_dev_recovery_work.lock);
-	INIT_DELAYED_WORK(&mana_dev_recovery_work.work,
-			  mana_recovery_delayed_func);
-
-	return pci_register_driver(&mana_driver);
-}
-
-static void mana_pci_driver_unregister(void)
-{
-	struct mana_dev_recovery *dev;
-	unsigned long flags;
-
-	disable_delayed_work_sync(&mana_dev_recovery_work.work);
-
-	spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
-	while (!list_empty(&mana_dev_recovery_work.dev_list)) {
-		dev = list_first_entry(&mana_dev_recovery_work.dev_list,
-				       struct mana_dev_recovery, list);
-		list_del(&dev->list);
-		pci_dev_put(dev->pdev);
-		kfree(dev);
-	}
-	spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
-
-	pci_unregister_driver(&mana_driver);
-}
-
 static int __init mana_driver_init(void)
 {
 	int err;
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_pci.c b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
new file mode 100644
index 000000000000..4f6a77f55e5a
--- /dev/null
+++ b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
@@ -0,0 +1,909 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (c) 2025, Microsoft Corporation. */
+
+#include <linux/bitmap.h>
+#include <linux/cpumask.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/topology.h>
+#include <linux/xarray.h>
+
+#include <net/mana/mana.h>
+#include <net/mana/hw_channel.h>
+
+struct mana_dev_recovery {
+	struct list_head list;
+	struct pci_dev *pdev;
+	enum gdma_eqe_type type;
+};
+
+static struct mana_dev_recovery_work {
+	struct list_head dev_list;
+	struct delayed_work work;
+
+	/* Lock for dev_list above */
+	spinlock_t lock;
+} mana_dev_recovery_work;
+
+static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state);
+static int mana_gd_resume(struct pci_dev *pdev);
+
+#define MANA_SERVICE_PERIOD 10
+
+static void mana_serv_rescan(struct pci_dev *pdev)
+{
+	struct pci_bus *parent;
+
+	pci_lock_rescan_remove();
+
+	parent = pdev->bus;
+	if (!parent) {
+		dev_err(&pdev->dev, "MANA service: no parent bus\n");
+		goto out;
+	}
+
+	pci_stop_and_remove_bus_device(pdev);
+	pci_rescan_bus(parent);
+
+out:
+	pci_unlock_rescan_remove();
+}
+
+static void mana_serv_fpga(struct pci_dev *pdev)
+{
+	struct pci_bus *bus, *parent;
+
+	pci_lock_rescan_remove();
+
+	bus = pdev->bus;
+	if (!bus) {
+		dev_err(&pdev->dev, "MANA service: no bus\n");
+		goto out;
+	}
+
+	parent = bus->parent;
+	if (!parent) {
+		dev_err(&pdev->dev, "MANA service: no parent bus\n");
+		goto out;
+	}
+
+	pci_stop_and_remove_bus_device(bus->self);
+
+	msleep(MANA_SERVICE_PERIOD * 1000);
+
+	pci_rescan_bus(parent);
+
+out:
+	pci_unlock_rescan_remove();
+}
+
+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) {
+		/* Perform PCI rescan on device if GC is not set up */
+		dev_err(&pdev->dev, "MANA service: GC not setup, rescanning\n");
+		mana_serv_rescan(pdev);
+		return;
+	}
+
+	hwc = gc->hwc.driver_data;
+	if (!hwc) {
+		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);
+
+	msleep(MANA_SERVICE_PERIOD * 1000);
+
+	ret = mana_gd_resume(pdev);
+	if (ret == -ETIMEDOUT || ret == -EPROTO) {
+		/* Perform PCI rescan on device if we failed on HWC */
+		dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n");
+		mana_serv_rescan(pdev);
+		return;
+	}
+
+	if (ret)
+		dev_info(&pdev->dev, "MANA reset cycle failed err %d\n", ret);
+	else
+		dev_info(&pdev->dev, "MANA reset cycle completed\n");
+
+out:
+	clear_bit(GC_IN_SERVICE, &gc->flags);
+}
+
+static void mana_do_service(enum gdma_eqe_type type, struct pci_dev *pdev)
+{
+	switch (type) {
+	case GDMA_EQE_HWC_FPGA_RECONFIG:
+		mana_serv_fpga(pdev);
+		break;
+
+	case GDMA_EQE_HWC_RESET_REQUEST:
+		mana_serv_reset(pdev);
+		break;
+
+	default:
+		dev_err(&pdev->dev, "MANA service: unknown type %d\n", type);
+		break;
+	}
+}
+
+static void mana_recovery_delayed_func(struct work_struct *w)
+{
+	struct mana_dev_recovery_work *work;
+	struct mana_dev_recovery *dev;
+	unsigned long flags;
+
+	work = container_of(w, struct mana_dev_recovery_work, work.work);
+
+	spin_lock_irqsave(&work->lock, flags);
+
+	while (!list_empty(&work->dev_list)) {
+		dev = list_first_entry(&work->dev_list,
+				       struct mana_dev_recovery, list);
+		list_del(&dev->list);
+		spin_unlock_irqrestore(&work->lock, flags);
+
+		mana_do_service(dev->type, dev->pdev);
+		pci_dev_put(dev->pdev);
+		kfree(dev);
+
+		spin_lock_irqsave(&work->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&work->lock, flags);
+}
+
+static void mana_serv_func(struct work_struct *w)
+{
+	struct mana_serv_work *mns_wk;
+	struct pci_dev *pdev;
+
+	mns_wk = container_of(w, struct mana_serv_work, serv_work);
+	pdev = mns_wk->pdev;
+
+	if (pdev)
+		mana_do_service(mns_wk->type, pdev);
+
+	pci_dev_put(pdev);
+	kfree(mns_wk);
+	module_put(THIS_MODULE);
+}
+
+static int mana_pci_schedule_serv_work(struct gdma_context *gc,
+				       enum gdma_eqe_type type)
+{
+	struct mana_serv_work *mns_wk;
+
+	if (test_and_set_bit(GC_IN_SERVICE, &gc->flags)) {
+		dev_info(gc->dev, "Already in service\n");
+		return -EBUSY;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		dev_info(gc->dev, "Module is unloading\n");
+		clear_bit(GC_IN_SERVICE, &gc->flags);
+		return -ENODEV;
+	}
+
+	mns_wk = kzalloc(sizeof(*mns_wk), GFP_ATOMIC);
+	if (!mns_wk) {
+		module_put(THIS_MODULE);
+		clear_bit(GC_IN_SERVICE, &gc->flags);
+		return -ENOMEM;
+	}
+
+	dev_info(gc->dev, "Start MANA service type:%d\n", type);
+	mns_wk->pdev = to_pci_dev(gc->dev);
+	mns_wk->type = type;
+	pci_dev_get(mns_wk->pdev);
+	INIT_WORK(&mns_wk->serv_work, mana_serv_func);
+	schedule_work(&mns_wk->serv_work);
+	return 0;
+}
+
+static bool mana_pci_msix_can_alloc_dyn(struct gdma_context *gc)
+{
+	return pci_msix_can_alloc_dyn(to_pci_dev(gc->dev));
+}
+
+static int mana_pci_msix_virq(struct gdma_context *gc, int msi)
+{
+	return pci_irq_vector(to_pci_dev(gc->dev), msi);
+}
+
+static int mana_pci_msix_alloc_at(struct gdma_context *gc, int *msi)
+{
+	struct msi_map irq_map;
+
+	irq_map = pci_msix_alloc_irq_at(to_pci_dev(gc->dev), *msi, NULL);
+	if (!irq_map.virq)
+		return irq_map.index;
+
+	*msi = irq_map.index;
+	return irq_map.virq;
+}
+
+static void mana_pci_msix_free(struct gdma_context *gc, int msi, int irq)
+{
+	struct msi_map irq_map = { .virq = irq, .index = msi };
+
+	pci_msix_free_irq(to_pci_dev(gc->dev), irq_map);
+}
+
+static int mana_pci_msix_vec_count(struct gdma_context *gc)
+{
+	return pci_msix_vec_count(to_pci_dev(gc->dev));
+}
+
+static int mana_pci_dev_reset(struct gdma_context *gc)
+{
+	return pcie_flr(to_pci_dev(gc->dev));
+}
+
+/*
+ * Spread on CPUs with the following heuristics:
+ *
+ * 1. No more than one IRQ per CPU, if possible;
+ * 2. NUMA locality is the second priority;
+ * 3. Sibling dislocality is the last priority.
+ *
+ * Let's consider this topology:
+ *
+ * Node            0               1
+ * Core        0       1       2       3
+ * CPU       0   1   2   3   4   5   6   7
+ *
+ * The most performant IRQ distribution based on the above topology
+ * and heuristics may look like this:
+ *
+ * IRQ     Nodes   Cores   CPUs
+ * 0       1       0       0-1
+ * 1       1       1       2-3
+ * 2       1       0       0-1
+ * 3       1       1       2-3
+ * 4       2       2       4-5
+ * 5       2       3       6-7
+ * 6       2       2       4-5
+ * 7       2       3       6-7
+ *
+ * The heuristics is implemented as follows.
+ *
+ * The outer for_each() loop resets the 'weight' to the actual number
+ * of CPUs in the hop. Then inner for_each() loop decrements it by the
+ * number of sibling groups (cores) while assigning first set of IRQs
+ * to each group. IRQs 0 and 1 above are distributed this way.
+ *
+ * Now, because NUMA locality is more important, we should walk the
+ * same set of siblings and assign 2nd set of IRQs (2 and 3), and it's
+ * implemented by the medium while() loop. We do like this unless the
+ * number of IRQs assigned on this hop will not become equal to number
+ * of CPUs in the hop (weight == 0). Then we switch to the next hop and
+ * do the same thing.
+ */
+
+static int mana_irq_setup_numa_aware(unsigned int *irqs, unsigned int len,
+				     int node, bool skip_first_cpu)
+{
+	const struct cpumask *next, *prev = cpu_none_mask;
+	cpumask_var_t cpus __free(free_cpumask_var);
+	int cpu, weight;
+
+	if (!alloc_cpumask_var(&cpus, GFP_KERNEL))
+		return -ENOMEM;
+
+	rcu_read_lock();
+	for_each_numa_hop_mask(next, node) {
+		weight = cpumask_weight_andnot(next, prev);
+		while (weight > 0) {
+			cpumask_andnot(cpus, next, prev);
+			for_each_cpu(cpu, cpus) {
+				cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu));
+				--weight;
+
+				if (unlikely(skip_first_cpu)) {
+					skip_first_cpu = false;
+					continue;
+				}
+
+				if (len-- == 0)
+					goto done;
+
+				irq_set_affinity_and_hint(*irqs++, topology_sibling_cpumask(cpu));
+			}
+		}
+		prev = next;
+	}
+done:
+	rcu_read_unlock();
+	return 0;
+}
+
+/* must be called with cpus_read_lock() held */
+static void mana_irq_setup_linear(unsigned int *irqs, unsigned int len)
+{
+	int cpu;
+
+	for_each_online_cpu(cpu) {
+		if (len == 0)
+			break;
+
+		irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu));
+		len--;
+	}
+}
+
+static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	struct gdma_irq_context *gic;
+	int *irqs, err, i, msi;
+
+	irqs = kmalloc_objs(int, nvec);
+	if (!irqs)
+		return -ENOMEM;
+
+	/*
+	 * In this function, num_msix_usable = HWC IRQ + Queue IRQ.
+	 * nvec is only Queue IRQ (HWC already setup).
+	 * While processing the next pci irq vector, we start with index 1,
+	 * as IRQ vector at index 0 is already processed for HWC.
+	 * However, the population of irqs array starts with index 0, to be
+	 * further used in mana_irq_setup_numa_aware()
+	 */
+	for (i = 1; i <= nvec; i++) {
+		msi = i;
+		gic = mana_gd_get_gic(gc, false, &msi);
+		if (IS_ERR(gic)) {
+			err = PTR_ERR(gic);
+			goto free_irq;
+		}
+
+		irqs[i - 1] = gic->irq;
+	}
+
+	/*
+	 * When calling mana_irq_setup_numa_aware() for dynamically added IRQs,
+	 * if number of CPUs is more than or equal to allocated MSI-X, we need to
+	 * skip the first CPU sibling group since they are already affinitized to
+	 * HWC IRQ
+	 */
+	cpus_read_lock();
+	if (gc->num_msix_usable <= num_online_cpus()) {
+		err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node,
+						true);
+		if (err) {
+			cpus_read_unlock();
+			goto free_irq;
+		}
+	} else {
+		/*
+		 * When num_msix_usable are more than num_online_cpus, our
+		 * queue IRQs should be equal to num of online vCPUs.
+		 * We try to make sure queue IRQs spread across all vCPUs.
+		 * In such a case NUMA or CPU core affinity does not matter.
+		 * Note: in this case the total mana IRQ should always be
+		 * num_online_cpus + 1. The first HWC IRQ is already handled
+		 * in HWC setup calls
+		 * However, if CPUs went offline since num_msix_usable was
+		 * computed, queue IRQs will be more than num_online_cpus().
+		 * In such cases remaining extra IRQs will retain their default
+		 * affinity.
+		 */
+		int first_unassigned = num_online_cpus();
+
+		if (nvec > first_unassigned) {
+			char buf[32];
+
+			if (first_unassigned == nvec - 1)
+				snprintf(buf, sizeof(buf), "%d",
+					 first_unassigned);
+			else
+				snprintf(buf, sizeof(buf), "%d-%d",
+					 first_unassigned, nvec - 1);
+
+			dev_dbg(&pdev->dev,
+				"MANA IRQ indices #%s will retain the default CPU affinity\n",
+				buf);
+		}
+
+		mana_irq_setup_linear(irqs, nvec);
+	}
+
+	cpus_read_unlock();
+	kfree(irqs);
+	return 0;
+
+free_irq:
+	for (i -= 1; i > 0; i--)
+		mana_gd_put_gic(gc, false, i);
+	kfree(irqs);
+	return err;
+}
+
+static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	struct gdma_irq_context *gic;
+	int *irqs, *start_irqs;
+	unsigned int cpu;
+	int err, i, msi;
+
+	irqs = kmalloc_objs(int, nvec);
+	if (!irqs)
+		return -ENOMEM;
+
+	start_irqs = irqs;
+
+	for (i = 0; i < nvec; i++) {
+		msi = i;
+		gic = mana_gd_get_gic(gc, false, &msi);
+		if (IS_ERR(gic)) {
+			err = PTR_ERR(gic);
+			goto free_irq;
+		}
+
+		irqs[i] = gic->irq;
+	}
+
+	/* If number of IRQ is one extra than number of online CPUs,
+	 * then we need to assign IRQ0 (hwc irq) and IRQ1 to
+	 * same CPU.
+	 * Else we will use different CPUs for IRQ0 and IRQ1.
+	 * Also we are using cpumask_local_spread instead of
+	 * cpumask_first for the node, because the node can be
+	 * mem only.
+	 */
+	cpus_read_lock();
+	if (nvec > num_online_cpus()) {
+		cpu = cpumask_local_spread(0, gc->numa_node);
+		irq_set_affinity_and_hint(irqs[0], cpumask_of(cpu));
+		irqs++;
+		nvec -= 1;
+	}
+
+	err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node, false);
+	if (err) {
+		cpus_read_unlock();
+		goto free_irq;
+	}
+
+	cpus_read_unlock();
+	kfree(start_irqs);
+	return 0;
+
+free_irq:
+	for (i -= 1; i >= 0; i--)
+		mana_gd_put_gic(gc, false, i);
+
+	kfree(start_irqs);
+	return err;
+}
+
+static int mana_gd_setup_hwc_irqs(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	unsigned int max_irqs, min_irqs;
+	int nvec, err;
+
+	if (pci_msix_can_alloc_dyn(pdev)) {
+		max_irqs = 1;
+		min_irqs = 1;
+	} else {
+		/* Need 1 interrupt for HWC */
+		max_irqs = min(num_online_cpus(), MANA_MAX_NUM_QUEUES) + 1;
+		min_irqs = 2;
+		gc->msi_sharing = true;
+	}
+
+	nvec = pci_alloc_irq_vectors(pdev, min_irqs, max_irqs, PCI_IRQ_MSIX);
+	if (nvec < 0)
+		return nvec;
+
+	err = mana_gd_setup_irqs(pdev, nvec);
+	if (err) {
+		pci_free_irq_vectors(pdev);
+		return err;
+	}
+
+	gc->num_msix_usable = nvec;
+	gc->max_num_msix = nvec;
+
+	return 0;
+}
+
+static int mana_gd_setup_remaining_irqs(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	struct msi_map irq_map;
+	int max_irqs, i, err;
+
+	if (!pci_msix_can_alloc_dyn(pdev))
+		/* remain irqs are already allocated with HWC IRQ */
+		return 0;
+
+	/* allocate only remaining IRQs*/
+	max_irqs = gc->num_msix_usable - 1;
+
+	for (i = 1; i <= max_irqs; i++) {
+		irq_map = pci_msix_alloc_irq_at(pdev, i, NULL);
+		if (!irq_map.virq) {
+			err = irq_map.index;
+			/* caller will handle cleaning up all allocated
+			 * irqs, after HWC is destroyed
+			 */
+			return err;
+		}
+	}
+
+	err = mana_gd_setup_dyn_irqs(pdev, max_irqs);
+	if (err)
+		return err;
+
+	gc->max_num_msix = gc->max_num_msix + max_irqs;
+
+	return 0;
+}
+
+static void mana_gd_remove_irqs(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	int i;
+
+	if (gc->max_num_msix < 1)
+		return;
+
+	for (i = 0; i < gc->max_num_msix; i++) {
+		if (!xa_load(&gc->irq_contexts, i))
+			continue;
+
+		mana_gd_put_gic(gc, false, i);
+	}
+
+	WARN_ON(!xa_empty(&gc->irq_contexts));
+
+	pci_free_irq_vectors(pdev);
+
+	bitmap_free(gc->msi_bitmap);
+	gc->msi_bitmap = NULL;
+	gc->max_num_msix = 0;
+	gc->num_msix_usable = 0;
+}
+
+static int mana_pci_setup_hwc_irqs(struct gdma_context *gc)
+{
+	return mana_gd_setup_hwc_irqs(to_pci_dev(gc->dev));
+}
+
+static int mana_pci_setup_remaining_irqs(struct gdma_context *gc)
+{
+	int err;
+
+	err = mana_gd_setup_remaining_irqs(to_pci_dev(gc->dev));
+	if (err) {
+		dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
+		return err;
+	}
+
+	if (!gc->msi_sharing) {
+		gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL);
+		if (!gc->msi_bitmap)
+			return -ENOMEM;
+		/* Set bit for HWC */
+		set_bit(0, gc->msi_bitmap);
+	}
+
+	return 0;
+}
+
+static void mana_pci_remove_irqs(struct gdma_context *gc)
+{
+	mana_gd_remove_irqs(to_pci_dev(gc->dev));
+}
+
+static const struct gdma_bus_ops mana_pci_bus_ops = {
+	.bus_name		= "pci",
+	.msix_can_alloc_dyn	= mana_pci_msix_can_alloc_dyn,
+	.msix_virq		= mana_pci_msix_virq,
+	.msix_alloc_at		= mana_pci_msix_alloc_at,
+	.msix_free		= mana_pci_msix_free,
+	.msix_vec_count		= mana_pci_msix_vec_count,
+	.setup_hwc_irqs		= mana_pci_setup_hwc_irqs,
+	.setup_remaining_irqs	= mana_pci_setup_remaining_irqs,
+	.remove_irqs		= mana_pci_remove_irqs,
+	.dev_reset		= mana_pci_dev_reset,
+	.schedule_serv_work	= mana_pci_schedule_serv_work,
+	.drv_cap_flags1		= GDMA_DRV_CAP_FLAGS1_PCI,
+};
+
+static bool mana_is_pf(unsigned short dev_id)
+{
+	return dev_id == MANA_PF_DEVICE_ID || dev_id == MANA_PF2_DEVICE_ID;
+}
+
+static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct gdma_context *gc;
+	void __iomem *bar0_va;
+	int bar = 0;
+	int err;
+
+	/* Each port has 2 CQs, each CQ has at most 1 EQE at a time */
+	BUILD_BUG_ON(2 * MAX_PORTS_IN_MANA_DEV * GDMA_EQE_SIZE > EQ_SIZE);
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to enable pci device (err=%d)\n", err);
+		return -ENXIO;
+	}
+
+	pci_set_master(pdev);
+
+	err = pci_request_regions(pdev, "mana");
+	if (err)
+		goto disable_dev;
+
+	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+	if (err) {
+		dev_err(&pdev->dev, "DMA set mask failed: %d\n", err);
+		goto release_region;
+	}
+	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
+
+	err = -ENOMEM;
+	gc = vzalloc(sizeof(*gc));
+	if (!gc)
+		goto release_region;
+
+	mutex_init(&gc->eq_test_event_mutex);
+	mutex_init(&gc->gic_mutex);
+	pci_set_drvdata(pdev, gc);
+	gc->bar0_pa = pci_resource_start(pdev, 0);
+	gc->bar0_size = pci_resource_len(pdev, 0);
+
+	bar0_va = pci_iomap(pdev, bar, 0);
+	if (!bar0_va)
+		goto free_gc;
+
+	gc->numa_node = dev_to_node(&pdev->dev);
+	gc->is_pf = mana_is_pf(pdev->device);
+	gc->is_pf2 = (pdev->device == MANA_PF2_DEVICE_ID);
+
+	gc->bar0_va = bar0_va;
+	gc->dev = &pdev->dev;
+	gc->bus_ops = &mana_pci_bus_ops;
+	xa_init(&gc->irq_contexts);
+
+	err = mana_gd_setup(gc);
+	if (err)
+		goto unmap_bar;
+
+	err = mana_probe(&gc->mana, false);
+	if (err)
+		goto cleanup_gd;
+
+	err = mana_rdma_probe(&gc->mana_ib);
+	if (err)
+		goto cleanup_mana;
+
+	/*
+	 * If a hardware reset event has occurred over HWC during probe,
+	 * rollback and perform hardware reset procedure.
+	 */
+	if (test_and_set_bit(GC_PROBE_SUCCEEDED, &gc->flags)) {
+		err = -EPROTO;
+		goto cleanup_mana_rdma;
+	}
+
+	return 0;
+
+cleanup_mana_rdma:
+	mana_rdma_remove(&gc->mana_ib);
+cleanup_mana:
+	mana_remove(&gc->mana, false);
+cleanup_gd:
+	mana_gd_cleanup(gc);
+unmap_bar:
+	xa_destroy(&gc->irq_contexts);
+	pci_iounmap(pdev, bar0_va);
+free_gc:
+	pci_set_drvdata(pdev, NULL);
+	vfree(gc);
+release_region:
+	pci_release_regions(pdev);
+disable_dev:
+	pci_disable_device(pdev);
+	dev_err(&pdev->dev, "gdma probe failed: err = %d\n", err);
+
+	/*
+	 * Hardware could be in recovery mode and the HWC returns TIMEDOUT or
+	 * EPROTO from mana_gd_setup(), mana_probe() or mana_rdma_probe(), or
+	 * we received a hardware reset event over HWC interrupt. In this case,
+	 * perform the device recovery procedure after MANA_SERVICE_PERIOD
+	 * seconds.
+	 */
+	if (err == -ETIMEDOUT || err == -EPROTO) {
+		struct mana_dev_recovery *dev;
+		unsigned long flags;
+
+		dev_info(&pdev->dev, "Start MANA recovery mode\n");
+
+		dev = kzalloc_obj(*dev);
+		if (!dev)
+			return err;
+
+		dev->pdev = pci_dev_get(pdev);
+		dev->type = GDMA_EQE_HWC_RESET_REQUEST;
+
+		spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
+		list_add_tail(&dev->list, &mana_dev_recovery_work.dev_list);
+		spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
+
+		schedule_delayed_work(&mana_dev_recovery_work.work,
+				      secs_to_jiffies(MANA_SERVICE_PERIOD));
+	}
+
+	return err;
+}
+
+static void mana_gd_remove(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+
+	pci_disable_sriov(pdev);
+
+	mana_rdma_remove(&gc->mana_ib);
+	mana_remove(&gc->mana, false);
+
+	mana_gd_cleanup(gc);
+
+	xa_destroy(&gc->irq_contexts);
+
+	pci_iounmap(pdev, gc->bar0_va);
+
+	vfree(gc);
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+
+	dev_dbg(&pdev->dev, "mana gdma remove successful\n");
+}
+
+/* The 'state' parameter is not used. */
+static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+
+	mana_rdma_remove(&gc->mana_ib);
+	mana_remove(&gc->mana, true);
+
+	mana_gd_cleanup(gc);
+
+	return 0;
+}
+
+static int mana_gd_resume(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+	int err;
+
+	err = mana_gd_setup(gc);
+	if (err)
+		return err;
+
+	err = mana_probe(&gc->mana, true);
+	if (err)
+		goto cleanup_gd;
+
+	err = mana_rdma_probe(&gc->mana_ib);
+	if (err)
+		mana_rdma_remove(&gc->mana_ib);
+
+	return err;
+
+cleanup_gd:
+	mana_gd_cleanup(gc);
+	return err;
+}
+
+/* Quiesce the device for kexec. This is also called upon reboot/shutdown. */
+static void mana_gd_shutdown(struct pci_dev *pdev)
+{
+	struct gdma_context *gc = pci_get_drvdata(pdev);
+
+	dev_info(&pdev->dev, "Shutdown was called\n");
+
+	mana_rdma_remove(&gc->mana_ib);
+	mana_remove(&gc->mana, true);
+
+	mana_gd_cleanup(gc);
+
+	pci_disable_device(pdev);
+}
+
+static int mana_sriov_configure(struct pci_dev *pdev, int numvfs)
+{
+	int err = 0;
+
+	dev_info(&pdev->dev, "Requested num VFs: %d\n", numvfs);
+
+	if (numvfs > 0) {
+		err = pci_enable_sriov(pdev, numvfs);
+	} else {
+		if (pci_vfs_assigned(pdev)) {
+			dev_warn(&pdev->dev,
+				 "Cannot disable SR-IOV while VFs are assigned\n");
+			return -EPERM;
+		}
+
+		pci_disable_sriov(pdev);
+	}
+
+	return err ? err : numvfs;
+}
+
+static const struct pci_device_id mana_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) },
+	{ }
+};
+
+static struct pci_driver mana_driver = {
+	.name		= "mana",
+	.id_table	= mana_id_table,
+	.probe		= mana_gd_probe,
+	.remove		= mana_gd_remove,
+	.suspend	= mana_gd_suspend,
+	.resume		= mana_gd_resume,
+	.shutdown	= mana_gd_shutdown,
+	.sriov_configure = mana_sriov_configure,
+};
+
+MODULE_DEVICE_TABLE(pci, mana_id_table);
+
+int mana_pci_driver_register(void)
+{
+	INIT_LIST_HEAD(&mana_dev_recovery_work.dev_list);
+	spin_lock_init(&mana_dev_recovery_work.lock);
+	INIT_DELAYED_WORK(&mana_dev_recovery_work.work,
+			  mana_recovery_delayed_func);
+
+	return pci_register_driver(&mana_driver);
+}
+
+void mana_pci_driver_unregister(void)
+{
+	struct mana_dev_recovery *dev;
+	unsigned long flags;
+
+	disable_delayed_work_sync(&mana_dev_recovery_work.work);
+
+	spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
+	while (!list_empty(&mana_dev_recovery_work.dev_list)) {
+		dev = list_first_entry(&mana_dev_recovery_work.dev_list,
+				       struct mana_dev_recovery, list);
+		list_del(&dev->list);
+		pci_dev_put(dev->pdev);
+		kfree(dev);
+	}
+	spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
+
+	pci_unregister_driver(&mana_driver);
+}
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index cdec5558304f..238ff8b9f067 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -1175,4 +1175,7 @@ int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
 			     u16 *max_num_vports, u8 *bm_hostmode);
 int mana_gd_dev_reset(struct gdma_context *gc);
 
+int mana_pci_driver_register(void);
+void mana_pci_driver_unregister(void);
+
 #endif /* _GDMA_H */
-- 
2.54.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next 3/4] net: mana: Build the PCI transport as a separate module
  2026-09-24 17:30 [PATCH net-next 0/4] net: mana: Add support for the CDX bus Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 1/4] net: mana: Introduce gdma_bus_ops for bus-specific operations Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 2/4] net: mana: Move PCI transport code into gdma_pci.c Manish Awasthi
@ 2026-09-24 17:30 ` Manish Awasthi
  2026-09-24 17:30 ` [PATCH net-next 4/4] net: mana: Add support for CDX device ID 0x00C2 Manish Awasthi
  3 siblings, 0 replies; 5+ messages in thread
From: Manish Awasthi @ 2026-09-24 17:30 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov
  Cc: horms, linux-hyperv, netdev, linux-kernel, linux-rdma, bpf,
	nipun.gupta, nikhil.agarwal, gargaditya, ernis, kees, paulros,
	mawasthi

Build the shared GDMA and Ethernet code as gdma_core.ko, selected by
MICROSOFT_GDMA_CORE. Build the PCI transport separately as mana.ko and
export the required core symbols.

Preserve the PCI module name, device table, and mana.eth/mana.rdma
auxiliary-device names. Each transport registers its own bus driver.

Signed-off-by: Manish Awasthi <mawasthi@linux.microsoft.com>
---
 drivers/net/ethernet/microsoft/Kconfig        | 12 ++++++---
 drivers/net/ethernet/microsoft/Makefile       |  2 +-
 drivers/net/ethernet/microsoft/mana/Makefile  |  7 ++++--
 .../net/ethernet/microsoft/mana/gdma_main.c   | 25 ++++++-------------
 .../net/ethernet/microsoft/mana/gdma_pci.c    | 11 ++++++--
 drivers/net/ethernet/microsoft/mana/mana_en.c |  7 +++++-
 include/net/mana/gdma.h                       |  3 ---
 7 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig
index e9be18c92ca5..ee5bc1c1bf59 100644
--- a/drivers/net/ethernet/microsoft/Kconfig
+++ b/drivers/net/ethernet/microsoft/Kconfig
@@ -15,15 +15,19 @@ config NET_VENDOR_MICROSOFT
 
 if NET_VENDOR_MICROSOFT
 
+config MICROSOFT_GDMA_CORE
+	tristate
+	select AUXILIARY_BUS
+	select DIMLIB
+	select PAGE_POOL
+	select NET_SHAPER
+
 config MICROSOFT_MANA
 	tristate "Microsoft Azure Network Adapter (MANA) support"
 	depends on PCI_MSI
 	depends on X86_64 || (ARM64 && !CPU_BIG_ENDIAN)
 	depends on PCI_HYPERV
-	select AUXILIARY_BUS
-	select DIMLIB
-	select PAGE_POOL
-	select NET_SHAPER
+	select MICROSOFT_GDMA_CORE
 	help
 	  This driver supports Microsoft Azure Network Adapter (MANA).
 	  So far, the driver is only supported on X86_64.
diff --git a/drivers/net/ethernet/microsoft/Makefile b/drivers/net/ethernet/microsoft/Makefile
index d2ddc218135f..c3b6dafdf473 100644
--- a/drivers/net/ethernet/microsoft/Makefile
+++ b/drivers/net/ethernet/microsoft/Makefile
@@ -2,4 +2,4 @@
 # Makefile for the Microsoft Azure network device driver.
 #
 
-obj-$(CONFIG_MICROSOFT_MANA) += mana/
+obj-$(CONFIG_MICROSOFT_GDMA_CORE) += mana/
diff --git a/drivers/net/ethernet/microsoft/mana/Makefile b/drivers/net/ethernet/microsoft/mana/Makefile
index 6705bf65c92c..72c03fb6e441 100644
--- a/drivers/net/ethernet/microsoft/mana/Makefile
+++ b/drivers/net/ethernet/microsoft/mana/Makefile
@@ -2,6 +2,9 @@
 #
 # Makefile for the Microsoft Azure Network Adapter driver
 
+obj-$(CONFIG_MICROSOFT_GDMA_CORE) += gdma_core.o
+gdma_core-y := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o \
+	       mana_bpf.o
+
 obj-$(CONFIG_MICROSOFT_MANA) += mana.o
-mana-y := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o mana_bpf.o \
-	  gdma_pci.o
+mana-y := gdma_pci.o
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 531170e58a62..ae0ed700b3b9 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1991,6 +1991,7 @@ int mana_gd_setup(struct gdma_context *gc)
 	dev_err(gc->dev, "%s failed (error %d)\n", __func__, err);
 	return err;
 }
+EXPORT_SYMBOL_NS(mana_gd_setup, "NET_MANA");
 
 void mana_gd_cleanup(struct gdma_context *gc)
 {
@@ -2005,36 +2006,24 @@ void mana_gd_cleanup(struct gdma_context *gc)
 
 	dev_dbg(gc->dev, "mana gdma cleanup successful\n");
 }
+EXPORT_SYMBOL_NS(mana_gd_cleanup, "NET_MANA");
 
-static int __init mana_driver_init(void)
+static int __init gdma_core_init(void)
 {
-	int err;
-
 	mana_debugfs_root = debugfs_create_dir("mana", NULL);
 
-	err = mana_pci_driver_register();
-	if (err)
-		goto err_debugfs;
-
 	return 0;
-
-err_debugfs:
-	debugfs_remove(mana_debugfs_root);
-	mana_debugfs_root = NULL;
-	return err;
 }
 
-static void __exit mana_driver_exit(void)
+static void __exit gdma_core_exit(void)
 {
-	mana_pci_driver_unregister();
-
 	debugfs_remove(mana_debugfs_root);
 
 	mana_debugfs_root = NULL;
 }
 
-module_init(mana_driver_init);
-module_exit(mana_driver_exit);
+module_init(gdma_core_init);
+module_exit(gdma_core_exit);
 
 MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("Microsoft Azure Network Adapter driver");
+MODULE_DESCRIPTION("Microsoft Azure Network Adapter GDMA core");
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_pci.c b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
index 4f6a77f55e5a..92a1e66e18b7 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_pci.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
@@ -878,7 +878,7 @@ static struct pci_driver mana_driver = {
 
 MODULE_DEVICE_TABLE(pci, mana_id_table);
 
-int mana_pci_driver_register(void)
+static int __init mana_driver_init(void)
 {
 	INIT_LIST_HEAD(&mana_dev_recovery_work.dev_list);
 	spin_lock_init(&mana_dev_recovery_work.lock);
@@ -888,7 +888,7 @@ int mana_pci_driver_register(void)
 	return pci_register_driver(&mana_driver);
 }
 
-void mana_pci_driver_unregister(void)
+static void __exit mana_driver_exit(void)
 {
 	struct mana_dev_recovery *dev;
 	unsigned long flags;
@@ -907,3 +907,10 @@ void mana_pci_driver_unregister(void)
 
 	pci_unregister_driver(&mana_driver);
 }
+
+module_init(mana_driver_init);
+module_exit(mana_driver_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Microsoft Azure Network Adapter driver");
+MODULE_IMPORT_NS("NET_MANA");
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 62b89b3d6072..16a9d192c099 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3957,7 +3957,8 @@ static int add_adev(struct gdma_dev *gd, const char *name)
 
 	/* madev is owned by the auxiliary device */
 	madev = NULL;
-	ret = auxiliary_device_add(adev);
+	/* Keep the established mana.{eth,rdma} auxiliary match names. */
+	ret = __auxiliary_device_add(adev, "mana");
 	if (ret)
 		goto add_fail;
 
@@ -4194,6 +4195,7 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
 
 	return err;
 }
+EXPORT_SYMBOL_NS(mana_probe, "NET_MANA");
 
 void mana_remove(struct gdma_dev *gd, bool suspending)
 {
@@ -4272,6 +4274,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
 	kfree(ac);
 	dev_dbg(dev, "%s succeeded\n", __func__);
 }
+EXPORT_SYMBOL_NS(mana_remove, "NET_MANA");
 
 int mana_rdma_probe(struct gdma_dev *gd)
 {
@@ -4307,6 +4310,7 @@ int mana_rdma_probe(struct gdma_dev *gd)
 
 	return err;
 }
+EXPORT_SYMBOL_NS(mana_rdma_probe, "NET_MANA");
 
 void mana_rdma_remove(struct gdma_dev *gd)
 {
@@ -4327,6 +4331,7 @@ void mana_rdma_remove(struct gdma_dev *gd)
 
 	mana_gd_deregister_device(gd);
 }
+EXPORT_SYMBOL_NS(mana_rdma_remove, "NET_MANA");
 
 struct net_device *mana_get_primary_netdev(struct mana_context *ac,
 					   u32 port_index,
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 238ff8b9f067..cdec5558304f 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -1175,7 +1175,4 @@ int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
 			     u16 *max_num_vports, u8 *bm_hostmode);
 int mana_gd_dev_reset(struct gdma_context *gc);
 
-int mana_pci_driver_register(void);
-void mana_pci_driver_unregister(void);
-
 #endif /* _GDMA_H */
-- 
2.54.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next 4/4] net: mana: Add support for CDX device ID 0x00C2
  2026-09-24 17:30 [PATCH net-next 0/4] net: mana: Add support for the CDX bus Manish Awasthi
                   ` (2 preceding siblings ...)
  2026-09-24 17:30 ` [PATCH net-next 3/4] net: mana: Build the PCI transport as a separate module Manish Awasthi
@ 2026-09-24 17:30 ` Manish Awasthi
  3 siblings, 0 replies; 5+ messages in thread
From: Manish Awasthi @ 2026-09-24 17:30 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
	edumazet, kuba, pabeni, kotaranov
  Cc: horms, linux-hyperv, netdev, linux-kernel, linux-rdma, bpf,
	nipun.gupta, nikhil.agarwal, gargaditya, ernis, kees, paulros,
	mawasthi

Add the CDX transport as mana_cdx.ko for device ID 0x00C2, using the shared
gdma_core.ko. Allocate interrupts at probe, limit queues to available
vectors, use 32-bit DMA, and keep CDX auxiliary devices separate from PCI.

Signed-off-by: Manish Awasthi <mawasthi@linux.microsoft.com>
---
 drivers/net/ethernet/microsoft/Kconfig        |  14 +
 drivers/net/ethernet/microsoft/mana/Makefile  |   3 +
 .../net/ethernet/microsoft/mana/gdma_cdx.c    | 340 ++++++++++++++++++
 .../net/ethernet/microsoft/mana/gdma_pci.c    |   1 +
 drivers/net/ethernet/microsoft/mana/mana_en.c |   5 +-
 include/net/mana/gdma.h                       |   4 +
 6 files changed, 365 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ethernet/microsoft/mana/gdma_cdx.c

diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig
index ee5bc1c1bf59..7e4b77f7f29c 100644
--- a/drivers/net/ethernet/microsoft/Kconfig
+++ b/drivers/net/ethernet/microsoft/Kconfig
@@ -35,4 +35,18 @@ config MICROSOFT_MANA
 	  To compile this driver as a module, choose M here.
 	  The module will be called mana.
 
+config MICROSOFT_MANA_CDX
+	tristate "Microsoft Azure Network Adapter (MANA) support for the CDX bus"
+	depends on CDX_BUS
+	depends on GENERIC_MSI_IRQ
+	depends on 64BIT && !CPU_BIG_ENDIAN
+	select MICROSOFT_GDMA_CORE
+	help
+	  This driver supports the Microsoft Azure Network Adapter (MANA) on
+	  systems where it is integrated into the SoC and presented on the CDX
+	  bus rather than discovered over PCI.
+
+	  To compile this driver as a module, choose M here.
+	  The module will be called mana_cdx.
+
 endif #NET_VENDOR_MICROSOFT
diff --git a/drivers/net/ethernet/microsoft/mana/Makefile b/drivers/net/ethernet/microsoft/mana/Makefile
index 72c03fb6e441..64926015f734 100644
--- a/drivers/net/ethernet/microsoft/mana/Makefile
+++ b/drivers/net/ethernet/microsoft/mana/Makefile
@@ -8,3 +8,6 @@ gdma_core-y := gdma_main.o shm_channel.o hw_channel.o mana_en.o mana_ethtool.o \
 
 obj-$(CONFIG_MICROSOFT_MANA) += mana.o
 mana-y := gdma_pci.o
+
+obj-$(CONFIG_MICROSOFT_MANA_CDX) += mana_cdx.o
+mana_cdx-y := gdma_cdx.o
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_cdx.c b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c
new file mode 100644
index 000000000000..0d01e3d2f0d9
--- /dev/null
+++ b/drivers/net/ethernet/microsoft/mana/gdma_cdx.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (c) 2025, Microsoft Corporation. */
+#include <linux/module.h>
+#include <linux/bitmap.h>
+#include <linux/cdx/cdx_bus.h>
+#include <linux/msi.h>
+#include <linux/xarray.h>
+
+#include <net/mana/mana.h>
+
+#define MANA_CDX_GDMA_REGS_SIZE	(GDMA_REG_SHM_OFFSET + sizeof(u64))
+
+static int mana_cdx_msix_virq(struct gdma_context *gc, int msi)
+{
+	unsigned int virq = msi_get_virq(gc->dev, msi);
+
+	/* CDX allocates its whole vector pool in mana_cdx_setup_hwc_irqs(),
+	 * so a vector with no Linux IRQ was never part of that pool.
+	 */
+	return virq ? virq : -EINVAL;
+}
+
+static int mana_cdx_dev_reset(struct gdma_context *gc)
+{
+	return cdx_dev_reset(gc->dev);
+}
+
+static int mana_cdx_setup_hwc_irqs(struct gdma_context *gc)
+{
+	struct cdx_device *cdx_dev = to_cdx_device(gc->dev);
+	struct gdma_irq_context *gic;
+	unsigned int max_irqs;
+	int nvec;
+	int err, i = 0, j;
+
+	max_irqs = cdx_dev->num_msi;
+
+	/* Need 1 IRQ for HWC and at least 1 for the data path queues */
+	if (max_irqs < 2) {
+		dev_err(&cdx_dev->dev, "Not enough MSI vectors: %u\n",
+			max_irqs);
+		return -ENOSPC;
+	}
+
+	/* Constrain the device to 32-bit DMA addresses, for both the MSI
+	 * doorbells and the data path buffers, and never widen it afterwards.
+	 *
+	 * The MSI doorbell address has to land within the low 4GB. The data
+	 * path carries a second constraint: the device does not perform
+	 * writes to the final page of the 48-bit IOVA window, and
+	 * iommu_dma_alloc_iova() allocates downwards from the top of that
+	 * window, so the first queue created lands on the unusable page and
+	 * its event queue stalls permanently. PCI devices are shielded from
+	 * this by the low-4GB first attempt in iommu_dma_alloc_iova(), but
+	 * that attempt is gated on dev_is_pci() and CDX devices do not
+	 * qualify.
+	 */
+	err = dma_set_mask_and_coherent(gc->dev, DMA_BIT_MASK(32));
+	if (err) {
+		dev_err(&cdx_dev->dev, "Failed to set 32-bit DMA mask\n");
+		return err;
+	}
+
+	/* The PCI path also calls dma_set_max_seg_size(), but that is
+	 * deliberately omitted here: the CDX core does not attach a
+	 * dma_parms to the device, so the helper would only WARN, and MANA
+	 * never builds scatter-gather lists, so the limit is never read.
+	 */
+
+	err = cdx_enable_msi(cdx_dev);
+	if (err) {
+		dev_err(&cdx_dev->dev, "Failed to enable MSI\n");
+		return err;
+	}
+
+	err = msi_domain_alloc_irqs(gc->dev, MSI_DEFAULT_DOMAIN, max_irqs);
+	if (err) {
+		dev_err(&cdx_dev->dev, "Failed to alloc MSI IRQs\n");
+		cdx_disable_msi(cdx_dev);
+		return err;
+	}
+
+	nvec = max_irqs;
+
+	xa_init(&gc->irq_contexts);
+
+	/* No IRQ affinity hint is applied here. The PCI transport spreads its
+	 * vectors over the NUMA node local to the device; the CDX platforms
+	 * this driver targets are single-node, so there is nothing to spread
+	 * over. Affinity management can be added when a multi-node CDX host
+	 * exists to validate it against.
+	 */
+	for (i = 0; i < nvec; i++) {
+		int msi = i;
+
+		gic = mana_gd_get_gic(gc, false, &msi);
+		if (IS_ERR(gic)) {
+			err = PTR_ERR(gic);
+			goto free_irq;
+		}
+	}
+
+	gc->max_num_msix = nvec;
+	gc->num_msix_usable = nvec;
+
+	/* CDX sizes its MSI pool at probe time and cannot grow it later, so
+	 * never share vectors: vector 0 is reserved for the HWC and each of
+	 * the remaining nvec - 1 vectors backs exactly one EQ. That bounds
+	 * the queue count at nvec - 1 and guarantees the HWC vector is never
+	 * handed out to a data queue.
+	 */
+	gc->msi_sharing = false;
+
+	gc->msi_bitmap = bitmap_zalloc(nvec, GFP_KERNEL);
+	if (!gc->msi_bitmap) {
+		err = -ENOMEM;
+		goto free_irq;
+	}
+
+	/* Reserve the HWC vector */
+	set_bit(0, gc->msi_bitmap);
+
+	return 0;
+
+free_irq:
+	for (j = i - 1; j >= 0; j--)
+		mana_gd_put_gic(gc, false, j);
+
+	xa_destroy(&gc->irq_contexts);
+	msi_domain_free_irqs_all(gc->dev, MSI_DEFAULT_DOMAIN);
+	cdx_disable_msi(cdx_dev);
+	return err;
+}
+
+static void mana_cdx_remove_irqs(struct gdma_context *gc)
+{
+	struct cdx_device *cdx_dev = to_cdx_device(gc->dev);
+	int i;
+
+	if (gc->max_num_msix < 1)
+		return;
+
+	for (i = 0; i < gc->max_num_msix; i++) {
+		if (!xa_load(&gc->irq_contexts, i))
+			continue;
+
+		mana_gd_put_gic(gc, false, i);
+	}
+
+	WARN_ON(!xa_empty(&gc->irq_contexts));
+
+	xa_destroy(&gc->irq_contexts);
+	msi_domain_free_irqs_all(gc->dev, MSI_DEFAULT_DOMAIN);
+	cdx_disable_msi(cdx_dev);
+
+	bitmap_free(gc->msi_bitmap);
+	gc->msi_bitmap = NULL;
+	gc->max_num_msix = 0;
+	gc->num_msix_usable = 0;
+}
+
+static int mana_cdx_setup_remaining_irqs(struct gdma_context *gc)
+{
+	unsigned int max_queues_vport;
+
+	/* mana_gd_query_max_resources() may turn MSI sharing back on and round
+	 * the per-vPort queue count up to MANA_DEF_NUM_QUEUES. CDX sizes its
+	 * vector pool at probe time and cannot grow it later, and the HWC
+	 * vector must stay private, so give every vPort a private slice of the
+	 * remaining vectors instead. gc->max_num_queues is already capped at
+	 * num_msix_usable - 1 by the core.
+	 */
+	gc->msi_sharing = false;
+	max_queues_vport = (gc->num_msix_usable - 1) / gc->num_ports;
+	if (!max_queues_vport) {
+		dev_err(gc->dev, "%u MSI vectors cannot serve %u vPorts\n",
+			gc->num_msix_usable, gc->num_ports);
+		return -ENOSPC;
+	}
+
+	gc->max_num_queues_vport = min(gc->max_num_queues, max_queues_vport);
+
+	dev_dbg(gc->dev, "%u MSI vectors, %u vPorts, %u queues, no MSI sharing\n",
+		gc->num_msix_usable, gc->num_ports, gc->max_num_queues_vport);
+
+	return 0;
+}
+
+static const struct gdma_bus_ops mana_cdx_bus_ops = {
+	.bus_name		= "cdx",
+	.adev_prefix		= "mana_cdx",
+	.msix_virq		= mana_cdx_msix_virq,
+	.setup_hwc_irqs		= mana_cdx_setup_hwc_irqs,
+	.setup_remaining_irqs	= mana_cdx_setup_remaining_irqs,
+	.remove_irqs		= mana_cdx_remove_irqs,
+	.dev_reset		= mana_cdx_dev_reset,
+};
+
+static int mana_cdx_gd_probe(struct cdx_device *cdx_dev)
+{
+	struct gdma_context *gc;
+	void __iomem *bar0_va;
+	int err;
+
+	/* Each port has 2 CQs, each CQ has at most 1 EQE at a time */
+	BUILD_BUG_ON(2 * MAX_PORTS_IN_MANA_DEV * GDMA_EQE_SIZE > EQ_SIZE);
+
+	err = cdx_dev_reset(&cdx_dev->dev);
+	if (err)
+		return err;
+
+	err = cdx_set_master(cdx_dev);
+	if (err)
+		return err;
+
+	gc = vzalloc(sizeof(*gc));
+	if (!gc) {
+		err = -ENOMEM;
+		goto clear_master;
+	}
+
+	mutex_init(&gc->eq_test_event_mutex);
+	mutex_init(&gc->gic_mutex);
+	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);
+	if (gc->bar0_size < MANA_CDX_GDMA_REGS_SIZE) {
+		dev_err(&cdx_dev->dev, "BAR0 size %#llx is too small\n",
+			(u64)gc->bar0_size);
+		err = -EINVAL;
+		goto free_gc;
+	}
+
+	bar0_va = ioremap(gc->bar0_pa, gc->bar0_size);
+	if (!bar0_va) {
+		err = -ENOMEM;
+		goto free_gc;
+	}
+
+	gc->numa_node = dev_to_node(&cdx_dev->dev);
+	gc->bar0_va = bar0_va;
+	gc->dev = &cdx_dev->dev;
+	gc->bus_ops = &mana_cdx_bus_ops;
+
+	/* gc->is_pf is intentionally left clear: this device uses the VF
+	 * register layout, and the vPort and MAC filter registration that
+	 * is_pf selects is not required on this bus.
+	 */
+
+	err = mana_gd_setup(gc);
+	if (err)
+		goto unmap_bar;
+
+	err = mana_probe(&gc->mana, false);
+	if (err)
+		goto cleanup_gd;
+
+	err = mana_rdma_probe(&gc->mana_ib);
+	if (err)
+		goto remove_mana;
+
+	/* If a hardware reset event arrived over HWC during probe, roll back:
+	 * the device state the rest of probe observed is no longer valid.
+	 */
+	if (test_and_set_bit(GC_PROBE_SUCCEEDED, &gc->flags)) {
+		err = -EPROTO;
+		goto remove_rdma;
+	}
+
+	return 0;
+
+remove_rdma:
+	mana_rdma_remove(&gc->mana_ib);
+remove_mana:
+	mana_remove(&gc->mana, false);
+cleanup_gd:
+	mana_gd_cleanup(gc);
+unmap_bar:
+	iounmap(bar0_va);
+free_gc:
+	dev_set_drvdata(&cdx_dev->dev, NULL);
+	vfree(gc);
+clear_master:
+	cdx_clear_master(cdx_dev);
+	return err;
+}
+
+static int mana_cdx_gd_remove(struct cdx_device *cdx_dev)
+{
+	struct gdma_context *gc = dev_get_drvdata(&cdx_dev->dev);
+
+	mana_rdma_remove(&gc->mana_ib);
+	mana_remove(&gc->mana, false);
+
+	mana_gd_cleanup(gc);
+	cdx_clear_master(cdx_dev);
+
+	iounmap(gc->bar0_va);
+
+	dev_set_drvdata(&cdx_dev->dev, NULL);
+	vfree(gc);
+	return 0;
+}
+
+/* Quiesce the device for kexec. This is also called upon reboot/shutdown. */
+static void mana_cdx_gd_shutdown(struct cdx_device *cdx_dev)
+{
+	struct gdma_context *gc = dev_get_drvdata(&cdx_dev->dev);
+
+	mana_rdma_remove(&gc->mana_ib);
+	mana_remove(&gc->mana, true);
+
+	mana_gd_cleanup(gc);
+	cdx_clear_master(cdx_dev);
+}
+
+#define CDX_VENDOR_ID_MICROSOFT 0x1414
+static const struct cdx_device_id mana_cdx_table[] = {
+	{ CDX_DEVICE(CDX_VENDOR_ID_MICROSOFT, MANA_CDX_DEVICE_ID) },
+	{}
+};
+
+MODULE_DEVICE_TABLE(cdx, mana_cdx_table);
+
+static struct cdx_driver gdma_cdx_driver = {
+	.match_id_table	= mana_cdx_table,
+	.probe		= mana_cdx_gd_probe,
+	.remove		= mana_cdx_gd_remove,
+	.shutdown	= mana_cdx_gd_shutdown,
+	.driver	= {
+		.name	= "mana_cdx",
+	},
+};
+
+module_driver(gdma_cdx_driver, cdx_driver_register, cdx_driver_unregister);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Microsoft Azure Network Adapter driver for the CDX bus");
+MODULE_IMPORT_NS("CDX_BUS");
+MODULE_IMPORT_NS("NET_MANA");
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_pci.c b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
index 92a1e66e18b7..ed8925296c8a 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_pci.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_pci.c
@@ -619,6 +619,7 @@ static void mana_pci_remove_irqs(struct gdma_context *gc)
 
 static const struct gdma_bus_ops mana_pci_bus_ops = {
 	.bus_name		= "pci",
+	.adev_prefix		= "mana",
 	.msix_can_alloc_dyn	= mana_pci_msix_can_alloc_dyn,
 	.msix_virq		= mana_pci_msix_virq,
 	.msix_alloc_at		= mana_pci_msix_alloc_at,
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 16a9d192c099..2ba48a1c4b15 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3957,8 +3957,9 @@ static int add_adev(struct gdma_dev *gd, const char *name)
 
 	/* madev is owned by the auxiliary device */
 	madev = NULL;
-	/* Keep the established mana.{eth,rdma} auxiliary match names. */
-	ret = __auxiliary_device_add(adev, "mana");
+	/* Keep match names independent of the common module's name. */
+	ret = __auxiliary_device_add(adev,
+				     gd->gdma_context->bus_ops->adev_prefix);
 	if (ret)
 		goto add_fail;
 
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index cdec5558304f..c610fc1067e0 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -432,6 +432,7 @@ struct gdma_context;
  * instance in gdma_context.bus_ops at probe time.
  *
  * @bus_name: Bus token used to build the per-vector IRQ names.
+ * @adev_prefix: Prefix used for auxiliary-device match names.
  * @msix_can_alloc_dyn: True if the bus can allocate MSI-X vectors after
  *	probe time. Buses that size their vector pool at probe and cannot
  *	grow it later leave this NULL.
@@ -459,6 +460,7 @@ struct gdma_context;
  */
 struct gdma_bus_ops {
 	const char *bus_name;
+	const char *adev_prefix;
 	bool (*msix_can_alloc_dyn)(struct gdma_context *gc);
 	int (*msix_virq)(struct gdma_context *gc, int msi);
 	int (*msix_alloc_at)(struct gdma_context *gc, int *msi);
@@ -684,6 +686,8 @@ struct gdma_eqe {
 #define MANA_PF2_DEVICE_ID 0x00C1
 #define MANA_VF_DEVICE_ID 0x00BA
 
+#define MANA_CDX_DEVICE_ID 0x00C2
+
 struct gdma_posted_wqe_info {
 	u32 wqe_size_in_bu;
 };
-- 
2.54.0

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-24 17:31 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 17:30 [PATCH net-next 0/4] net: mana: Add support for the CDX bus Manish Awasthi
2026-09-24 17:30 ` [PATCH net-next 1/4] net: mana: Introduce gdma_bus_ops for bus-specific operations Manish Awasthi
2026-09-24 17:30 ` [PATCH net-next 2/4] net: mana: Move PCI transport code into gdma_pci.c Manish Awasthi
2026-09-24 17:30 ` [PATCH net-next 3/4] net: mana: Build the PCI transport as a separate module Manish Awasthi
2026-09-24 17:30 ` [PATCH net-next 4/4] net: mana: Add support for CDX device ID 0x00C2 Manish Awasthi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®