Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 03 Aug 2026 10:18:34 +0000
Message-ID:  <6a706afa.40a62.6f74575e@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

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

commit ac2be06e0c697f0c15c9a4fcb9503b2ddb14e135
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-03 05:37:12 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-03 10:16:35 +0000

    igb: preserve coalesced 82576 MDD events
    
    WVBR is read-clear, so reading it from the deferred admin pass loses
    earlier queue bits when multiple VF malicious-driver events arrive
    before that pass.
    
    Snapshot WVBR in the interrupt filter, translate its staggered queue
    bitmap to pool bits, and OR observations into software latches for
    deferred notification and recovery.
    
    Retain the one-queue VMDq policy used for mixed-driver safety (the
    vswitch cannot handle a 2Q guest loopback to a 1Q guest per errata).
---
 sys/dev/e1000/if_em.c      |  1 +
 sys/dev/e1000/if_em.h      |  1 +
 sys/dev/e1000/if_igb_iov.c | 85 +++++++++++++++++++++++++++++++++++-----------
 3 files changed, 67 insertions(+), 20 deletions(-)

diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 6b6de5d74621..5be866657e71 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -1109,6 +1109,7 @@ em_set_num_queues(if_ctx_t ctx)
 		maxqueues = 2;
 		break;
 	case e1000_vfadapt:
+		/* Keep 82576 VFs at one RX/TX queue for mixed-driver safety. */
 	case e1000_vfadapt_i350:
 		maxqueues = 1;
 		break;
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index 4676cffc5033..a25aaff3307d 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -610,6 +610,7 @@ struct e1000_softc {
 	u32			iov_mdd_cause;
 	u32			iov_pending;
 	u32			iov_spoof_pending;
+	u32			iov_blocked_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 3e03d056865e..5544d1e3dd8c 100644
--- a/sys/dev/e1000/if_igb_iov.c
+++ b/sys/dev/e1000/if_igb_iov.c
@@ -111,6 +111,8 @@ SDT_PROBE_DEFINE3(igb_iov, vlan, rebuild, vfta_set,
     "struct e1000_softc *", "u_int", "uint32_t");
 SDT_PROBE_DEFINE3(igb_iov, vlan, rebuild, state,
     "struct e1000_softc *", "uint32_t *", "uint32_t *");
+SDT_PROBE_DEFINE4(igb_iov, mdd, sample, wvbr,
+    "struct e1000_softc *", "uint32_t", "uint32_t", "uint32_t");
 
 static const struct timeval igb_iov_nack_interval = { 2, 0 };
 static const struct timeval igb_iov_mbx_log_interval = { 2, 0 };
@@ -647,6 +649,7 @@ igb_iov_reset_prepare(struct e1000_softc *sc)
 	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);
 }
 
 void
@@ -1546,19 +1549,19 @@ void
 igb_iov_handle_mdd(struct e1000_softc *sc)
 {
 	struct igb_vf *vf;
-	u32 blocked, blocked_queues, cleared, handled, lvmmc, queue;
+	u32 blocked, cleared, handled, lvmmc;
 	u32 readback, spoofed;
-	u32 spoof_queues;
-	u32 wvbr;
 	bool mdfb_valid, pending;
 	int i;
 
 	pending = atomic_readandclear_32(&sc->iov_pending) != 0;
 	lvmmc = pending ?
 	    atomic_readandclear_32(&sc->iov_mdd_cause) : 0;
-	spoofed = atomic_readandclear_32(&sc->iov_spoof_pending);
-	if (!sc->iov_hw_active)
+	if (!sc->iov_hw_active) {
+		atomic_readandclear_32(&sc->iov_spoof_pending);
+		atomic_readandclear_32(&sc->iov_blocked_pending);
 		return;
+	}
 
 	blocked = 0;
 	handled = 0;
@@ -1566,6 +1569,7 @@ igb_iov_handle_mdd(struct e1000_softc *sc)
 	if (sc->hw.mac.type == e1000_i350) {
 		u32 mdfb;
 
+		spoofed = atomic_readandclear_32(&sc->iov_spoof_pending);
 		/*
 		 * I350 reports ordinary MAC/VLAN spoofing through the
 		 * interrupt-time LVMMC snapshot rather than WVBR.  The
@@ -1611,20 +1615,15 @@ igb_iov_handle_mdd(struct e1000_softc *sc)
 	} else {
 		if (!pending)
 			return;
-		wvbr = E1000_READ_REG(&sc->hw, E1000_WVBR);
-		if (__predict_false(wvbr == 0xffffffff))
-			wvbr = 0;
-		spoof_queues = wvbr & IGB_82576_QUEUE_MASK;
-		blocked_queues = (wvbr >> 16) & IGB_82576_QUEUE_MASK;
-		spoofed = (spoof_queues & 0xff) |
-		    (spoof_queues >> IGB_82576_STAGGERED_QUEUE_SHIFT);
-		blocked = (blocked_queues & 0xff) |
-		    (blocked_queues >> IGB_82576_STAGGERED_QUEUE_SHIFT);
-		if (blocked == 0 &&
-		    (lvmmc & IGB_82576_LVMMC_BLOCK_MASK) != 0) {
-			queue = (lvmmc >> 16) & 0xf;
-			blocked = 1U << (queue & 0x7);
-		}
+		/*
+		 * WVBR is read-clear and does not preserve every queue across
+		 * multiple MDDET interrupts.  The interrupt filter snapshots and
+		 * accumulates its pool bitmaps before this deferred admin pass.
+		 */
+		spoofed = atomic_readandclear_32(&sc->iov_spoof_pending);
+		blocked = atomic_readandclear_32(&sc->iov_blocked_pending);
+		/* A blocked-queue classification dominates its WVBR low bit. */
+		spoofed &= ~blocked;
 	}
 
 	for (i = 0; i < sc->num_vfs; i++) {
@@ -1713,7 +1712,7 @@ igb_iov_handle_mdd(struct e1000_softc *sc)
 void
 igb_iov_mdd_event(struct e1000_softc *sc)
 {
-	u32 cause, queue;
+	u32 blocked, cause, queues, queue, spoofed, wvbr;
 
 	/*
 	 * LVMMC is clear-on-read.  Preserve it in the interrupt filter, as
@@ -1722,6 +1721,49 @@ igb_iov_mdd_event(struct e1000_softc *sc)
 	cause = E1000_READ_REG(&sc->hw, E1000_LVMMC);
 	if (__predict_false(cause == 0xffffffff))
 		return;
+	if (sc->hw.mac.type == e1000_82576) {
+		/*
+		 * Snapshot WVBR in the interrupt filter.  Waiting for the admin
+		 * task loses all but the last of back-to-back VF MDD events on
+		 * 82576.  Convert the staggered queue map into pool bits and OR
+		 * each observation into software latches for deferred recovery.
+		 */
+		wvbr = E1000_READ_REG(&sc->hw, E1000_WVBR);
+		if (__predict_false(wvbr == 0xffffffff)) {
+			spoofed = 0;
+			blocked = 0;
+		} else {
+			queues = wvbr & IGB_82576_QUEUE_MASK;
+			spoofed = (queues & 0xff) |
+			    (queues >> IGB_82576_STAGGERED_QUEUE_SHIFT);
+			queues = (wvbr >> 16) & IGB_82576_QUEUE_MASK;
+			blocked = (queues & 0xff) |
+			    (queues >> IGB_82576_STAGGERED_QUEUE_SHIFT);
+		}
+		SDT_PROBE4(igb_iov, mdd, sample, wvbr, sc, wvbr, spoofed,
+		    blocked);
+		/*
+		 * 82576 can report a coalesced block-class event with all affected
+		 * queues in WVBR's low half and no high-half blocked bits.  If an
+		 * ordinary spoof shares that snapshot, the register has no per-queue
+		 * cause information.  Deliberately fail closed by recovering every
+		 * low-half queue; this can reset a spoof-only sibling, but avoids
+		 * stranding a blocked VF.  LVMMC.Last_Q identifies only the final
+		 * event and lost simultaneous blocked VFs on tested silicon.
+		 */
+		if (blocked == 0 &&
+		    (cause & IGB_82576_LVMMC_BLOCK_MASK) != 0) {
+			blocked = spoofed;
+			if (blocked == 0) {
+				queue = (cause >> 16) & 0xf;
+				blocked = 1U << (queue & 0x7);
+			}
+		}
+		if (spoofed != 0)
+			atomic_set_32(&sc->iov_spoof_pending, spoofed);
+		if (blocked != 0)
+			atomic_set_32(&sc->iov_blocked_pending, blocked);
+	}
 	if (sc->hw.mac.type == e1000_i350 &&
 	    (cause & IGB_I350_LVMMC_MAC_VLAN_SPOOF) != 0) {
 		queue = (cause >> IGB_I350_LVMMC_LAST_Q_SHIFT) &
@@ -1775,6 +1817,8 @@ igb_iov_initialize(struct e1000_softc *sc)
 	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);
+	/* Plain VMDq keeps every 82576 PF/VF pool on queue zero. */
 	E1000_WRITE_REG(hw, E1000_MRQC, E1000_MRQC_ENABLE_VMDQ);
 
 	vt_ctl = E1000_READ_REG(hw, E1000_VT_CTL);
@@ -1982,6 +2026,7 @@ igb_if_iov_uninit(if_ctx_t ctx)
 	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_teardown, 0);
 }
 


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a706afa.40a62.6f74575e>