aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Bowling <kbowling@FreeBSD.org>2026-08-03 05:38:11 +0000
committerKevin Bowling <kbowling@FreeBSD.org>2026-08-03 10:18:42 +0000
commit176259efcb907bd73079e4da83344077271916a5 (patch)
tree7f00cc75479e31cfe0d7ae6001cf2e104467801b
parentac2be06e0c697f0c15c9a4fcb9503b2ddb14e135 (diff)
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.
-rw-r--r--sys/dev/e1000/if_em.c1
-rw-r--r--sys/dev/e1000/if_em.h1
-rw-r--r--sys/dev/e1000/if_igb_iov.c50
-rw-r--r--sys/dev/e1000/if_igb_iov.h2
4 files changed, 54 insertions, 0 deletions
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)