git: 28bbe1d28d6f - main - e1000: Recover from I350 memory errors
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 16 Aug 2026 09:43:22 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=28bbe1d28d6f1e68e2ee3b34686ccb6114c42ca7
commit 28bbe1d28d6f1e68e2ee3b34686ccb6114c42ca7
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 23:34:56 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-16 09:41:25 +0000
e1000: Recover from I350 memory errors
I350 reports uncorrectable internal memory errors through ICR.FER and
identifies the affected region in PEIND. Depending on the region and
memory, hardware stops transmit, receive, or all PCIe and DMA traffic
until the port is reset and reinitialized.
Enable FER and all regional indication masks. Capture the read-clear
status in the interrupt filter.
Record the fatal PCIe, DMA, and LAN status registers, keep FER masked
while recovery is pending, and expose per-region indication counters.
Use the datasheet required port reset before master disable order for
PCIe parity errors. Reset for PCIe, DMA, and traffic-affecting LAN
errors. Statistics and VF-mailbox parity errors only require their
status to be discarded and cleared; management-memory recovery remains
under firmware control.
Validated on an I350 (8086:1521 revision 1). Three software-set FER
interrupts each advanced the unknown-region counter once, requested a
single reset, restored carrier and traffic, and left FER rearmed without
a watchdog. The software-set cause has no subordinate error status, so
region attribution and region-specific clearing remain datasheet-based.
MFC after: 2 weeks
Sponsored by: BBOX.io
---
sys/dev/e1000/e1000_defines.h | 15 ++--
sys/dev/e1000/e1000_regs.h | 2 +
sys/dev/e1000/if_em.c | 159 ++++++++++++++++++++++++++++++++----------
sys/dev/e1000/if_em.h | 2 +
4 files changed, 136 insertions(+), 42 deletions(-)
diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index fb2332462206..da06481be7de 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -528,24 +528,29 @@
#define E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT 8
#define E1000_PBECCSTS_ECC_ENABLE 0x00010000
-/* I210/I211 memory error status bits. */
+/* I350 and I210/I211 memory error status bits. */
#define E1000_PEIND_LANPORT_PARITY_FATAL 0x00000001
#define E1000_PEIND_MNG_PARITY_FATAL 0x00000002
#define E1000_PEIND_PCIE_PARITY_FATAL 0x00000004
#define E1000_PEIND_DMA_PARITY_FATAL 0x00000008
#define E1000_PEIND_FATAL_MASK 0x0000000F
-#define E1000_PEIND_HOST_FATAL_MASK (E1000_PEIND_LANPORT_PARITY_FATAL | \
- E1000_PEIND_PCIE_PARITY_FATAL | E1000_PEIND_DMA_PARITY_FATAL)
#define E1000_PBECCSTS_I210_ECC_ENABLE 0x00000001
#define E1000_PBECCSTS_I210_CORR_ERR 0x00000004
-#define E1000_PCIEERRSTS_FATAL_MASK 0x00000078
+#define E1000_PCIEERRSTS_I210_FATAL_MASK 0x00000078
+#define E1000_PCIEERRSTS_I350_FATAL_MASK 0x0000007C
#define E1000_PCIEECCSTS_TX_WR_DATA 0x00000010
#define E1000_PCIEECCSTS_RETRY_BUF 0x00000020
-#define E1000_PCIEECCSTS_CORR_MASK 0x00000030
+#define E1000_PCIEECCSTS_I210_CORR_MASK 0x00000030
+
+#define E1000_DTPARS_FATAL_MASK 0x00000020
+#define E1000_DRPARS_FATAL_MASK 0x00000002
#define E1000_LANPERRSTS_RETX_BUF 0x00000200
+#define E1000_LANPERRSTS_I350_NO_RESET_MASK 0x00008400
+#define E1000_LANPERRSTS_I350_RESET_MASK 0x00007BFE
+#define E1000_LANPERRSTS_I350_FATAL_MASK 0x0000FFFE
#define IFS_MAX 80
#define IFS_MIN 40
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index fe7b62a9a493..d2ac267f0be9 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -695,6 +695,8 @@
#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
/* Memory error status registers */
+#define E1000_DTPARS 0x03F10 /* DMA Tx Parity and ECC Status - RW1C */
+#define E1000_DRPARS 0x03F14 /* DMA Rx Parity and ECC Status - RW1C */
#define E1000_PCIEERRSTS 0x05BA8 /* PCIe Parity Status - RW1C */
#define E1000_PCIEECCSTS 0x05BAC /* PCIe ECC Status - RW1C */
#define E1000_LANPERRSTS 0x05F58 /* LAN Port Parity Status - RW1C */
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 1d9ac22bb667..263bdd48a871 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -2187,6 +2187,14 @@ em_has_i210_memory_errors(const struct e1000_hw *hw)
return (hw->mac.type == e1000_i210 ||
hw->mac.type == e1000_i211);
}
+
+static bool
+em_has_i350_memory_errors(const struct e1000_hw *hw)
+{
+
+ return (hw->mac.type == e1000_i350);
+}
+
static void
em_configure_peind_memory_errors(struct e1000_softc *sc)
{
@@ -2194,7 +2202,8 @@ em_configure_peind_memory_errors(struct e1000_softc *sc)
u32 peindm;
hw = &sc->hw;
- if (!em_has_i210_memory_errors(hw))
+ if (!em_has_i350_memory_errors(hw) &&
+ !em_has_i210_memory_errors(hw))
return;
/* Discard indications left by firmware before enabling reactions. */
@@ -2206,12 +2215,31 @@ em_configure_peind_memory_errors(struct e1000_softc *sc)
E1000_WRITE_FLUSH(hw);
}
+static bool
+em_has_i210_i350_memory_errors(const struct e1000_hw *hw)
+{
+
+ return (em_has_i350_memory_errors(hw) ||
+ em_has_i210_memory_errors(hw));
+}
+
+static u32
+em_pcie_fatal_error_mask(const struct e1000_hw *hw)
+{
+
+ if (em_has_i350_memory_errors(hw))
+ return (E1000_PCIEERRSTS_I350_FATAL_MASK);
+ if (em_has_i210_memory_errors(hw))
+ return (E1000_PCIEERRSTS_I210_FATAL_MASK);
+ return (0);
+}
+
static u32
em_fatal_error_intr_mask(struct e1000_softc *sc)
{
if ((em_has_pch_ecc(&sc->hw) ||
- em_has_i210_memory_errors(&sc->hw)) &&
+ em_has_i210_i350_memory_errors(&sc->hw)) &&
atomic_load_acq_32(&sc->fatal_error_state) ==
EM_FATAL_ERROR_NONE)
return (E1000_IMS_FER);
@@ -2246,7 +2274,7 @@ em_update_i210_ecc_stats(struct e1000_softc *sc)
}
pcieeccsts = E1000_READ_REG(hw, E1000_PCIEECCSTS) &
- E1000_PCIEECCSTS_CORR_MASK;
+ E1000_PCIEECCSTS_I210_CORR_MASK;
if (pcieeccsts & E1000_PCIEECCSTS_TX_WR_DATA)
sc->corrected_error_pcie_tx_data_count++;
if (pcieeccsts & E1000_PCIEECCSTS_RETRY_BUF)
@@ -2263,10 +2291,10 @@ static void
em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
{
struct e1000_hw *hw;
- u32 lanerr, pcieerr, peind;
+ u32 dma_rx, dma_tx, lanerr, pcieerr, peind;
if ((!em_has_pch_ecc(&sc->hw) &&
- !em_has_i210_memory_errors(&sc->hw)) ||
+ !em_has_i210_i350_memory_errors(&sc->hw)) ||
(icr & E1000_ICR_FER) == 0)
return;
@@ -2283,16 +2311,31 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
peind = E1000_READ_REG(hw, E1000_PEIND) &
E1000_PEIND_FATAL_MASK;
pcieerr = E1000_READ_REG(hw, E1000_PCIEERRSTS) &
- E1000_PCIEERRSTS_FATAL_MASK;
- lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
- E1000_LANPERRSTS_RETX_BUF;
+ em_pcie_fatal_error_mask(hw);
+ 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) &
+ E1000_DRPARS_FATAL_MASK;
+ lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
+ E1000_LANPERRSTS_I350_FATAL_MASK;
+ } else {
+ dma_tx = 0;
+ dma_rx = 0;
+ lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
+ E1000_LANPERRSTS_RETX_BUF;
+ }
if (pcieerr != 0)
peind |= E1000_PEIND_PCIE_PARITY_FATAL;
if (lanerr != 0)
peind |= E1000_PEIND_LANPORT_PARITY_FATAL;
+ if (dma_tx != 0 || dma_rx != 0)
+ peind |= E1000_PEIND_DMA_PARITY_FATAL;
sc->fatal_error_peind = peind;
sc->fatal_error_pcie = pcieerr;
sc->fatal_error_lan = lanerr;
+ sc->fatal_error_dma_tx = dma_tx;
+ sc->fatal_error_dma_rx = dma_rx;
}
atomic_store_rel_32(&sc->fatal_error_state,
EM_FATAL_ERROR_DETECTED);
@@ -2303,6 +2346,7 @@ static bool
em_handle_fatal_error_admin(struct e1000_softc *sc)
{
u32 peind;
+ bool reset_required;
if (!atomic_cmpset_acq_32(&sc->fatal_error_state,
EM_FATAL_ERROR_DETECTED, EM_FATAL_ERROR_RESET_REQUESTED))
@@ -2329,14 +2373,35 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
sc->fatal_error_unknown_count++;
device_printf(sc->dev,
"fatal internal memory error: PEIND %#x, "
- "PCIEERRSTS %#x, LANPERRSTS %#x\n", peind,
- sc->fatal_error_pcie, sc->fatal_error_lan);
+ "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 |
+ E1000_PEIND_DMA_PARITY_FATAL)) != 0;
+ if (peind == 0)
+ reset_required = true;
+ if (peind & E1000_PEIND_LANPORT_PARITY_FATAL) {
+ if (!em_has_i350_memory_errors(&sc->hw) ||
+ sc->fatal_error_lan == 0 ||
+ (sc->fatal_error_lan &
+ E1000_LANPERRSTS_I350_RESET_MASK) != 0)
+ reset_required = true;
+ }
/* Management-memory recovery belongs to management firmware. */
- if (peind != 0 &&
- (peind & E1000_PEIND_HOST_FATAL_MASK) == 0) {
+ if (!reset_required) {
+ if (em_has_i350_memory_errors(&sc->hw) &&
+ sc->fatal_error_lan != 0)
+ E1000_WRITE_REG(&sc->hw, E1000_LANPERRSTS,
+ sc->fatal_error_lan &
+ E1000_LANPERRSTS_I350_NO_RESET_MASK);
sc->fatal_error_peind = 0;
sc->fatal_error_pcie = 0;
sc->fatal_error_lan = 0;
+ sc->fatal_error_dma_tx = 0;
+ sc->fatal_error_dma_rx = 0;
atomic_store_rel_32(&sc->fatal_error_state,
EM_FATAL_ERROR_NONE);
E1000_WRITE_REG(&sc->hw, E1000_IMS,
@@ -2355,9 +2420,9 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
}
/*
- * A PCIe-region parity failure stops PCIe and DMA traffic. I210 and I211
- * require a port reset before master disable in this case, unlike the normal
- * reset path, which disables the bus master first.
+ * 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.
*/
static void
em_prepare_fatal_error_reset(struct e1000_softc *sc)
@@ -2367,14 +2432,14 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
u32 ctrl, pcieerr;
int i;
- if (!em_has_i210_memory_errors(&sc->hw) ||
+ if (!em_has_i210_i350_memory_errors(&sc->hw) ||
atomic_load_acq_32(&sc->fatal_error_state) !=
EM_FATAL_ERROR_RESET_REQUESTED)
return;
pcieerr = sc->fatal_error_pcie |
(E1000_READ_REG(&sc->hw, E1000_PCIEERRSTS) &
- E1000_PCIEERRSTS_FATAL_MASK);
+ em_pcie_fatal_error_mask(&sc->hw));
if ((sc->fatal_error_peind & E1000_PEIND_PCIE_PARITY_FATAL) == 0 &&
pcieerr == 0)
return;
@@ -2401,7 +2466,7 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
"PCIe master disable failed during parity recovery: %d\n",
error);
pcieerr |= E1000_READ_REG(hw, E1000_PCIEERRSTS) &
- E1000_PCIEERRSTS_FATAL_MASK;
+ em_pcie_fatal_error_mask(hw);
if (pcieerr != 0)
E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
atomic_store_rel_32(&sc->fatal_error_state,
@@ -2412,7 +2477,7 @@ static void
em_finish_fatal_error_reset(struct e1000_softc *sc)
{
struct e1000_hw *hw;
- u32 lanerr, pcieerr;
+ u32 dma_rx, dma_tx, lanerr, pcieerr;
u32 state;
state = atomic_load_acq_32(&sc->fatal_error_state);
@@ -2421,15 +2486,31 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
return;
hw = &sc->hw;
- if (em_has_i210_memory_errors(hw)) {
+ if (em_has_i210_i350_memory_errors(hw)) {
pcieerr = sc->fatal_error_pcie |
(E1000_READ_REG(hw, E1000_PCIEERRSTS) &
- E1000_PCIEERRSTS_FATAL_MASK);
+ em_pcie_fatal_error_mask(hw));
if (pcieerr != 0)
E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
- lanerr = sc->fatal_error_lan |
- (E1000_READ_REG(hw, E1000_LANPERRSTS) &
- E1000_LANPERRSTS_RETX_BUF);
+ if (em_has_i350_memory_errors(hw)) {
+ dma_tx = sc->fatal_error_dma_tx |
+ (E1000_READ_REG(hw, E1000_DTPARS) &
+ E1000_DTPARS_FATAL_MASK);
+ if (dma_tx != 0)
+ E1000_WRITE_REG(hw, E1000_DTPARS, dma_tx);
+ dma_rx = sc->fatal_error_dma_rx |
+ (E1000_READ_REG(hw, E1000_DRPARS) &
+ E1000_DRPARS_FATAL_MASK);
+ if (dma_rx != 0)
+ E1000_WRITE_REG(hw, E1000_DRPARS, dma_rx);
+ lanerr = sc->fatal_error_lan |
+ (E1000_READ_REG(hw, E1000_LANPERRSTS) &
+ E1000_LANPERRSTS_I350_FATAL_MASK);
+ } else {
+ lanerr = sc->fatal_error_lan |
+ (E1000_READ_REG(hw, E1000_LANPERRSTS) &
+ E1000_LANPERRSTS_RETX_BUF);
+ }
if (lanerr != 0)
E1000_WRITE_REG(hw, E1000_LANPERRSTS, lanerr);
/*
@@ -2441,6 +2522,8 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
sc->fatal_error_peind = 0;
sc->fatal_error_pcie = 0;
sc->fatal_error_lan = 0;
+ sc->fatal_error_dma_tx = 0;
+ sc->fatal_error_dma_rx = 0;
}
sc->fatal_error_pbeccsts = 0;
atomic_store_rel_32(&sc->fatal_error_state, EM_FATAL_ERROR_NONE);
@@ -6398,7 +6481,7 @@ em_add_hw_stats(struct e1000_softc *sc)
CTLFLAG_RD, &stats->rlpic, "RX LPI event count");
}
if (em_has_pch_ecc(&sc->hw) ||
- em_has_i210_memory_errors(&sc->hw)) {
+ em_has_i210_i350_memory_errors(&sc->hw)) {
struct sysctl_oid *memerr_node;
struct sysctl_oid_list *memerr_list;
@@ -6440,18 +6523,20 @@ em_add_hw_stats(struct e1000_softc *sc)
"fatal_unknown", CTLFLAG_RD,
&sc->fatal_error_unknown_count,
"Fatal memory errors without a reported region");
- SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
- "corrected_dma", CTLFLAG_RD,
- &sc->corrected_error_dma_count,
- "Corrected DMA memory error indications");
- SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
- "corrected_pcie_tx_data", CTLFLAG_RD,
- &sc->corrected_error_pcie_tx_data_count,
- "Corrected PCIe transmit-data memory indications");
- SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
- "corrected_pcie_retry", CTLFLAG_RD,
- &sc->corrected_error_pcie_retry_count,
- "Corrected PCIe retry-buffer memory indications");
+ if (em_has_i210_memory_errors(&sc->hw)) {
+ SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+ "corrected_dma", CTLFLAG_RD,
+ &sc->corrected_error_dma_count,
+ "Corrected DMA memory error indications");
+ SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+ "corrected_pcie_tx_data", CTLFLAG_RD,
+ &sc->corrected_error_pcie_tx_data_count,
+ "Corrected PCIe transmit-data memory indications");
+ SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+ "corrected_pcie_retry", CTLFLAG_RD,
+ &sc->corrected_error_pcie_retry_count,
+ "Corrected PCIe retry-buffer memory indications");
+ }
}
}
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index 0dd413dafcc8..ccb23afb48a2 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -629,6 +629,8 @@ struct e1000_softc {
u32 fatal_error_peind;
u32 fatal_error_pcie;
u32 fatal_error_lan;
+ u32 fatal_error_dma_tx;
+ u32 fatal_error_dma_rx;
u64 fatal_error_reset_count;
u64 fatal_error_lan_count;
u64 fatal_error_mng_count;