git: c28f2c551daf - main - ixgbe: Defer ECC recovery to iflib

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 12 Aug 2026 03:53:29 UTC
The branch main has been updated by kbowling:

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

commit c28f2c551daf07345ac78b74459efe1014c49464
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 02:33:20 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-12 03:39:39 +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.
    
    MFC after:      2 weeks
---
 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 8610bfad0d4c..c12956fb6e07 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -3149,6 +3149,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)
  ************************************************************************/
@@ -3187,21 +3206,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) {
@@ -4829,6 +4844,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 */
@@ -4975,6 +4991,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. */
@@ -5120,6 +5141,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);
 
@@ -5302,6 +5325,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 85b8c3480edd..3f06d1ab8120 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 9222d8d54fd0..3194666bd81c 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_ */