git: 16b31a184211 - stable/14 - ixgbe: Defer ECC recovery to iflib

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 26 Aug 2026 01:07:08 UTC
The branch stable/14 has been updated by kbowling:

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

commit 16b31a18421169addeef688be07538152e07032b
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 02:33:20 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-26 01:02:37 +0000

    ixgbe: Defer ECC recovery to iflib
    
    The link interrupt filter performed a full hardware reset in interrupt
    context.  This bypassed iflib stop and initialization, including queue
    quiescence and restoration of temporary LED state.
    
    Record the ECC event in the administrative request mask and ask iflib
    to perform the reset from its taskqueue.  Keep the ECC cause masked
    until reset so the intermediate admin pass cannot re-enable a sticky
    condition.  Handle ECC independently of Flow Director and in legacy
    interrupt mode.
    
    Remove the redundant EICR write; the filter has already cleared the
    reported causes.  Also remove the accompanying complement-mask update
    of mac.flags.  It set every flag except DOUBLE_RESET_REQUIRED and had
    no place in ECC recovery.
    
    (cherry picked from commit c28f2c551daf07345ac78b74459efe1014c49464)
---
 sys/dev/ixgbe/if_ix.c      | 54 ++++++++++++++++++++++++++++++++++------------
 sys/dev/ixgbe/ixgbe.h      |  1 +
 sys/dev/ixgbe/ixgbe_type.h |  1 +
 3 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index a1d64222a2e9..78fcabc3d68b 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -3140,6 +3140,25 @@ ixgbe_if_promisc_set(if_ctx_t ctx, int flags)
 	return (0);
 } /* ixgbe_if_promisc_set */
 
+/************************************************************************
+ * ixgbe_handle_ecc - Defer recovery from an ECC interrupt
+ ************************************************************************/
+static bool
+ixgbe_handle_ecc(struct ixgbe_softc *sc, u32 eicr)
+{
+	struct ixgbe_hw *hw = &sc->hw;
+
+	if ((eicr & IXGBE_EICR_ECC) == 0)
+		return (false);
+
+	IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_ECC);
+	if (!atomic_cmpset_int(&sc->ecc_reset_pending, 0, 1))
+		return (false);
+
+	device_printf(sc->dev, "Received ECC Err, initiating reset\n");
+	return (true);
+}
+
 /************************************************************************
  * ixgbe_msix_link - Link status change ISR (MSI/MSI-X)
  ************************************************************************/
@@ -3178,21 +3197,17 @@ ixgbe_msix_link(void *arg)
 		if ((sc->feat_en & IXGBE_FEATURE_FDIR) &&
 		    (eicr & IXGBE_EICR_FLOW_DIR)) {
 			/* This is probably overkill :) */
-			if (!atomic_cmpset_int(&sc->fdir_reinit, 0, 1))
-				return (FILTER_HANDLED);
-			/* Disable the interrupt */
-			IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EICR_FLOW_DIR);
-			atomic_set_32(&sc->task_requests, IXGBE_REQUEST_TASK_FDIR);
-		} else
-			if (eicr & IXGBE_EICR_ECC) {
-				device_printf(iflib_get_dev(sc->ctx),
-				    "Received ECC Err, initiating reset\n");
-				hw->mac.flags |=
-				    ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
-				ixgbe_reset_hw(hw);
-				IXGBE_WRITE_REG(hw, IXGBE_EICR,
-				    IXGBE_EICR_ECC);
+			if (atomic_cmpset_int(&sc->fdir_reinit, 0, 1)) {
+				/* Disable the interrupt */
+				IXGBE_WRITE_REG(hw, IXGBE_EIMC,
+				    IXGBE_EICR_FLOW_DIR);
+				atomic_set_32(&sc->task_requests,
+				    IXGBE_REQUEST_TASK_FDIR);
 			}
+		}
+		if (ixgbe_handle_ecc(sc, eicr))
+			atomic_set_32(&sc->task_requests,
+			    IXGBE_REQUEST_TASK_RESET);
 
 		/* Check for over temp condition */
 		if (sc->feat_en & IXGBE_FEATURE_TEMP_SENSOR) {
@@ -4820,6 +4835,7 @@ ixgbe_if_stop(if_ctx_t ctx)
 		ixgbe_quiesce_vfs(sc);
 	}
 	ixgbe_reset_hw(hw);
+	atomic_store_rel_int(&sc->ecc_reset_pending, 0);
 	hw->adapter_stopped = false;
 	ixgbe_stop_adapter(hw);
 	/* Turn off the laser - noop with no optics */
@@ -4966,6 +4982,11 @@ ixgbe_if_update_admin_status(if_ctx_t ctx)
 			ixgbe_handle_phy(ctx);
 		if (requests & IXGBE_REQUEST_TASK_LSC)
 			check_link = true;
+		if (requests & IXGBE_REQUEST_TASK_RESET) {
+			/* Re-enter the admin task so it observes IFC_DO_RESET. */
+			iflib_request_reset(ctx);
+			iflib_admin_intr_deferred(ctx);
+		}
 	}
 
 	/* Do not let a continuous producer monopolize the admin taskqueue. */
@@ -5111,6 +5132,8 @@ ixgbe_if_enable_intr(if_ctx_t ctx)
 	/* Enable Flow Director */
 	if (sc->feat_en & IXGBE_FEATURE_FDIR)
 		mask |= IXGBE_EIMS_FLOW_DIR;
+	if (atomic_load_acq_int(&sc->ecc_reset_pending))
+		mask &= ~IXGBE_EIMS_ECC;
 
 	IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask);
 
@@ -5293,6 +5316,9 @@ ixgbe_intr(void *arg)
 	    (eicr & IXGBE_EICR_GPI_SDP0_X540)) {
 		requests |= IXGBE_REQUEST_TASK_PHY;
 	}
+	if (hw->mac.type != ixgbe_mac_82598EB &&
+	    ixgbe_handle_ecc(sc, eicr))
+		requests |= IXGBE_REQUEST_TASK_RESET;
 	if (requests != 0) {
 		atomic_set_32(&sc->task_requests, requests);
 		iflib_admin_intr_deferred(ctx);
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 6dfba9fda736..d8f383dfc867 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -459,6 +459,7 @@ struct ixgbe_softc {
 
 	/* Flow Director */
 	int			fdir_reinit;
+	u_int			ecc_reset_pending;
 
 	u32			task_requests;
 
diff --git a/sys/dev/ixgbe/ixgbe_type.h b/sys/dev/ixgbe/ixgbe_type.h
index 85190dc59fdb..3ce32a3a9fe4 100644
--- a/sys/dev/ixgbe/ixgbe_type.h
+++ b/sys/dev/ixgbe/ixgbe_type.h
@@ -4613,5 +4613,6 @@ struct ixgbe_bypass_eeprom {
 #define IXGBE_REQUEST_TASK_PHY		0x10
 #define IXGBE_REQUEST_TASK_LSC		0x20
 #define IXGBE_REQUEST_TASK_FWEVENT	0x40
+#define IXGBE_REQUEST_TASK_RESET		0x80
 
 #endif /* _IXGBE_TYPE_H_ */