git: 176259efcb90 - main - igb: drain stale MDD state before interrupt arm

Kevin Bowling <[email protected]> Mon, 03 Aug 2026 10:19:26 +0000
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=176259efcb907bd73079e4da83344077271916a5

commit 176259efcb907bd73079e4da83344077271916a5
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-03 05:38:11 +0000
Commit:     Kevin Bowling <[email protected]>
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)