During error, bnxt_init_int_mode() will free bp->irq_tbl but bp->total_irqs retains the old value. If a subsequent reinitialization happens, bnxt_reserve_rings() may see that bp->total_irqs does not match a new irqs_required. It will then try to adjust if dynamic MSI-X is supported and call bnxt_change_msix(). It will then crash when dereferencing the NULL bp->irq_tbl. Fix it by clearing bp->total_irqs when freeing bp->irq_tbl. Dynamic MSI-X adjustments should only proceed if bp->irq_tbl is valid which means that MSI-X has been initialized and can be adjusted. Add a bp->irq_tbl_size to prevent OOB bp->irq_tbl[] array access in case MSI-X capabilities change during re-init. For dynamic MSI-X, allocate the biggest bp->irq_tbl that is not clamped by CP/NQ rings to allow dynamic MSI-X to grow to the max. In the AER path, if MSI-X capabilities change, bnxt_reserve_rings() will initialize MSI-X if BNXT_NEW_RM() is true. Add a check in bnxt_io_resume() to skip doing it again later. Fixes: e68256c8a73c ("bnxt_en: Support dynamic MSIX") Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 18 ++++++++++++++---- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d7728d0c5b6e..51557ee6c9ad 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11511,6 +11511,12 @@ static int bnxt_change_msix(struct bnxt *bp, int total) struct msi_map map; int i; + if (!bp->irq_tbl) + return 0; + + if (total > bp->irq_tbl_size) + return bp->total_irqs; + /* add MSIX to the end if needed */ for (i = bp->total_irqs; i < total; i++) { map = pci_msix_alloc_irq_at(bp->pdev, i, NULL); @@ -11653,12 +11659,13 @@ static int bnxt_init_int_mode(struct bnxt *bp) tbl_size = total_vecs; if (pci_msix_can_alloc_dyn(bp->pdev)) - tbl_size = max; + tbl_size = bp->hw_resc.max_irqs; bp->irq_tbl = kzalloc_objs(*bp->irq_tbl, tbl_size); if (!bp->irq_tbl) { rc = -ENOMEM; goto msix_setup_exit; } + bp->irq_tbl_size = tbl_size; for (i = 0; i < total_vecs; i++) bp->irq_tbl[i].vector = pci_irq_vector(bp->pdev, i); @@ -11681,6 +11688,8 @@ static int bnxt_init_int_mode(struct bnxt *bp) netdev_err(bp->dev, "bnxt_init_int_mode err: %x\n", rc); kfree(bp->irq_tbl); bp->irq_tbl = NULL; + bp->total_irqs = 0; + bp->irq_tbl_size = 0; pci_free_irq_vectors(bp->pdev); return rc; } @@ -11691,6 +11700,7 @@ static void bnxt_clear_int_mode(struct bnxt *bp) kfree(bp->irq_tbl); bp->irq_tbl = NULL; + bp->irq_tbl_size = 0; } int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) @@ -11717,7 +11727,7 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) if (irq_re_init && BNXT_NEW_RM(bp) && irqs_required != bp->total_irqs) { irq_change = true; - if (!pci_msix_can_alloc_dyn(bp->pdev)) { + if (!pci_msix_can_alloc_dyn(bp->pdev) || !bp->irq_tbl) { bnxt_ulp_irq_stop(bp); bnxt_clear_int_mode(bp); irq_cleared = true; @@ -15081,7 +15091,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, hwr.cp += bnxt_get_ulp_msix_num(bp); hwr.cp = min_t(int, hwr.cp, bnxt_get_max_func_irqs(bp)); } - if (hwr.cp > bp->total_irqs) { + if (bp->irq_tbl && hwr.cp > bp->total_irqs) { int total_msix = bnxt_change_msix(bp, hwr.cp); if (total_msix < hwr.cp) { @@ -17692,7 +17702,7 @@ static void bnxt_io_resume(struct pci_dev *pdev) err = bnxt_open(netdev); } else { err = bnxt_reserve_rings(bp, true); - if (!err) + if (!err && !bp->irq_tbl) err = bnxt_init_int_mode(bp); } } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index c673b2ce4a0d..a757d8258f71 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2490,6 +2490,7 @@ struct bnxt { */ unsigned long *ring_affinity_set; int max_irqs; + int irq_tbl_size; int total_irqs; int ulp_num_msix_want; u8 mac_addr[ETH_ALEN]; -- 2.51.0