net: mana: Introduce GIC context with refcounting for interrupt management

To allow Ethernet EQs to use dedicated or shared MSI-X vectors and RDMA
EQs to share the same MSI-X, introduce a GIC (GDMA IRQ Context) with
reference counting. This allows the driver to create an interrupt context
on an assigned or unassigned MSI-X vector and share it across multiple
EQ consumers.

Signed-off-by: Long Li <longli@microsoft.com>
Link: https://patch.msgid.link/20260605005717.2059954-4-longli@microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Long Li 2026-06-04 17:57:12 -07:00 committed by Jakub Kicinski
parent d7c253d614
commit d478457fc1
2 changed files with 181 additions and 0 deletions

View File

@ -871,6 +871,10 @@ static int mana_gd_register_irq(struct gdma_queue *queue,
}
queue->eq.msix_index = msi_index;
/* The caller acquired a GIC reference via mana_gd_get_gic().
* That refcount prevents mana_gd_put_gic() from erasing this
* irq_contexts entry concurrently.
*/
gic = xa_load(&gc->irq_contexts, msi_index);
if (WARN_ON(!gic))
return -EINVAL;
@ -898,6 +902,10 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)
if (WARN_ON(msix_index >= gc->num_msix_usable))
return;
/* The caller releases the GIC reference via mana_gd_put_gic()
* after this function returns. The refcount guarantees this
* irq_contexts entry is still valid.
*/
gic = xa_load(&gc->irq_contexts, msix_index);
if (WARN_ON(!gic))
return;
@ -1679,6 +1687,166 @@ static irqreturn_t mana_gd_intr(int irq, void *arg)
return IRQ_HANDLED;
}
void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi)
{
struct pci_dev *dev = to_pci_dev(gc->dev);
struct gdma_irq_context *gic;
struct msi_map irq_map;
int irq;
mutex_lock(&gc->gic_mutex);
gic = xa_load(&gc->irq_contexts, msi);
if (WARN_ON(!gic)) {
mutex_unlock(&gc->gic_mutex);
return;
}
if (use_msi_bitmap)
gic->bitmap_refs--;
if (use_msi_bitmap && gic->bitmap_refs == 0)
clear_bit(msi, gc->msi_bitmap);
if (!refcount_dec_and_test(&gic->refcount))
goto out;
irq = gic->irq;
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);
}
xa_erase(&gc->irq_contexts, msi);
kfree(gic);
out:
mutex_unlock(&gc->gic_mutex);
}
EXPORT_SYMBOL_NS(mana_gd_put_gic, "NET_MANA");
/*
* Get a GIC (GDMA IRQ Context) on a MSI vector
* a MSI can be shared between different EQs, this function supports setting
* up separate MSIs using a bitmap, or directly using the MSI index
*
* @use_msi_bitmap:
* True if MSI is assigned by this function on available slots from bitmap.
* False if MSI is passed from *msi_requested
*/
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);
struct gdma_irq_context *gic;
struct msi_map irq_map = { };
int irq;
int msi;
int err;
mutex_lock(&gc->gic_mutex);
if (use_msi_bitmap) {
msi = find_first_zero_bit(gc->msi_bitmap, gc->num_msix_usable);
if (msi >= gc->num_msix_usable) {
dev_err(gc->dev, "No free MSI vectors available\n");
gic = ERR_PTR(-ENOSPC);
goto out;
}
*msi_requested = msi;
} else {
msi = *msi_requested;
}
gic = xa_load(&gc->irq_contexts, msi);
if (gic) {
refcount_inc(&gic->refcount);
if (use_msi_bitmap) {
gic->bitmap_refs++;
set_bit(msi, gc->msi_bitmap);
}
goto out;
}
irq = pci_irq_vector(dev, 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);
goto out;
}
irq = irq_map.virq;
msi = irq_map.index;
*msi_requested = msi;
}
gic = kzalloc(sizeof(*gic), GFP_KERNEL);
if (!gic) {
gic = ERR_PTR(-ENOMEM);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
gic->handler = mana_gd_process_eq_events;
gic->msi = msi;
gic->irq = irq;
INIT_LIST_HEAD(&gic->eq_list);
spin_lock_init(&gic->lock);
if (!gic->msi)
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
pci_name(dev));
else
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@pci:%s",
gic->msi, pci_name(dev));
err = request_irq(irq, mana_gd_intr, 0, gic->name, gic);
if (err) {
dev_err(gc->dev, "Failed to request irq %d %s\n",
irq, gic->name);
kfree(gic);
gic = ERR_PTR(err);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
gic->dyn_msix = !!irq_map.virq;
refcount_set(&gic->refcount, 1);
gic->bitmap_refs = use_msi_bitmap ? 1 : 0;
err = xa_err(xa_store(&gc->irq_contexts, msi, gic, GFP_KERNEL));
if (err) {
dev_err(gc->dev, "Failed to store irq context for msi %d: %d\n",
msi, err);
free_irq(irq, gic);
kfree(gic);
gic = ERR_PTR(err);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
if (use_msi_bitmap)
set_bit(msi, gc->msi_bitmap);
out:
mutex_unlock(&gc->gic_mutex);
return gic;
}
EXPORT_SYMBOL_NS(mana_gd_get_gic, "NET_MANA");
int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r)
{
r->map = bitmap_zalloc(res_avail, GFP_KERNEL);
@ -2180,6 +2348,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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);

View File

@ -388,6 +388,11 @@ struct gdma_irq_context {
spinlock_t lock;
struct list_head eq_list;
char name[MANA_IRQ_NAME_SZ];
unsigned int msi;
unsigned int irq;
refcount_t refcount;
unsigned int bitmap_refs;
bool dyn_msix;
};
enum gdma_context_flags {
@ -450,6 +455,9 @@ struct gdma_context {
unsigned long flags;
/* Protect access to GIC context */
struct mutex gic_mutex;
/* Indicate if this device is sharing MSI for EQs on MANA */
bool msi_sharing;
@ -1027,6 +1035,10 @@ int mana_gd_resume(struct pci_dev *pdev);
bool mana_need_log(struct gdma_context *gc, int err);
struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
bool use_msi_bitmap,
int *msi_requested);
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);