From: Manish Awasthi <mawasthi@linux.microsoft.com>
To: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, kotaranov@microsoft.com
Cc: horms@kernel.org, linux-hyperv@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-rdma@vger.kernel.org, bpf@vger.kernel.org,
nipun.gupta@amd.com, nikhil.agarwal@amd.com,
gargaditya@linux.microsoft.com, ernis@linux.microsoft.com,
kees@kernel.org, paulros@microsoft.com, mawasthi@microsoft.com
Subject: [PATCH net-next 2/4] net: mana: Move PCI transport code into gdma_pci.c
Date: Thu, 24 Sep 2026 17:30:52 +0000 [thread overview]
Message-ID: <20260924173054.589291-3-mawasthi@linux.microsoft.com> (raw)
In-Reply-To: <20260924173054.589291-1-mawasthi@linux.microsoft.com>
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
next prev parent reply other threads:[~2026-09-24 17:31 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260924173054.589291-3-mawasthi@linux.microsoft.com \
--to=mawasthi@linux.microsoft.com \
--cc=andrew+netdev@lunn.ch \
--cc=bpf@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=decui@microsoft.com \
--cc=edumazet@google.com \
--cc=ernis@linux.microsoft.com \
--cc=gargaditya@linux.microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=horms@kernel.org \
--cc=kees@kernel.org \
--cc=kotaranov@microsoft.com \
--cc=kuba@kernel.org \
--cc=kys@microsoft.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=mawasthi@microsoft.com \
--cc=netdev@vger.kernel.org \
--cc=nikhil.agarwal@amd.com \
--cc=nipun.gupta@amd.com \
--cc=pabeni@redhat.com \
--cc=paulros@microsoft.com \
--cc=wei.liu@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®