mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
parent
d7c253d614
commit
d478457fc1
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user