git: 176259efcb90 - main - igb: drain stale MDD state before interrupt arm
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 03 Aug 2026 10:19:26 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=176259efcb907bd73079e4da83344077271916a5
commit 176259efcb907bd73079e4da83344077271916a5
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-03 05:38:11 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-03 10:18:42 +0000
igb: drain stale MDD state before interrupt arm
IOV policy setup can leave MDDET and its read-clear diagnostic
registers populated while the admin vector is masked. Carrying that
state across the unmask can suppress the next spoof-event edge.
Mark initialization for a one-shot drain and consume LVMMC, WVBR when
applicable, and ICR immediately before EIMS/IMS arms the vector.
Preserve the synthetic link-status cause across the arm-time ICR read,
and clear the one-shot latch at reset preparation.
---
sys/dev/e1000/if_em.c | 1 +
sys/dev/e1000/if_em.h | 1 +
sys/dev/e1000/if_igb_iov.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++
sys/dev/e1000/if_igb_iov.h | 2 ++
4 files changed, 54 insertions(+)
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 5be866657e71..fb12345a3f43 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -4877,6 +4877,7 @@ igb_if_intr_enable(if_ctx_t ctx)
E1000_WRITE_REG(hw, E1000_EIAC, reg | mask);
reg = E1000_READ_REG(hw, E1000_EIAM);
E1000_WRITE_REG(hw, E1000_EIAM, reg | mask);
+ igb_iov_intr_drain_stale(sc);
E1000_WRITE_REG(hw, E1000_EIMS, mask);
E1000_WRITE_REG(hw, E1000_IMS,
E1000_IMS_LSC | igb_iov_intr_mask(sc));
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index a25aaff3307d..4204dee4d982 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -611,6 +611,7 @@ struct e1000_softc {
u32 iov_pending;
u32 iov_spoof_pending;
u32 iov_blocked_pending;
+ u32 iov_intr_drain_pending;
u32 iov_teardown;
struct timeval iov_last_mdd_log;
u16 num_vfs;
diff --git a/sys/dev/e1000/if_igb_iov.c b/sys/dev/e1000/if_igb_iov.c
index 5544d1e3dd8c..7a7348ede657 100644
--- a/sys/dev/e1000/if_igb_iov.c
+++ b/sys/dev/e1000/if_igb_iov.c
@@ -534,6 +534,33 @@ igb_iov_intr_mask(const struct e1000_softc *sc)
return (E1000_IMS_VMMB | E1000_IMS_MDDET);
}
+void
+igb_iov_intr_drain_stale(struct e1000_softc *sc)
+{
+ struct e1000_hw *hw;
+ u32 icr;
+
+ if (atomic_readandclear_32(&sc->iov_intr_drain_pending) == 0)
+ return;
+ hw = &sc->hw;
+ /*
+ * Consume setup-time diagnostic state at the actual transition from
+ * masked to armed. Read ICR last so an event arriving after the drain
+ * remains pending and is delivered when the caller enables MDDET.
+ */
+ (void)E1000_READ_REG(hw, E1000_LVMMC);
+ if (hw->mac.type == e1000_82576)
+ (void)E1000_READ_REG(hw, E1000_WVBR);
+ icr = E1000_READ_REG(hw, E1000_ICR);
+ /*
+ * em_if_init() injects LSC after IOV setup to close the post-reset
+ * link race. Preserve that cause across this MDDET-specific drain.
+ */
+ if (__predict_true(icr != 0xffffffff) &&
+ (icr & E1000_ICR_LSC) != 0)
+ E1000_WRITE_REG(hw, E1000_ICS, E1000_ICS_LSC);
+}
+
static void
igb_iov_vfta_shadow_invalidate(struct e1000_softc *sc)
{
@@ -650,6 +677,7 @@ igb_iov_reset_prepare(struct e1000_softc *sc)
atomic_readandclear_32(&sc->iov_pending);
atomic_readandclear_32(&sc->iov_spoof_pending);
atomic_readandclear_32(&sc->iov_blocked_pending);
+ atomic_readandclear_32(&sc->iov_intr_drain_pending);
}
void
@@ -1889,6 +1917,27 @@ igb_iov_initialize(struct e1000_softc *sc)
E1000_WRITE_REG(hw, E1000_CTRL_EXT,
ctrl_ext | E1000_CTRL_EXT_PFRSTD);
E1000_WRITE_FLUSH(hw);
+ /*
+ * MDDET remains masked until iov_hw_active is published and iflib
+ * rearms the admin vector. Programming the per-pool policy above can
+ * leave a setup-time MDDET observation in the read-clear registers.
+ * If that stale cause is carried across the unmask, a later ordinary
+ * spoof can update LVMMC without generating a new interrupt edge.
+ *
+ * Drain only after all IOV policy is installed and before exposing the
+ * active state. Mailbox requests are also serviced by the periodic
+ * admin pass, and ping_all_vfs() below supplies a fresh notification.
+ */
+ (void)E1000_READ_REG(hw, E1000_LVMMC);
+ if (hw->mac.type == e1000_82576)
+ (void)E1000_READ_REG(hw, E1000_WVBR);
+ /* Read ICR last so a later event remains pending for the arm below. */
+ (void)E1000_READ_REG(hw, E1000_ICR);
+ atomic_readandclear_32(&sc->iov_mdd_cause);
+ atomic_readandclear_32(&sc->iov_pending);
+ atomic_readandclear_32(&sc->iov_spoof_pending);
+ atomic_readandclear_32(&sc->iov_blocked_pending);
+ atomic_store_rel_32(&sc->iov_intr_drain_pending, 1);
sc->iov_hw_active = true;
igb_iov_ping_all_vfs(sc);
}
@@ -2027,6 +2076,7 @@ igb_if_iov_uninit(if_ctx_t ctx)
atomic_readandclear_32(&sc->iov_pending);
atomic_readandclear_32(&sc->iov_spoof_pending);
atomic_readandclear_32(&sc->iov_blocked_pending);
+ atomic_readandclear_32(&sc->iov_intr_drain_pending);
atomic_store_rel_32(&sc->iov_teardown, 0);
}
diff --git a/sys/dev/e1000/if_igb_iov.h b/sys/dev/e1000/if_igb_iov.h
index f24b3199867e..8bd576d47649 100644
--- a/sys/dev/e1000/if_igb_iov.h
+++ b/sys/dev/e1000/if_igb_iov.h
@@ -31,6 +31,7 @@ void igb_iov_mdd_event(struct e1000_softc *);
void igb_iov_ping_all_vfs(struct e1000_softc *);
void igb_iov_reset_prepare(struct e1000_softc *);
u32 igb_iov_intr_mask(const struct e1000_softc *);
+void igb_iov_intr_drain_stale(struct e1000_softc *);
void igb_iov_rebuild_mta(struct e1000_softc *);
void igb_iov_rebuild_vlan(struct e1000_softc *);
void igb_iov_update_pf_vmolr(struct e1000_softc *);
@@ -48,6 +49,7 @@ void igb_iov_update_pf_vmolr(struct e1000_softc *);
#define igb_iov_ping_all_vfs(_sc)
#define igb_iov_reset_prepare(_sc)
#define igb_iov_intr_mask(_sc) (0)
+#define igb_iov_intr_drain_stale(_sc) ((void)(_sc))
#define igb_iov_rebuild_mta(_sc)
#define igb_iov_rebuild_vlan(_sc)
#define igb_iov_update_pf_vmolr(_sc)