* [PATCH net v2] bnxt_en: Don't keep MSI-X vectors added before FW reserves them
@ 2026-10-28 11:49 Vlad Poenaru
0 siblings, 0 replies; only message in thread
From: Vlad Poenaru @ 2026-10-28 11:49 UTC (permalink / raw)
To: netdev
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Andy Gospodarek, Thomas Gleixner, Marc Zyngier, Jinqian Yang,
Andrew Preece, regressions, linux-kernel
When dynamic MSI-X is supported, bnxt_check_rings() grows the MSI-X
vectors with bnxt_change_msix() once FW says the new rings would fit,
and keeps them. FW only reserves the extra IRQs later, when
bnxt_reserve_rings() calls __bnxt_reserve_rings(). The MSI-X table
entries beyond the function's reserved IRQs are not backed until then:
the MSI message written when the new vectors are activated is lost, and
the entries read back as zero. After FW reserves them they are live but
still zero, so the NQ rings bound to them signal address 0 and every
completion on those rings is lost. The next ring change (for example
ethtool -G) then times out freeing each of those rings and the TX
watchdog fires:
bnxt_en 0002:01:00.0 eth0: Resp cmpl intr err msg: 0x51
bnxt_en 0002:01:00.0 eth0: hwrm_ring_free type 1 failed. rc:fffffff0 err:0
...
bnxt_en 0002:01:00.0 eth0: NETDEV WATCHDOG: CPU: 17: transmit queue 18 timed out 5716 ms
On x86 this is hidden: GENERIC_IRQ_RESERVATION_MODE makes the PCI MSI
domains MSI_FLAG_MUST_REACTIVATE, so the message is composed and written
again when the IRQ is requested, after FW has reserved the vector.
Since commit 0d6a7c417983 ("irqchip/gic-v3-its: Enable dynamic MSI-X
allocation"), arm64 GICv3 systems take the dynamic path as well, but
write the message only once, at allocation. On an NVIDIA Grace system
with a BCM57508, raising the channel count from 18 to 32 left MSI-X
entries 18-31 all zero, and the 14 rings using them lost their
interrupts.
Keep the allocation in bnxt_check_rings() as a check that enough vectors
are available, but free the new vectors again right away when FW manages
IRQ reservations (BNXT_NEW_RM). bnxt_reserve_rings() then allocates
them after __bnxt_reserve_rings() has reserved them. Without
BNXT_NEW_RM there is no IRQ reservation and bnxt_reserve_rings() does
not grow the vectors, so keep the current behaviour there.
In bnxt_reserve_rings(), size that allocation from the rings FW actually
reserved (bnxt_get_num_msix()) instead of the count computed before the
reservation, so that a partial grant does not activate unreserved
entries either, at least while no RoCE driver is registered.
The allocation can now fail after FW has committed the reservation. A
later open would then find nothing to reserve and return before growing
the vectors, and bnxt_request_irq() would use irq_tbl[] entries past
bp->total_irqs, which still hold the freed virqs. Grow the vectors in
that case as well, and clear irq_tbl[].vector when trimming.
Fixes: 2d51eb0bd81c ("bnxt_en: Add MSIX check in bnxt_check_rings()")
Reported-by: Andrew Preece <apreece@meta.com>
Closes: https://lore.kernel.org/all/20261022193620.2152038-1-vlad.wing@gmail.com/
Signed-off-by: Vlad Poenaru <vlad.wing@gmail.com>
---
Changes in v2:
- Size the bnxt_reserve_rings() allocation from what FW reserved
(bnxt_get_num_msix()), so a partial grant does not activate
unreserved entries while no RoCE driver is registered (Sashiko).
With RoCE registered, __bnxt_reserve_rings() can still keep more L2
rings than the grant covers; that accounting predates this patch.
- Grow the vectors in bnxt_reserve_rings() even when no reservation
is needed, and clear irq_tbl[].vector on trim, so a failed allocation
after the FW reservation no longer leaves stale virqs behind
(Sashiko).
- Only free the probed vectors when BNXT_NEW_RM is set; reword the
comment.
v1: https://lore.kernel.org/all/20261023121321.2127263-1-vlad.wing@gmail.com/
Tested on an NVIDIA Grace system (GICv3 ITS, ACPI) with a BCM57508,
FW 230.0.165.3: the boot-time channel change (18 -> 32) followed by a
ring resize no longer loses completions. With instrumentation, MSI-X
entries 18-31 read back as all zero after bnxt_check_rings() allocated
them, and read back correctly once bnxt_reserve_rings() allocated them
after the FW reservation.
This conflicts with the "bnxt_en: Ring accounting fixes" series on
net-next, which also changes the dynamic MSI-X path in
bnxt_reserve_rings().
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 34 ++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 0682060b048ce..55b11c7684313 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -11524,6 +11524,7 @@ static int bnxt_change_msix(struct bnxt *bp, int total)
map.index = i - 1;
map.virq = bp->irq_tbl[i - 1].vector;
pci_msix_free_irq(bp->pdev, map);
+ bp->irq_tbl[i - 1].vector = 0;
bp->total_irqs--;
}
return bp->total_irqs;
@@ -11701,8 +11702,23 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init)
int irqs_required;
int rc;
- if (!bnxt_need_reserve_rings(bp))
+ if (!bnxt_need_reserve_rings(bp)) {
+ /* FW may already hold the reservation from an earlier
+ * reservation that was not followed by growing the dynamic
+ * MSI-X vectors, e.g. because the allocation failed.
+ */
+ if (irq_re_init && BNXT_NEW_RM(bp) && bp->irq_tbl &&
+ pci_msix_can_alloc_dyn(bp->pdev)) {
+ irqs_required = bnxt_get_num_msix(bp);
+ if (irqs_required > bp->total_irqs &&
+ bnxt_change_msix(bp, irqs_required) != irqs_required) {
+ netdev_err(bp->dev, "Unable to allocate %d MSIX vectors, maximum available %d\n",
+ irqs_required, bp->total_irqs);
+ return -ENOSPC;
+ }
+ }
return 0;
+ }
if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(edev)) {
int ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want);
@@ -11728,6 +11744,10 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init)
rc = bnxt_init_int_mode(bp);
bnxt_ulp_irq_restart(bp, rc);
} else if (irq_change && !rc) {
+ /* Size the vectors from what FW actually reserved, which
+ * can be less than what was requested.
+ */
+ irqs_required = bnxt_get_num_msix(bp);
if (bnxt_change_msix(bp, irqs_required) != irqs_required)
rc = -ENOSPC;
}
@@ -15093,6 +15113,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
hwr.cp = min_t(int, hwr.cp, bnxt_get_max_func_irqs(bp));
}
if (hwr.cp > bp->total_irqs) {
+ int old_total = bp->total_irqs;
int total_msix = bnxt_change_msix(bp, hwr.cp);
if (total_msix < hwr.cp) {
@@ -15100,6 +15121,17 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
hwr.cp, total_msix);
rc = -ENOSPC;
}
+ /* FW has not reserved the new vectors yet, and writes to
+ * MSI-X table entries beyond the reserved IRQs don't
+ * stick (seen on BCM57508). Free them again;
+ * bnxt_reserve_rings() allocates them after
+ * __bnxt_reserve_rings(). Without the new resource
+ * manager there is no IRQ reservation and
+ * bnxt_reserve_rings() does not grow the vectors, so
+ * keep them.
+ */
+ if (BNXT_NEW_RM(bp))
+ bnxt_change_msix(bp, old_total);
}
}
return rc;
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-28 11:49 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 11:49 [PATCH net v2] bnxt_en: Don't keep MSI-X vectors added before FW reserves them Vlad Poenaru
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox