mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
e1000: Separate DH89xxCC memory errors
DH89xxCC shares the 82580 MAC type but reserves several of its memory
error control and status registers. Accessing the full 82580 register
set breaks normal RX/TX operation on affected adapters.
Give DH89xxCC separate configuration, accounting, and recovery paths.
Use its transmit packet-buffer ECC counter and DMA/LAN parity status
with device-specific masks, avoiding the reserved receive-buffer ECC
and PCIe error registers. Retain the port-reset sequence for fatal
memory-error recovery without waiting for the absent STATUS.RST_DONE.
Tested by: chuck2k (https://github.com/opnsense/src/issues/329)
Fixes: 915c628c4f ("igb: Report 82580 memory ECC errors")
MFC after: 1 day
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D61138
This commit is contained in:
1 parent
ba855bc358
commit
0857b57787
2 files changed
+153
-17
No files matched your search
@@ -535,7 +535,7 @@
|
||||
#define E1000_ECC_82575_UNCORR_CNT_SHIFT 8
|
||||
#define E1000_ECC_82575_ENABLE 0x00010000
|
||||
|
||||
/* I350, I354, and I210/I211 memory error status bits. */
|
||||
/* 82580, DH89xxCC, I350, I354, 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
|
||||
@@ -555,6 +555,14 @@
|
||||
#define E1000_PBECCSTS_82580_ECC_ENABLE 0x00010000
|
||||
#define E1000_PBECCSTS_82580_CORR_CNT_MASK 0x000000FF
|
||||
|
||||
/* DH89xxCC implements only the transmit packet-buffer ECC counter. */
|
||||
#define E1000_TPBECCSTS_DH89XXCC_ECC_ENABLE 0x00010000
|
||||
#define E1000_TPBECCSTS_DH89XXCC_CORR_CNT_MASK 0x000000FF
|
||||
#define E1000_DTPARS_DH89XXCC_ERROR_MASK 0x0000007F
|
||||
#define E1000_DRPARS_DH89XXCC_ERROR_MASK 0x0000000F
|
||||
#define E1000_DDPARS_DH89XXCC_ERROR_MASK 0x0000000F
|
||||
#define E1000_LANPERRSTS_DH89XXCC_ERROR_MASK 0x00007FFF
|
||||
|
||||
/* 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
|
||||
|
||||
+144
-16
@@ -469,8 +469,10 @@ 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_configure_dh89xxcc_memory_errors(struct e1000_softc *);
|
||||
static void em_update_82580_ecc_stats(struct e1000_softc *, u32, u32,
|
||||
u32);
|
||||
static void em_update_dh89xxcc_ecc_stats(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);
|
||||
@@ -2096,6 +2098,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);
|
||||
em_configure_dh89xxcc_memory_errors(sc);
|
||||
if (sc->vf_ifp) {
|
||||
sc->vf_reset_pending = false;
|
||||
} else {
|
||||
@@ -2382,19 +2385,37 @@ em_configure_82576_memory_errors(struct e1000_softc *sc)
|
||||
E1000_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
static bool
|
||||
em_has_dh89xxcc_memory_errors(const struct e1000_hw *hw)
|
||||
{
|
||||
|
||||
if (hw->mac.type != e1000_82580)
|
||||
return (false);
|
||||
switch (hw->device_id) {
|
||||
case E1000_DEV_ID_DH89XXCC_SGMII:
|
||||
case E1000_DEV_ID_DH89XXCC_SERDES:
|
||||
case E1000_DEV_ID_DH89XXCC_BACKPLANE:
|
||||
case E1000_DEV_ID_DH89XXCC_SFP:
|
||||
return (true);
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
em_has_82580_memory_errors(const struct e1000_hw *hw)
|
||||
{
|
||||
|
||||
return (hw->mac.type == e1000_82580);
|
||||
return (hw->mac.type == e1000_82580 &&
|
||||
!em_has_dh89xxcc_memory_errors(hw));
|
||||
}
|
||||
|
||||
static void
|
||||
em_clear_82580_memory_error_status(struct e1000_hw *hw, u32 reg)
|
||||
em_clear_memory_error_status(struct e1000_hw *hw, u32 reg, u32 mask)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = E1000_READ_REG(hw, reg);
|
||||
status = E1000_READ_REG(hw, reg) & mask;
|
||||
if (status != 0)
|
||||
E1000_WRITE_REG(hw, reg, status);
|
||||
}
|
||||
@@ -2411,10 +2432,10 @@ em_configure_82580_memory_errors(struct e1000_softc *sc)
|
||||
|
||||
/* 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_memory_error_status(hw, E1000_DTPARS_82580, ~0U);
|
||||
em_clear_memory_error_status(hw, E1000_DRPARS_82580, ~0U);
|
||||
em_clear_memory_error_status(hw, E1000_DDPARS_82580, ~0U);
|
||||
em_clear_memory_error_status(hw, E1000_PCIEERRSTS, ~0U);
|
||||
(void)E1000_READ_REG(hw, E1000_LANPERRSTS);
|
||||
em_update_82580_ecc_stats(sc,
|
||||
E1000_READ_REG(hw, E1000_RPBECCSTS),
|
||||
@@ -2452,6 +2473,43 @@ em_configure_82580_memory_errors(struct e1000_softc *sc)
|
||||
E1000_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
static void
|
||||
em_clear_dh89xxcc_parity_status(struct e1000_hw *hw)
|
||||
{
|
||||
|
||||
em_clear_memory_error_status(hw, E1000_DTPARS_82580,
|
||||
E1000_DTPARS_DH89XXCC_ERROR_MASK);
|
||||
em_clear_memory_error_status(hw, E1000_DRPARS_82580,
|
||||
E1000_DRPARS_DH89XXCC_ERROR_MASK);
|
||||
em_clear_memory_error_status(hw, E1000_DDPARS_82580,
|
||||
E1000_DDPARS_DH89XXCC_ERROR_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
em_configure_dh89xxcc_memory_errors(struct e1000_softc *sc)
|
||||
{
|
||||
struct e1000_hw *hw;
|
||||
|
||||
hw = &sc->hw;
|
||||
if (!em_has_dh89xxcc_memory_errors(hw))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Intel 89xx datasheet, section 28.22: the DMA parity status layout
|
||||
* matches 82580, but its parity controls, receive-buffer ECC and PCIe
|
||||
* error registers are reserved. LANPERRCTL only controls management
|
||||
* flex filters whose memories are not initialized by this driver.
|
||||
*/
|
||||
em_clear_dh89xxcc_parity_status(hw);
|
||||
(void)E1000_READ_REG(hw, E1000_PEIND);
|
||||
em_update_dh89xxcc_ecc_stats(sc);
|
||||
E1000_WRITE_REG(hw, E1000_TPBECCSTS,
|
||||
E1000_TPBECCSTS_DH89XXCC_ECC_ENABLE);
|
||||
/* All other PEINDM bits are reserved; do not preserve read values. */
|
||||
E1000_WRITE_REG(hw, E1000_PEINDM, E1000_PEIND_FATAL_MASK);
|
||||
E1000_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
static bool
|
||||
em_has_i210_memory_errors(const struct e1000_hw *hw)
|
||||
{
|
||||
@@ -2493,6 +2551,7 @@ em_has_peind_memory_errors(const struct e1000_hw *hw)
|
||||
{
|
||||
|
||||
return (em_has_82580_memory_errors(hw) ||
|
||||
em_has_dh89xxcc_memory_errors(hw) ||
|
||||
em_has_i350_i354_memory_errors(hw) ||
|
||||
em_has_i210_memory_errors(hw));
|
||||
}
|
||||
@@ -2563,6 +2622,15 @@ em_update_82580_ecc_stats(struct e1000_softc *sc, u32 rpbeccsts,
|
||||
E1000_WRITE_REG(&sc->hw, E1000_PCIEECCSTS, status);
|
||||
}
|
||||
|
||||
static void
|
||||
em_update_dh89xxcc_ecc_stats(struct e1000_softc *sc)
|
||||
{
|
||||
|
||||
sc->corrected_error_packet_buffer_count +=
|
||||
E1000_READ_REG(&sc->hw, E1000_TPBECCSTS) &
|
||||
E1000_TPBECCSTS_DH89XXCC_CORR_CNT_MASK;
|
||||
}
|
||||
|
||||
static void
|
||||
em_update_82575_ecc_stats(struct e1000_softc *sc, u32 pbeccsts,
|
||||
u32 rdhests, u32 tdhests)
|
||||
@@ -2788,6 +2856,27 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
|
||||
E1000_READ_REG(hw, E1000_TDHESTS_82575);
|
||||
} else if (em_has_82576_memory_errors(hw)) {
|
||||
sc->fatal_error_peind = E1000_READ_REG(hw, E1000_PEIND);
|
||||
} else if (em_has_dh89xxcc_memory_errors(hw)) {
|
||||
/* PCIe and management errors have no subordinate status CSR. */
|
||||
peind = E1000_READ_REG(hw, E1000_PEIND) &
|
||||
E1000_PEIND_FATAL_MASK;
|
||||
dma_tx = E1000_READ_REG(hw, E1000_DTPARS_82580) &
|
||||
E1000_DTPARS_DH89XXCC_ERROR_MASK;
|
||||
dma_rx = E1000_READ_REG(hw, E1000_DRPARS_82580) &
|
||||
E1000_DRPARS_DH89XXCC_ERROR_MASK;
|
||||
dma_host = E1000_READ_REG(hw, E1000_DDPARS_82580) &
|
||||
E1000_DDPARS_DH89XXCC_ERROR_MASK;
|
||||
lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
|
||||
E1000_LANPERRSTS_DH89XXCC_ERROR_MASK;
|
||||
if (dma_tx != 0 || dma_rx != 0 || dma_host != 0)
|
||||
peind |= E1000_PEIND_DMA_PARITY_FATAL;
|
||||
if (lanerr != 0)
|
||||
peind |= E1000_PEIND_LANPORT_PARITY_FATAL;
|
||||
sc->fatal_error_peind = peind;
|
||||
sc->fatal_error_dma_tx = dma_tx;
|
||||
sc->fatal_error_dma_rx = dma_rx;
|
||||
sc->fatal_error_dma_host = dma_host;
|
||||
sc->fatal_error_lan = lanerr;
|
||||
} else {
|
||||
peind = E1000_READ_REG(hw, E1000_PEIND) &
|
||||
E1000_PEIND_FATAL_MASK;
|
||||
@@ -2906,7 +2995,9 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
|
||||
E1000_READ_REG(&sc->hw, E1000_RPBECCSTS),
|
||||
E1000_READ_REG(&sc->hw, E1000_TPBECCSTS),
|
||||
pcieecc);
|
||||
} else if (em_has_i350_i354_memory_errors(&sc->hw))
|
||||
} else if (em_has_dh89xxcc_memory_errors(&sc->hw))
|
||||
em_update_dh89xxcc_ecc_stats(sc);
|
||||
else if (em_has_i350_i354_memory_errors(&sc->hw))
|
||||
em_update_i350_i354_ecc_stats(sc);
|
||||
if (peind & E1000_PEIND_LANPORT_PARITY_FATAL)
|
||||
sc->fatal_error_lan_count++;
|
||||
@@ -2930,6 +3021,15 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
|
||||
sc->fatal_error_dma_rx,
|
||||
sc->fatal_error_dma_host,
|
||||
sc->fatal_error_lan);
|
||||
} else if (em_has_dh89xxcc_memory_errors(&sc->hw)) {
|
||||
device_printf(sc->dev,
|
||||
"fatal internal memory error: PEIND %#x, "
|
||||
"DTPARS %#x, DRPARS %#x, DDPARS %#x, "
|
||||
"LANPERRSTS %#x\n", peind,
|
||||
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, "
|
||||
@@ -3186,6 +3286,8 @@ igb_finish_device_reset(struct e1000_softc *sc, u32 icr)
|
||||
* 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.
|
||||
* DH89xxCC uses the same port-reset sequence without the 82580 PCIe status
|
||||
* registers. Like 82580, it has no STATUS.RST_DONE bit.
|
||||
*
|
||||
* Indications that relatch after admin accounting are discarded during
|
||||
* reset; sticky bits cannot distinguish them from the saved event.
|
||||
@@ -3203,11 +3305,13 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
|
||||
EM_FATAL_ERROR_RESET_REQUESTED)
|
||||
return;
|
||||
|
||||
pcieerr = sc->fatal_error_pcie |
|
||||
(E1000_READ_REG(&sc->hw, E1000_PCIEERRSTS) &
|
||||
em_pcie_fatal_error_mask(&sc->hw));
|
||||
pcieerr = sc->fatal_error_pcie;
|
||||
if (!em_has_dh89xxcc_memory_errors(&sc->hw))
|
||||
pcieerr |= E1000_READ_REG(&sc->hw, E1000_PCIEERRSTS) &
|
||||
em_pcie_fatal_error_mask(&sc->hw);
|
||||
pcieecc = sc->fatal_error_pcie_ecc;
|
||||
if (!em_has_82580_memory_errors(&sc->hw) &&
|
||||
!em_has_dh89xxcc_memory_errors(&sc->hw) &&
|
||||
(sc->fatal_error_peind & E1000_PEIND_PCIE_PARITY_FATAL) == 0 &&
|
||||
pcieerr == 0)
|
||||
return;
|
||||
@@ -3220,7 +3324,7 @@ 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) ||
|
||||
(hw->mac.type == e1000_82580 ||
|
||||
(E1000_READ_REG(hw, E1000_STATUS) &
|
||||
E1000_STATUS_RST_DONE) != 0))
|
||||
break;
|
||||
@@ -3234,10 +3338,12 @@ em_prepare_fatal_error_reset(struct e1000_softc *sc)
|
||||
device_printf(sc->dev,
|
||||
"PCIe master disable failed during parity recovery: %d\n",
|
||||
error);
|
||||
pcieerr |= E1000_READ_REG(hw, E1000_PCIEERRSTS) &
|
||||
em_pcie_fatal_error_mask(hw);
|
||||
if (pcieerr != 0)
|
||||
E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
|
||||
if (!em_has_dh89xxcc_memory_errors(hw)) {
|
||||
pcieerr |= E1000_READ_REG(hw, E1000_PCIEERRSTS) &
|
||||
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;
|
||||
@@ -3305,6 +3411,21 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
|
||||
sc->fatal_error_dma_tx = 0;
|
||||
sc->fatal_error_dma_rx = 0;
|
||||
sc->fatal_error_dma_host = 0;
|
||||
} else if (em_has_dh89xxcc_memory_errors(hw)) {
|
||||
em_clear_dh89xxcc_parity_status(hw);
|
||||
/* LANPERRSTS is read-only and is cleared by the port reset. */
|
||||
lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
|
||||
E1000_LANPERRSTS_DH89XXCC_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_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) &
|
||||
@@ -7465,6 +7586,8 @@ em_update_stats_counters(struct e1000_softc *sc)
|
||||
E1000_READ_REG(&sc->hw, E1000_RPBECCSTS),
|
||||
E1000_READ_REG(&sc->hw, E1000_TPBECCSTS),
|
||||
E1000_READ_REG(&sc->hw, E1000_PCIEECCSTS));
|
||||
else if (em_has_dh89xxcc_memory_errors(&sc->hw))
|
||||
em_update_dh89xxcc_ecc_stats(sc);
|
||||
else if (em_has_i350_i354_memory_errors(&sc->hw))
|
||||
em_update_i350_i354_ecc_stats(sc);
|
||||
else if (em_has_i210_memory_errors(&sc->hw))
|
||||
@@ -8032,6 +8155,11 @@ em_add_hw_stats(struct e1000_softc *sc)
|
||||
"uncorrected_pcie", CTLFLAG_RD,
|
||||
&sc->uncorrected_error_pcie_count,
|
||||
"Uncorrected PCIe command-memory ECC indications");
|
||||
} else if (em_has_dh89xxcc_memory_errors(&sc->hw)) {
|
||||
SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
|
||||
"corrected_packet_buffer", CTLFLAG_RD,
|
||||
&sc->corrected_error_packet_buffer_count,
|
||||
"Corrected transmit packet-buffer ECC errors");
|
||||
} else if (em_has_i210_memory_errors(&sc->hw)) {
|
||||
SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
|
||||
"corrected_dma", CTLFLAG_RD,
|
||||
|
||||
Reference in new issue
Block a user