git: 12a27f82b370 - stable/15 - igc: Report corrected internal ECC errors

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 26 Aug 2026 00:57:10 UTC
The branch stable/15 has been updated by kbowling:

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

commit 12a27f82b370a049ac027203af9865ff63be9dbc
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 04:13:43 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-26 00:56:52 +0000

    igc: Report corrected internal ECC errors
    
    I225 and I226 do not interrupt for corrected internal ECC errors.
    Instead, the DMA packet buffer and PCIe memories expose sticky status
    bits in PBECCSTS and PCIEECCSTS.
    
    Sample these bits with the regular hardware statistics update, preserve
    the PBECCSTS ECC enable state while clearing its RW1C indication, and
    expose separate counters for the DMA packet buffer, PCIe transmit-data
    memory, and PCIe retry buffer.
    
    These counters represent observed indications rather than an exact error
    count because multiple corrections between samples collapse into one
    sticky status bit.
    
    Hardware validation used an I225-IT (rev 3) and a debug kernel that
    wrote only the documented self-clearing injection bits.  Each test
    armed the injector, exercised the owning RAM with traffic, and compared
    the corresponding counter before and after.
    
    Coverage:
        Memory                Observed result
        DMA packet buffer     corrected_dma advanced once
        PCIe transmit data    corrected_pcie_tx_data advanced once
        PCIe retry buffer     No PCIe replay source; not exercised
    
    The retry-buffer injector requires a real PCIe replay to read the
    corrupted entry.  The test root port exposed AER and DPC reporting but
    no protocol error injector, so ordinary traffic could not cover that
    case.
    
    Sponsored by:   BBOX.io
    
    (cherry picked from commit 9f7633b932954162792e2caef4f6f0cd87e82f43)
---
 sys/dev/igc/if_igc.c | 40 ++++++++++++++++++++++++++++++++++++++++
 sys/dev/igc/if_igc.h |  3 +++
 2 files changed, 43 insertions(+)

diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c
index 58b3596d86dd..75fbe5a67a2d 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -137,6 +137,7 @@ static void	igc_if_multi_set(if_ctx_t);
 static void	igc_if_update_admin_status(if_ctx_t);
 static void	igc_apply_i225_ipg_workaround(struct igc_softc *);
 static void	igc_if_debug(if_ctx_t);
+static void	igc_update_ecc_stats(struct igc_softc *);
 static void	igc_update_stats_counters(struct igc_softc *);
 static void	igc_add_hw_stats(struct igc_softc *);
 static int	igc_if_set_promisc(if_ctx_t, int);
@@ -2927,6 +2928,32 @@ pme:
  *  Update the board statistics counters.
  *
  **********************************************************************/
+static void
+igc_update_ecc_stats(struct igc_softc *sc)
+{
+	struct igc_hw *hw;
+	u32 pbeccsts, pcieeccsts;
+
+	hw = &sc->hw;
+	pbeccsts = IGC_READ_REG(hw, IGC_PBECCSTS);
+	if (pbeccsts & IGC_PBECCSTS_CORR_ERR) {
+		sc->corrected_error_dma_count++;
+		/* Preserve the enable bit while clearing the RW1C status. */
+		IGC_WRITE_REG(hw, IGC_PBECCSTS,
+		    pbeccsts & (IGC_PBECCSTS_ECC_ENABLE |
+		    IGC_PBECCSTS_CORR_ERR));
+	}
+
+	pcieeccsts = IGC_READ_REG(hw, IGC_PCIEECCSTS) &
+	    IGC_PCIEECCSTS_CORR_MASK;
+	if (pcieeccsts & IGC_PCIEECCSTS_TX_WR_DATA)
+		sc->corrected_error_pcie_tx_data_count++;
+	if (pcieeccsts & IGC_PCIEECCSTS_RETRY_BUF)
+		sc->corrected_error_pcie_retry_count++;
+	if (pcieeccsts != 0)
+		IGC_WRITE_REG(hw, IGC_PCIEECCSTS, pcieeccsts);
+}
+
 static void
 igc_update_stats_counters(struct igc_softc *sc)
 {
@@ -3008,6 +3035,8 @@ igc_update_stats_counters(struct igc_softc *sc)
 	sc->stats.tncrs += IGC_READ_REG(&sc->hw, IGC_TNCRS);
 	sc->stats.htdpmc += IGC_READ_REG(&sc->hw, IGC_HTDPMC);
 	sc->stats.tsctc += IGC_READ_REG(&sc->hw, IGC_TSCTC);
+
+	igc_update_ecc_stats(sc);
 }
 
 static uint64_t
@@ -3169,6 +3198,17 @@ igc_add_hw_stats(struct igc_softc *sc)
 	SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO, "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 error 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 error indications");
 	eee_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "eee",
 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
 	    "Energy Efficient Ethernet statistics");
diff --git a/sys/dev/igc/if_igc.h b/sys/dev/igc/if_igc.h
index 47a744d085de..c7ee70b6a15f 100644
--- a/sys/dev/igc/if_igc.h
+++ b/sys/dev/igc/if_igc.h
@@ -425,6 +425,9 @@ struct igc_softc {
 	uint64_t	fatal_error_pcie_count;
 	uint64_t	fatal_error_dma_count;
 	uint64_t	fatal_error_unknown_count;
+	uint64_t	corrected_error_dma_count;
+	uint64_t	corrected_error_pcie_tx_data_count;
+	uint64_t	corrected_error_pcie_retry_count;
 	struct igc_hw_stats stats;
 	u16		vf_ifp;
 };