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)