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)

Reply via email to