git: 8367882d5313 - main - e1000: Report corrected I350 ECC errors

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sun, 16 Aug 2026 09:44:50 UTC
The branch main has been updated by kbowling:

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

commit 8367882d531313eea68966b07ddf917e39690b77
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 23:35:43 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-16 09:43:44 +0000

    e1000: Report corrected I350 ECC errors
    
    I350 does not interrupt for corrected internal ECC errors.  Instead,
    the PCIe, DMA, packet buffer, loopback, and management memories expose
    sticky status bits in their region-specific status registers.
    
    Sample those bits with the regular hardware statistics update, preserve
    the RX and TX packet buffer ECC enable state while clearing RW1C
    indications, and expose counters grouped by memory region.  Each counter
    records observed indication bits rather than exact error counts because
    repeated corrections between samples collapse into one sticky bit.
    
    On an I350 (8086:1521 revision 1), the ECC enables remained set.  All
    corrected-error status registers remained clear across boot, interface
    down/up, three FER recovery resets, and bidirectional line-rate traffic.
    The device has no documented corrected error injector.  Therefore, the
    per-region paths were validated against the register definitions rather
    than an injected SRAM error.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/e1000_defines.h |  8 +++++
 sys/dev/e1000/e1000_regs.h    |  3 ++
 sys/dev/e1000/if_em.c         | 79 +++++++++++++++++++++++++++++++++++++++++++
 sys/dev/e1000/if_em.h         |  1 +
 4 files changed, 91 insertions(+)

diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index da06481be7de..761ce02d06af 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -543,9 +543,17 @@
 #define E1000_PCIEECCSTS_TX_WR_DATA	0x00000010
 #define E1000_PCIEECCSTS_RETRY_BUF	0x00000020
 #define E1000_PCIEECCSTS_I210_CORR_MASK	0x00000030
+#define E1000_PCIEECCSTS_I350_OTHER_MASK	0x0000000F
+#define E1000_PCIEECCSTS_I350_CORR_MASK	0x0000003F
 
+#define E1000_DTPARS_CORR_MASK		0x0000005B
 #define E1000_DTPARS_FATAL_MASK		0x00000020
+#define E1000_DRPARS_CORR_MASK		0x0000000D
 #define E1000_DRPARS_FATAL_MASK		0x00000002
+#define E1000_DDECCS_CORR_MASK		0x0000000F
+
+#define E1000_PBECCSTS_I350_ENABLE_MASK	0x00030000
+#define E1000_PBECCSTS_I350_CORR_MASK	0x14000000
 
 #define E1000_LANPERRSTS_RETX_BUF		0x00000200
 #define E1000_LANPERRSTS_I350_NO_RESET_MASK	0x00008400
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index d2ac267f0be9..ec5df9ad774d 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -695,8 +695,11 @@
 #define E1000_PCIEMISC	0x05BB8 /* PCIE misc config register */
 
 /* 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_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_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 263bdd48a871..e29b34116b5d 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -2283,6 +2283,62 @@ em_update_i210_ecc_stats(struct e1000_softc *sc)
 		E1000_WRITE_REG(hw, E1000_PCIEECCSTS, pcieeccsts);
 }
 
+static void
+em_update_i350_ecc_stats(struct e1000_softc *sc)
+{
+	struct e1000_hw *hw;
+	u32 pbeccsts, status;
+
+	hw = &sc->hw;
+	status = E1000_READ_REG(hw, E1000_DTPARS) &
+	    E1000_DTPARS_CORR_MASK;
+	if (status != 0) {
+		sc->corrected_error_dma_count += bitcount32(status);
+		E1000_WRITE_REG(hw, E1000_DTPARS, status);
+	}
+	status = E1000_READ_REG(hw, E1000_DRPARS) &
+	    E1000_DRPARS_CORR_MASK;
+	if (status != 0) {
+		sc->corrected_error_dma_count += bitcount32(status);
+		E1000_WRITE_REG(hw, E1000_DRPARS, status);
+	}
+	status = E1000_READ_REG(hw, E1000_DDECCS) &
+	    E1000_DDECCS_CORR_MASK;
+	if (status != 0) {
+		sc->corrected_error_dma_count += bitcount32(status);
+		E1000_WRITE_REG(hw, E1000_DDECCS, status);
+	}
+
+	pbeccsts = E1000_READ_REG(hw, E1000_RPBECCSTS);
+	status = pbeccsts & E1000_PBECCSTS_I350_CORR_MASK;
+	if (status != 0) {
+		sc->corrected_error_packet_buffer_count += bitcount32(status);
+		/* Preserve the enable bits while clearing RW1C status. */
+		E1000_WRITE_REG(hw, E1000_RPBECCSTS,
+		    pbeccsts & (E1000_PBECCSTS_I350_ENABLE_MASK |
+		    E1000_PBECCSTS_I350_CORR_MASK));
+	}
+	pbeccsts = E1000_READ_REG(hw, E1000_TPBECCSTS);
+	status = pbeccsts & E1000_PBECCSTS_I350_CORR_MASK;
+	if (status != 0) {
+		sc->corrected_error_packet_buffer_count += bitcount32(status);
+		E1000_WRITE_REG(hw, E1000_TPBECCSTS,
+		    pbeccsts & (E1000_PBECCSTS_I350_ENABLE_MASK |
+		    E1000_PBECCSTS_I350_CORR_MASK));
+	}
+
+	status = E1000_READ_REG(hw, E1000_PCIEECCSTS) &
+	    E1000_PCIEECCSTS_I350_CORR_MASK;
+	if (status & E1000_PCIEECCSTS_TX_WR_DATA)
+		sc->corrected_error_pcie_tx_data_count++;
+	if (status & E1000_PCIEECCSTS_RETRY_BUF)
+		sc->corrected_error_pcie_retry_count++;
+	sc->corrected_error_pcie_other_count += bitcount32(status &
+	    E1000_PCIEECCSTS_I350_OTHER_MASK);
+	if (status != 0)
+		E1000_WRITE_REG(hw, E1000_PCIEECCSTS, status);
+}
+
 /*
  * Fatal internal-memory errors stop part or all of the MAC.  Capture the
  * read-clear indication before handing recovery to the iflib admin task.
@@ -6066,6 +6122,8 @@ em_update_stats_counters(struct e1000_softc *sc)
 	if (em_has_pch_ecc(&sc->hw))
 		em_update_pch_ecc_stats(sc,
 		    E1000_READ_REG(&sc->hw, E1000_PBECCSTS));
+	else if (em_has_i350_memory_errors(&sc->hw))
+		em_update_i350_ecc_stats(sc);
 	else if (em_has_i210_memory_errors(&sc->hw))
 		em_update_i210_ecc_stats(sc);
 }
@@ -6536,6 +6594,27 @@ em_add_hw_stats(struct e1000_softc *sc)
 				    "corrected_pcie_retry", CTLFLAG_RD,
 				    &sc->corrected_error_pcie_retry_count,
 				    "Corrected PCIe retry-buffer memory indications");
+			} else {
+				SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+				    "corrected_dma", CTLFLAG_RD,
+				    &sc->corrected_error_dma_count,
+				    "Corrected DMA memory indications");
+				SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+				    "corrected_packet_buffer", CTLFLAG_RD,
+				    &sc->corrected_error_packet_buffer_count,
+				    "Corrected packet-buffer memory 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");
+				SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+				    "corrected_pcie_other", CTLFLAG_RD,
+				    &sc->corrected_error_pcie_other_count,
+				    "Other corrected PCIe memory indications");
 			}
 		}
 	}
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index ccb23afb48a2..3273d4c6b7fe 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -640,6 +640,7 @@ struct e1000_softc {
 	u64			corrected_error_dma_count;
 	u64			corrected_error_pcie_tx_data_count;
 	u64			corrected_error_pcie_retry_count;
+	u64			corrected_error_pcie_other_count;
 	u64			corrected_error_packet_buffer_count;
 	u64			uncorrected_error_packet_buffer_count;