git: 7827d437dc88 - stable/15 - e1000: Recover from the 82574 PHY hang

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sun, 30 Aug 2026 01:37:57 UTC
The branch stable/15 has been updated by kbowling:

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

commit 7827d437dc88cc3bb341691fd0c04622175a371a
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-16 07:02:25 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-30 01:37:19 +0000

    e1000: Recover from the 82574 PHY hang
    
    The shared code provides e1000_check_phy_82574() to recognize a PHY
    hang from saturated receive error and idle error counters, but em(4)
    never calls it.
    
    Run the check from timer driven admin work.  Match Intel e1000e by
    requiring two consecutive positive samples before requesting a full
    iflib reset.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
    
    (cherry picked from commit 81d5356799a1db1701cb3f91146131c34dede413)
---
 sys/dev/e1000/if_em.c | 25 ++++++++++++++++++++++++-
 sys/dev/e1000/if_em.h |  1 +
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index b4ca34a0593a..93bab0e65ee6 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -3053,8 +3053,30 @@ em_if_update_admin_status(if_ctx_t ctx)
 	 * message.  Exported counters can consequently trail hardware by the
 	 * timer interval (normally 500 ms).
 	 */
-	if (atomic_readandclear_32(&sc->stats_pending) != 0)
+	if (atomic_readandclear_32(&sc->stats_pending) != 0) {
 		em_update_stats_counters(sc);
+		/*
+		 * The 82574 PHY can enter a state in which both its receive
+		 * error and idle error counters saturate.  Require two
+		 * consecutive timer samples before resetting, matching Intel's
+		 * e1000e recovery policy and avoiding a reset on a transient
+		 * register sample.
+		 */
+		if (hw->mac.type == e1000_82574) {
+			if (e1000_check_phy_82574(hw))
+				sc->phy_hang_count++;
+			else
+				sc->phy_hang_count = 0;
+			if (sc->phy_hang_count > 1) {
+				sc->phy_hang_count = 0;
+				device_printf(dev,
+				    "PHY appears hung; requesting reset\n");
+				iflib_request_reset(ctx);
+				iflib_admin_intr_deferred(ctx);
+				return;
+			}
+		}
+	}
 
 	/* Reset LAA into RAR[0] on 82571 */
 	if (hw->mac.type == e1000_82571 && e1000_get_laa_state_82571(hw))
@@ -4067,6 +4089,7 @@ em_reset(if_ctx_t ctx)
 	E1000_WRITE_REG(hw, E1000_VET, ETHERTYPE_VLAN);
 	e1000_get_phy_info(hw);
 	e1000_check_for_link(hw);
+	sc->phy_hang_count = 0;
 
 	return (E1000_SUCCESS);
 }
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index 5beb3e09fcad..4675317c2993 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -618,6 +618,7 @@ struct e1000_softc {
 	u32			pba;
 	int			link_mask;
 	int			tso_automasked;
+	u32			phy_hang_count;
 	u32			stats_pending;
 	u32			fatal_error_state;
 	u32			fatal_error_icr;