git: 5e56a1fb09cd - main - e1000: Recover from 82580 memory errors

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a8a68ef.18824.29649956__7404.42615221895$1787455748$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by kbowling:

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

commit 5e56a1fb09cd3cc2081978dd6508ff5bc7c59eca
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-16 05:48:58 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-23 03:28:33 +0000

    e1000: Recover from 82580 memory errors
    
    82580 reports fatal parity and uncorrectable ECC errors through ICR.FER
    and its four region PEIND hierarchy.  Region specific status registers
    identify PCIe, DMA transmit, DMA receive, DMA host, and LAN port
    memories that can leave traffic stopped.
    
    Enable the documented DMA, PCIe, packet-buffer, and host-owned LAN
    parity and ECC checks only after initializing queue and filter tables.
    Leave the flexible filter parity controls under management firmware
    ownership.  Capture read-clear and RW1C status in the interrupt filter
    and keep FER masked until the admin task resolves the event.
    
    Reset for a host-owned region or an unknown FER source.  Leave
    management-only recovery to firmware.  Use CTRL.RST before master
    disable because fatal 82580 memory errors can stop PCIe traffic.  Do not
    use CTRL.DEV_RST: specification update item 9 declares that bit reserved
    and says it must always be written as zero.  Wait for EEPROM auto read
    completion; STATUS bit 21 is reserved on 82580, not PF_RST_DONE.
    
    Validated on an Intel I340-T2 (82580, revision 1).  A one queue
    port programmed LANPERRCTL as 0x6e00 while a four-queue port programmed
    0x7e00, avoiding the RSS checker until RETA is initialized.  Both ports
    programmed PEINDM 0xf, DTPARC 0x1555, DRPARC and DDPARC 0x55,
    PCIEERRCTL 0x5555, and PCIEECCCTL 0x11.  Three down/up cycles left all
    status and counters clear.
    
    A synthetic ICS.FER event on each function caused exactly one
    unknown source reset without advancing the sibling function counters.
    Controls were restored, the linked port recovered carrier, and
    bidirectional traffic after recovery.
    
    Enabling flexible filter parity checkers before programming their
    memories produced genuine LAN region faults with LANPERRSTS bits 0 and
    1.  Each fault advanced fatal_lan and fatal_resets exactly once, left
    the sibling function unchanged, and recovered the port proving hardware
    events will trigger the intended recovery.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/e1000_defines.h |  12 +++
 sys/dev/e1000/e1000_regs.h    |   9 ++
 sys/dev/e1000/if_em.c         | 205 +++++++++++++++++++++++++++++++++++++-----
 sys/dev/e1000/if_em.h         |   2 +
 4 files changed, 207 insertions(+), 21 deletions(-)

diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index 033eab0ae370..56c9418b2419 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -542,6 +542,18 @@
 #define E1000_PEIND_DMA_PARITY_FATAL	0x00000008
 #define E1000_PEIND_FATAL_MASK		0x0000000F
 
+/* 82580 parity and ECC detection controls. */
+#define E1000_DTPARC_82580_ENABLE_MASK		0x00001555
+#define E1000_DRPARC_82580_ENABLE_MASK		0x00000055
+#define E1000_DDPARC_82580_ENABLE_MASK		0x00000055
+#define E1000_PCIEERRCTL_82580_ENABLE_MASK	0x00005555
+#define E1000_PCIEECCCTL_82580_ENABLE_MASK	0x00000011
+#define E1000_LANPERRCTL_82580_HOST_MASK	0x00007E00
+#define E1000_LANPERRCTL_82580_RSS_ENABLE	0x00001000
+#define E1000_PCIEECCSTS_82580_ERROR_MASK	0x00000003
+#define E1000_LANPERRSTS_82580_ERROR_MASK	0x00007FFF
+#define E1000_PBECCSTS_82580_ECC_ENABLE		0x00010000
+
 /* 82576 uses PEIND directly rather than the later four-region layout. */
 #define E1000_PEIND_82576_NONFATAL_MASK	0x00000007
 #define E1000_PEIND_82576_FATAL_MASK	0x7FFFFF00
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index 3cfa1a05f297..a2c724247014 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -707,11 +707,20 @@
 /* Memory error status registers */
 #define E1000_RPBECCSTS	0x0245C /* Rx Packet Buffer ECC Status - RW */
 #define E1000_TPBECCSTS	0x0345C /* Tx Packet Buffer ECC Status - RW */
+#define E1000_DTPARC_82580 0x03500 /* 82580 DMA Tx Parity Control - RW */
+#define E1000_DRPARC_82580 0x03504 /* 82580 DMA Rx Parity Control - RW */
+#define E1000_DDPARC_82580 0x03508 /* 82580 DMA Host Parity Control - RW */
+#define E1000_DTPARS_82580 0x03510 /* 82580 DMA Tx Parity Status - RW1C */
+#define E1000_DRPARS_82580 0x03514 /* 82580 DMA Rx Parity Status - RW1C */
+#define E1000_DDPARS_82580 0x03518 /* 82580 DMA Host Parity Status - RW1C */
 #define E1000_DTPARS	0x03F10 /* DMA Tx Parity and ECC Status - RW1C */
 #define E1000_DRPARS	0x03F14 /* DMA Rx Parity and ECC Status - RW1C */
 #define E1000_DDECCS	0x03F18 /* DMA Host ECC Status - RW1C */
+#define E1000_PCIEERRCTL_82580 0x05BA0 /* 82580 PCIe Parity Control - RW */
+#define E1000_PCIEECCCTL_82580 0x05BA4 /* 82580 PCIe ECC Control - RW */
 #define E1000_PCIEERRSTS	0x05BA8 /* PCIe Parity Status - RW1C */
 #define E1000_PCIEECCSTS 0x05BAC /* PCIe ECC Status - RW1C */
+#define E1000_LANPERRCTL_82580 0x05F54 /* 82580 LAN Parity Control - RW */
 #define E1000_LANPERRSTS 0x05F58 /* LAN Port Parity Status - RW1C */
 
 #define E1000_PROXYS	0x5F64 /* Proxying Status */
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 7e312399b132..95f50c6694a6 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -461,6 +461,7 @@ static void	em_prepare_fatal_error_reset(struct e1000_softc *);
 static void	em_finish_fatal_error_reset(struct e1000_softc *);
 static void	em_configure_peind_memory_errors(struct e1000_softc *);
 static void	em_configure_82575_memory_errors(struct e1000_softc *);
+static void	em_configure_82580_memory_errors(struct e1000_softc *);
 static void	em_if_multi_set(if_ctx_t);
 static void	em_if_update_admin_status(if_ctx_t);
 static void	em_if_debug(if_ctx_t);
@@ -1998,6 +1999,7 @@ em_if_init(if_ctx_t ctx)
 	}
 	em_configure_peind_memory_errors(sc);
 	em_configure_82575_memory_errors(sc);
+	em_configure_82580_memory_errors(sc);
 	if (sc->vf_ifp)
 		sc->vf_reset_pending = false;
 }
@@ -2268,6 +2270,75 @@ em_configure_82576_memory_errors(struct e1000_softc *sc)
 	E1000_WRITE_FLUSH(hw);
 }
 
+static bool
+em_has_82580_memory_errors(const struct e1000_hw *hw)
+{
+
+	return (hw->mac.type == e1000_82580);
+}
+
+static void
+em_clear_82580_memory_error_status(struct e1000_hw *hw, u32 reg)
+{
+	u32 status;
+
+	status = E1000_READ_REG(hw, reg);
+	if (status != 0)
+		E1000_WRITE_REG(hw, reg, status);
+}
+
+static void
+em_configure_82580_memory_errors(struct e1000_softc *sc)
+{
+	struct e1000_hw *hw;
+	u32 reg;
+
+	hw = &sc->hw;
+	if (!em_has_82580_memory_errors(hw))
+		return;
+
+	/* Clear status left before the driver completed its memory tables. */
+	(void)E1000_READ_REG(hw, E1000_PEIND);
+	em_clear_82580_memory_error_status(hw, E1000_DTPARS_82580);
+	em_clear_82580_memory_error_status(hw, E1000_DRPARS_82580);
+	em_clear_82580_memory_error_status(hw, E1000_DDPARS_82580);
+	em_clear_82580_memory_error_status(hw, E1000_PCIEERRSTS);
+	em_clear_82580_memory_error_status(hw, E1000_PCIEECCSTS);
+	(void)E1000_READ_REG(hw, E1000_LANPERRSTS);
+	(void)E1000_READ_REG(hw, E1000_RPBECCSTS);
+	(void)E1000_READ_REG(hw, E1000_TPBECCSTS);
+	E1000_WRITE_REG(hw, E1000_RPBECCSTS,
+	    E1000_PBECCSTS_82580_ECC_ENABLE);
+	E1000_WRITE_REG(hw, E1000_TPBECCSTS,
+	    E1000_PBECCSTS_82580_ECC_ENABLE);
+
+	reg = E1000_READ_REG(hw, E1000_DTPARC_82580);
+	E1000_WRITE_REG(hw, E1000_DTPARC_82580,
+	    reg | E1000_DTPARC_82580_ENABLE_MASK);
+	reg = E1000_READ_REG(hw, E1000_DRPARC_82580);
+	E1000_WRITE_REG(hw, E1000_DRPARC_82580,
+	    reg | E1000_DRPARC_82580_ENABLE_MASK);
+	reg = E1000_READ_REG(hw, E1000_DDPARC_82580);
+	E1000_WRITE_REG(hw, E1000_DDPARC_82580,
+	    reg | E1000_DDPARC_82580_ENABLE_MASK);
+	reg = E1000_READ_REG(hw, E1000_PCIEERRCTL_82580);
+	E1000_WRITE_REG(hw, E1000_PCIEERRCTL_82580,
+	    reg | E1000_PCIEERRCTL_82580_ENABLE_MASK);
+	reg = E1000_READ_REG(hw, E1000_PCIEECCCTL_82580);
+	E1000_WRITE_REG(hw, E1000_PCIEECCCTL_82580,
+	    reg | E1000_PCIEECCCTL_82580_ENABLE_MASK);
+	reg = E1000_READ_REG(hw, E1000_LANPERRCTL_82580);
+	reg |= E1000_LANPERRCTL_82580_HOST_MASK;
+	/* The RSS memory is initialized only for a multiqueue layout. */
+	if (sc->rx_num_queues <= 1)
+		reg &= ~E1000_LANPERRCTL_82580_RSS_ENABLE;
+	E1000_WRITE_REG(hw, E1000_LANPERRCTL_82580, reg);
+	reg = E1000_READ_REG(hw, E1000_PEINDM);
+	E1000_WRITE_REG(hw, E1000_PEINDM,
+	    reg | E1000_PEIND_FATAL_MASK);
+	E1000_WRITE_FLUSH(hw);
+}
+
 static bool
 em_has_i210_memory_errors(const struct e1000_hw *hw)
 {
@@ -2304,10 +2375,11 @@ em_configure_peind_memory_errors(struct e1000_softc *sc)
 }
 
 static bool
-em_has_i210_i350_memory_errors(const struct e1000_hw *hw)
+em_has_peind_memory_errors(const struct e1000_hw *hw)
 {
 
-	return (em_has_i350_memory_errors(hw) ||
+	return (em_has_82580_memory_errors(hw) ||
+	    em_has_i350_memory_errors(hw) ||
 	    em_has_i210_memory_errors(hw));
 }
 
@@ -2315,6 +2387,8 @@ static u32
 em_pcie_fatal_error_mask(const struct e1000_hw *hw)
 {
 
+	if (em_has_82580_memory_errors(hw))
+		return (~0U);
 	if (em_has_i350_memory_errors(hw))
 		return (E1000_PCIEERRSTS_I350_FATAL_MASK);
 	if (em_has_i210_memory_errors(hw))
@@ -2330,7 +2404,7 @@ em_memory_error_intr_mask(const struct e1000_hw *hw)
 		return (E1000_IMS_82575_MEMORY_ERROR_MASK);
 	if (em_has_82576_memory_errors(hw))
 		return (E1000_IMS_FER | E1000_IMS_NFER);
-	if (em_has_pch_ecc(hw) || em_has_i210_i350_memory_errors(hw))
+	if (em_has_pch_ecc(hw) || em_has_peind_memory_errors(hw))
 		return (E1000_IMS_FER);
 	return (0);
 }
@@ -2551,7 +2625,8 @@ static void
 em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 {
 	struct e1000_hw *hw;
-	u32 dma_rx, dma_tx, error_mask, lanerr, pcieerr, peind;
+	u32 dma_host, dma_rx, dma_tx, error_mask, lanerr, pcieecc;
+	u32 pcieerr, peind;
 
 	error_mask = em_memory_error_intr_mask(&sc->hw);
 	if (!em_has_memory_errors(&sc->hw) ||
@@ -2582,7 +2657,25 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 		    E1000_PEIND_FATAL_MASK;
 		pcieerr = E1000_READ_REG(hw, E1000_PCIEERRSTS) &
 		    em_pcie_fatal_error_mask(hw);
-		if (em_has_i350_memory_errors(hw)) {
+		pcieecc = 0;
+		dma_host = 0;
+		if (em_has_82580_memory_errors(hw)) {
+			/*
+			 * PEIND is visible through every function.  Retain the
+			 * management indication, which has no subordinate status,
+			 * but attribute host-owned regions from this function's
+			 * status registers.
+			 */
+			peind &= E1000_PEIND_MNG_PARITY_FATAL;
+			pcieecc = E1000_READ_REG(hw, E1000_PCIEECCSTS) &
+			    E1000_PCIEECCSTS_82580_ERROR_MASK;
+			dma_tx = E1000_READ_REG(hw, E1000_DTPARS_82580);
+			dma_rx = E1000_READ_REG(hw, E1000_DRPARS_82580);
+			dma_host = E1000_READ_REG(hw,
+			    E1000_DDPARS_82580);
+			lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
+			    E1000_LANPERRSTS_82580_ERROR_MASK;
+		} else if (em_has_i350_memory_errors(hw)) {
 			dma_tx = E1000_READ_REG(hw, E1000_DTPARS) &
 			    E1000_DTPARS_FATAL_MASK;
 			dma_rx = E1000_READ_REG(hw, E1000_DRPARS) &
@@ -2595,17 +2688,19 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 			lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
 			    E1000_LANPERRSTS_RETX_BUF;
 		}
-		if (pcieerr != 0)
+		if (pcieerr != 0 || pcieecc != 0)
 			peind |= E1000_PEIND_PCIE_PARITY_FATAL;
 		if (lanerr != 0)
 			peind |= E1000_PEIND_LANPORT_PARITY_FATAL;
-		if (dma_tx != 0 || dma_rx != 0)
+		if (dma_tx != 0 || dma_rx != 0 || dma_host != 0)
 			peind |= E1000_PEIND_DMA_PARITY_FATAL;
 		sc->fatal_error_peind = peind;
 		sc->fatal_error_pcie = pcieerr;
+		sc->fatal_error_pcie_ecc = pcieecc;
 		sc->fatal_error_lan = lanerr;
 		sc->fatal_error_dma_tx = dma_tx;
 		sc->fatal_error_dma_rx = dma_rx;
+		sc->fatal_error_dma_host = dma_host;
 	}
 	atomic_store_rel_32(&sc->fatal_error_state,
 	    EM_FATAL_ERROR_DETECTED);
@@ -2676,12 +2771,28 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 			sc->fatal_error_dma_count++;
 		if (peind == 0)
 			sc->fatal_error_unknown_count++;
-		device_printf(sc->dev,
-		    "fatal internal memory error: PEIND %#x, "
-		    "PCIEERRSTS %#x, DTPARS %#x, DRPARS %#x, "
-		    "LANPERRSTS %#x\n", peind, sc->fatal_error_pcie,
-		    sc->fatal_error_dma_tx, sc->fatal_error_dma_rx,
-		    sc->fatal_error_lan);
+		if (em_has_82580_memory_errors(&sc->hw)) {
+			device_printf(sc->dev,
+			    "fatal internal memory error: PEIND %#x, "
+			    "PCIEERRSTS %#x, PCIEECCSTS %#x, "
+			    "DTPARS %#x, DRPARS %#x, DDPARS %#x, "
+			    "LANPERRSTS %#x\n", peind,
+			    sc->fatal_error_pcie,
+			    sc->fatal_error_pcie_ecc,
+			    sc->fatal_error_dma_tx,
+			    sc->fatal_error_dma_rx,
+			    sc->fatal_error_dma_host,
+			    sc->fatal_error_lan);
+		} else {
+			device_printf(sc->dev,
+			    "fatal internal memory error: PEIND %#x, "
+			    "PCIEERRSTS %#x, DTPARS %#x, DRPARS %#x, "
+			    "LANPERRSTS %#x\n", peind,
+			    sc->fatal_error_pcie,
+			    sc->fatal_error_dma_tx,
+			    sc->fatal_error_dma_rx,
+			    sc->fatal_error_lan);
+		}
 
 		reset_required = (peind &
 		    (E1000_PEIND_PCIE_PARITY_FATAL |
@@ -2704,9 +2815,11 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 				    E1000_LANPERRSTS_I350_NO_RESET_MASK);
 			sc->fatal_error_peind = 0;
 			sc->fatal_error_pcie = 0;
+			sc->fatal_error_pcie_ecc = 0;
 			sc->fatal_error_lan = 0;
 			sc->fatal_error_dma_tx = 0;
 			sc->fatal_error_dma_rx = 0;
+			sc->fatal_error_dma_host = 0;
 			atomic_store_rel_32(&sc->fatal_error_state,
 			    EM_FATAL_ERROR_NONE);
 			E1000_WRITE_REG(&sc->hw, E1000_IMS,
@@ -2726,18 +2839,20 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 
 /*
  * A PCIe-region parity failure stops PCIe and DMA traffic.  I350, I210, and
- * I211 require a port reset before master disable in this case, unlike the
- * normal reset path, which disables the bus master first.
+ * I211 require a port reset before master disable in this case.  82580 stops
+ * PCIe traffic for a fatal error in any host-owned region, so use the same
+ * order for every 82580 recovery.  This differs from the normal reset path,
+ * which disables the bus master first.
  */
 static void
 em_prepare_fatal_error_reset(struct e1000_softc *sc)
 {
 	struct e1000_hw *hw;
 	s32 error;
-	u32 ctrl, pcieerr;
+	u32 ctrl, pcieecc, pcieerr;
 	int i;
 
-	if (!em_has_i210_i350_memory_errors(&sc->hw) ||
+	if (!em_has_peind_memory_errors(&sc->hw) ||
 	    atomic_load_acq_32(&sc->fatal_error_state) !=
 	    EM_FATAL_ERROR_RESET_REQUESTED)
 		return;
@@ -2745,7 +2860,9 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
 	pcieerr = sc->fatal_error_pcie |
 	    (E1000_READ_REG(&sc->hw, E1000_PCIEERRSTS) &
 	    em_pcie_fatal_error_mask(&sc->hw));
-	if ((sc->fatal_error_peind & E1000_PEIND_PCIE_PARITY_FATAL) == 0 &&
+	pcieecc = sc->fatal_error_pcie_ecc;
+	if (!em_has_82580_memory_errors(&sc->hw) &&
+	    (sc->fatal_error_peind & E1000_PEIND_PCIE_PARITY_FATAL) == 0 &&
 	    pcieerr == 0)
 		return;
 
@@ -2757,8 +2874,9 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
 	for (i = 0; i < AUTO_READ_DONE_TIMEOUT; i++) {
 		if ((E1000_READ_REG(hw, E1000_EECD) &
 		    E1000_EECD_AUTO_RD) != 0 &&
+		    (em_has_82580_memory_errors(hw) ||
 		    (E1000_READ_REG(hw, E1000_STATUS) &
-		    E1000_STATUS_RST_DONE) != 0)
+		    E1000_STATUS_RST_DONE) != 0))
 			break;
 		msec_delay(1);
 	}
@@ -2774,6 +2892,12 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
 	    em_pcie_fatal_error_mask(hw);
 	if (pcieerr != 0)
 		E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
+	if (em_has_82580_memory_errors(hw)) {
+		pcieecc |= E1000_READ_REG(hw, E1000_PCIEECCSTS) &
+		    E1000_PCIEECCSTS_82580_ERROR_MASK;
+		if (pcieecc != 0)
+			E1000_WRITE_REG(hw, E1000_PCIEECCSTS, pcieecc);
+	}
 	atomic_store_rel_32(&sc->fatal_error_state,
 	    EM_FATAL_ERROR_RESET_PREPARED);
 }
@@ -2782,7 +2906,7 @@ static void
 em_finish_fatal_error_reset(struct e1000_softc *sc)
 {
 	struct e1000_hw *hw;
-	u32 dma_rx, dma_tx, lanerr, pcieerr;
+	u32 dma_host, dma_rx, dma_tx, lanerr, pcieecc, pcieerr;
 	u32 state;
 
 	state = atomic_load_acq_32(&sc->fatal_error_state);
@@ -2798,7 +2922,44 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
 		/* Drain any indication relatched while the port was resetting. */
 		(void)E1000_READ_REG(hw, E1000_PEIND);
 		sc->fatal_error_peind = 0;
-	} else if (em_has_i210_i350_memory_errors(hw)) {
+	} else if (em_has_82580_memory_errors(hw)) {
+		pcieerr = sc->fatal_error_pcie |
+		    E1000_READ_REG(hw, E1000_PCIEERRSTS);
+		if (pcieerr != 0)
+			E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
+		pcieecc = sc->fatal_error_pcie_ecc |
+		    (E1000_READ_REG(hw, E1000_PCIEECCSTS) &
+		    E1000_PCIEECCSTS_82580_ERROR_MASK);
+		if (pcieecc != 0)
+			E1000_WRITE_REG(hw, E1000_PCIEECCSTS, pcieecc);
+		dma_tx = sc->fatal_error_dma_tx |
+		    E1000_READ_REG(hw, E1000_DTPARS_82580);
+		if (dma_tx != 0)
+			E1000_WRITE_REG(hw, E1000_DTPARS_82580, dma_tx);
+		dma_rx = sc->fatal_error_dma_rx |
+		    E1000_READ_REG(hw, E1000_DRPARS_82580);
+		if (dma_rx != 0)
+			E1000_WRITE_REG(hw, E1000_DRPARS_82580, dma_rx);
+		dma_host = sc->fatal_error_dma_host |
+		    E1000_READ_REG(hw, E1000_DDPARS_82580);
+		if (dma_host != 0)
+			E1000_WRITE_REG(hw, E1000_DDPARS_82580, dma_host);
+		/* LANPERRSTS is read-only and is cleared by the port reset. */
+		lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
+		    E1000_LANPERRSTS_82580_ERROR_MASK;
+		if (lanerr != 0)
+			device_printf(sc->dev,
+			    "LAN parity status remained set after reset: %#x\n",
+			    lanerr);
+		(void)E1000_READ_REG(hw, E1000_PEIND);
+		sc->fatal_error_peind = 0;
+		sc->fatal_error_pcie = 0;
+		sc->fatal_error_pcie_ecc = 0;
+		sc->fatal_error_lan = 0;
+		sc->fatal_error_dma_tx = 0;
+		sc->fatal_error_dma_rx = 0;
+		sc->fatal_error_dma_host = 0;
+	} else if (em_has_peind_memory_errors(hw)) {
 		pcieerr = sc->fatal_error_pcie |
 		    (E1000_READ_REG(hw, E1000_PCIEERRSTS) &
 		    em_pcie_fatal_error_mask(hw));
@@ -2833,9 +2994,11 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
 		(void)E1000_READ_REG(hw, E1000_PEIND);
 		sc->fatal_error_peind = 0;
 		sc->fatal_error_pcie = 0;
+		sc->fatal_error_pcie_ecc = 0;
 		sc->fatal_error_lan = 0;
 		sc->fatal_error_dma_tx = 0;
 		sc->fatal_error_dma_rx = 0;
+		sc->fatal_error_dma_host = 0;
 	}
 	sc->fatal_error_icr = 0;
 	sc->fatal_error_pbeccsts = 0;
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index e5b0fec8b087..0901c1ce5780 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -630,9 +630,11 @@ struct e1000_softc {
 	u32			fatal_error_pbeccsts;
 	u32			fatal_error_peind;
 	u32			fatal_error_pcie;
+	u32			fatal_error_pcie_ecc;
 	u32			fatal_error_lan;
 	u32			fatal_error_dma_tx;
 	u32			fatal_error_dma_rx;
+	u32			fatal_error_dma_host;
 	u64			fatal_error_reset_count;
 	u64			fatal_error_lan_count;
 	u64			fatal_error_mng_count;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.