git: dbde45975144 - main - if_bnxt: fix HWRM failures/timeouts after repeated FW resets

From: Sumit Saxena <ssaxena_at_FreeBSD.org>
Date: Fri, 09 Oct 2026 10:12:53 UTC
The branch main has been updated by ssaxena:

URL: https://cgit.FreeBSD.org/src/commit/?id=dbde459751448ab9e98662660049513db783600c

commit dbde459751448ab9e98662660049513db783600c
Author:     Sumit Saxena <sumit.saxena@broadcom.com>
AuthorDate: 2026-07-31 16:13:44 +0000
Commit:     Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2026-10-09 08:24:45 +0000

    if_bnxt: fix HWRM failures/timeouts after repeated FW resets
    
    Consecutive firmware-initiated reset cycles produced HWRM
    failures/timeouts and traffic didn't come back. Fix the FW-reset
    recovery path:
    
    - bnxt_fw_reset_close() called bnxt_stop() and
      bnxt_hwrm_func_drv_unrgtr(), sending HWRM ring/VNIC/filter free
      commands to a firmware that's already mid-reset and unresponsive,
      producing the observed timeouts. Both are unnecessary since
      firmware comes back with fresh state anyway; drop them along with
      the now-redundant iflib_request_reset().
    - bnxt_func_reset() now skips bnxt_hwrm_resource_free() entirely
      while BNXT_STATE_IN_FW_RESET is set, for the same reason.
    - bnxt_open() (used to reopen after a firmware reset) now issues
      bnxt_hwrm_func_reset() up front and drives reinit through iflib's
      own reset machinery (iflib_request_reset() plus a new
      bnxt_iflib_reset_sync() that waits for
      IFF_DRV_RUNNING/OACTIVE to flip 1->0->1) instead of calling
      bnxt_init()/bnxt_intr_enable() directly.
    - Route repeated PORT_QSTATS/PORT_QSTATS_EXT HWRM timeouts through
      bnxt_log_live() instead of device_printf(), so a stats poll timing
      out during a reset doesn't spam dmesg on every tick.
    
    Reviewed by:    gallatin
    MFC after:      2 weeks
    Sponsored by:   Broadcom Inc.
    Differential Revision:  https://reviews.freebsd.org/D58617
---
 sys/dev/bnxt/bnxt_en/bnxt.h      |  1 +
 sys/dev/bnxt/bnxt_en/bnxt_hwrm.c | 15 ++++++---
 sys/dev/bnxt/bnxt_en/if_bnxt.c   | 67 ++++++++++++++++++++++++++++++++--------
 3 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h
index 763748974458..37914dae9a1d 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt.h
@@ -50,6 +50,7 @@
 #include "bnxt_dcb.h"
 #include "bnxt_auxbus_compat.h"
 #include "bnxt_sriov.h"
+#include "bnxt_log.h"
 
 #define DFLT_HWRM_CMD_TIMEOUT		500
 
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
index 19f2a8c80485..b9cd7174209c 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
@@ -256,10 +256,17 @@ _hwrm_send_message(struct bnxt_softc *softc, void *msg, uint32_t msg_len)
 		DELAY(1000);
 	}
 	if (i >= softc->hwrm_cmd_timeo) {
-		device_printf(softc->dev,
-		    "Timeout sending %s: (timeout: %u) seq: %d\n",
-		    GET_HWRM_REQ_TYPE(req->req_type), softc->hwrm_cmd_timeo,
-		    le16toh(req->seq_id));
+		if (req->req_type == HWRM_PORT_QSTATS ||
+		    req->req_type == HWRM_PORT_QSTATS_EXT)
+			bnxt_log_live(softc, BNXT_LOGGER_L2,
+				      "Timeout sending %s: (timeout: %u) seq: %d\n",
+				      GET_HWRM_REQ_TYPE(req->req_type), softc->hwrm_cmd_timeo,
+				      le16toh(req->seq_id));
+		else
+			device_printf(softc->dev,
+				      "Timeout sending %s: (timeout: %u) seq: %d\n",
+				      GET_HWRM_REQ_TYPE(req->req_type), softc->hwrm_cmd_timeo,
+				      le16toh(req->seq_id));
 		return ETIMEDOUT;
 	}
 	/* Last byte of resp contains the valid key */
diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c
index 2d996d80a0f5..b7dd72ffb177 100644
--- a/sys/dev/bnxt/bnxt_en/if_bnxt.c
+++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c
@@ -1978,10 +1978,6 @@ static void bnxt_fw_reset_close(struct bnxt_softc *bp)
 		bnxt_fw_fatal_close(bp);
 	}
 
-	iflib_request_reset(bp->ctx);
-	bnxt_stop(bp->ctx);
-	bnxt_hwrm_func_drv_unrgtr(bp, false);
-
 	for (i = bp->nrxqsets-1; i>=0; i--) {
 		if (BNXT_CHIP_P5_PLUS(bp))
 			iflib_irq_free(bp->ctx, &bp->nq_rings[i].irq);
@@ -2310,10 +2306,52 @@ static bool bnxt_fw_reset_timeout(struct bnxt_softc *bp)
 			  (bp->fw_reset_max_dsecs * HZ / 10));
 }
 
+/* enqueue an iflib reset and wait for RUNNING/OACTIVE to flip 1->0->1. */
+static int
+bnxt_iflib_reset_sync(struct bnxt_softc *bp, int timeout_us)
+{
+	unsigned flags;
+	bool reset_started;
+	int remaining_us = timeout_us;
+
+	/* request reset (async) and enqueue admin task */
+	iflib_request_reset(bp->ctx);
+	iflib_admin_intr_deferred(bp->ctx);
+
+	/* see if reset already started */
+	flags = if_getdrvflags(bp->ifp);
+	reset_started = ((flags & IFF_DRV_RUNNING) == 0) ||
+			 ((flags & IFF_DRV_OACTIVE) != 0);
+
+	while (remaining_us > 0) {
+		flags = if_getdrvflags(bp->ifp);
+
+		if (!reset_started) {
+			/* wait for reset to START (RUNNING=0 or OACTIVE=1) */
+			if ((flags & IFF_DRV_RUNNING) == 0 || (flags & IFF_DRV_OACTIVE) != 0)
+				reset_started = true;
+		} else {
+			/* wait for reset to FINISH (RUNNING=1 and OACTIVE=0) */
+			if ((flags & IFF_DRV_RUNNING) != 0 && (flags & IFF_DRV_OACTIVE) == 0)
+				return 0;  /* success */
+		}
+
+		pause_sbt("bnxtrst", SBT_1US * 20, 0, 0);  /* 20 us sleep */
+		remaining_us -= 20;
+	}
+	return ETIMEDOUT;
+}
+
 static int bnxt_open(struct bnxt_softc *bp)
 {
 	int rc = 0;
 
+	rc = bnxt_hwrm_func_reset(bp);
+	if (rc) {
+		device_printf(bp->dev, "reinit: hwrm func_reset failed\n");
+		return rc;
+	}
+
 	/* A function/chip reset drops the CAGR tick-resolution register
 	 * programmed in bnxt_attach_pre(); restore it before we start
 	 * relying on interrupt coalescing again. */
@@ -2380,10 +2418,6 @@ static int bnxt_open(struct bnxt_softc *bp)
 	}
 
 	bnxt_msix_intr_assign(bp->ctx, 0);
-	rc = bnxt_init_hw(bp->ctx);
-	if (rc != 0)
-		return (rc);
-	bnxt_intr_enable(bp->ctx);
 
 	if (test_and_clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state)) {
 		if (!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) {
@@ -2391,7 +2425,13 @@ static int bnxt_open(struct bnxt_softc *bp)
 		}
 	}
 
-	device_printf(bp->dev, "Network interface is UP and operational\n");
+	rc = bnxt_iflib_reset_sync(bp, 3000000);
+	if (rc) {
+		device_printf(bp->dev, "timeout waiting for reset/init\n");
+		return rc;
+	}
+
+	device_printf(bp->dev, "FW Reset Successfull, Interface is UP and Operational\n");
 
 	return rc;
 }
@@ -2488,9 +2528,8 @@ static void bnxt_fw_reset_task(struct work_struct *work)
 	case BNXT_FW_RESET_STATE_OPENING:
 		rc = bnxt_open(bp);
 		if (rc) {
-			device_printf(bp->dev, "bnxt_open() failed during FW reset\n");
+			device_printf(bp->dev, "Firmware reset failed (err=%d)\n", rc);
 			bnxt_fw_reset_abort(bp, rc);
-			rtnl_unlock();
 			return;
 		}
 
@@ -3452,7 +3491,8 @@ bnxt_func_reset(struct bnxt_softc *softc)
 		return;
 	}
 
-	bnxt_hwrm_resource_free(softc);
+	if (!test_bit(BNXT_STATE_IN_FW_RESET, &softc->state))
+		bnxt_hwrm_resource_free(softc);
 	return;
 }
 
@@ -5729,7 +5769,8 @@ bnxt_handle_async_event(struct bnxt_softc *softc, struct cmpl_base *cmpl)
 			softc->fw_health->survivals++;
 			set_bit(BNXT_STATE_FW_NON_FATAL_COND, &softc->state);
 		}
-		device_printf(softc->dev,
+		device_printf(softc->dev, "%s firmware reset event arrived.\n", type_str);
+		bnxt_log_live(softc, BNXT_LOGGER_L2,
 			   "%s firmware reset event, data1: 0x%x, data2: 0x%x, min wait %u ms, max wait %u ms\n",
 			   type_str, data1, data2,
 			   softc->fw_reset_min_dsecs * 100,