git: 8c8724705613 - main - igb: Sanitize retained VF queue state
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 30 Jul 2026 14:06:07 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=8c8724705613c856836c3928e7240baae03e690c
commit 8c8724705613c856836c3928e7240baae03e690c
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-30 07:40:55 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-07-30 14:06:00 +0000
igb: Sanitize retained VF queue state
82576 and I350 VFLR leave the VF queue configuration unchanged. A VF
can program transmit head write-back and leave its DMA destination for
a later VF owner; mainstream VF drivers do not overwrite TDWBAL/H.
Disable every receive and transmit queue assigned to the VF, wait for
the enable bits to clear, then clear SRRCTL, PSRTYPE, RXCTRL, TXCTRL,
and TDWBAL/H. Spin briefly for the normal transition, then sleep at
100 microsecond intervals with an approximately 1 ms bound. This
prevents a VF that keeps asserting QUEUE_ENABLE from busy-waiting the
PF context lock for 10 ms.
If a queue does not quiesce, leave the VF disabled and NACK its reset
rather than programming an active queue. Rate-limit this diagnostic
independently from mailbox and malicious-driver notifications.
I350 maps pool n to queue n. 82576 assigns physical queues n and n+8
to VF n, so sanitize both queues while clearing per-pool PSRTYPE once.
An incoming VF initializes its active ring base, head, and tail while
enabling each queue.
This is also required by malicious-driver recovery, which deliberately
does not assert VTCTRL.RST because doing so would discard the VF's
admin-vector routing before the PF can notify it.
This implements Software Clarification 3 from the 82576 and I350
specification updates.
Sponsored by: BBOX.io
---
sys/dev/e1000/if_igb_iov.c | 132 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 124 insertions(+), 8 deletions(-)
diff --git a/sys/dev/e1000/if_igb_iov.c b/sys/dev/e1000/if_igb_iov.c
index f43e994c0440..3e03d056865e 100644
--- a/sys/dev/e1000/if_igb_iov.c
+++ b/sys/dev/e1000/if_igb_iov.c
@@ -25,14 +25,24 @@
/* 82576 Datasheet rev. 2.0, Section 8.14.16: VMOLR[31] must be one. */
#define IGB_82576_VMOLR_RSV (1U << 31)
#define IGB_82576_LVMMC_BLOCK_MASK 0x1c
+#define IGB_82576_NUM_QUEUES 16
#define IGB_82576_QUEUE_MASK 0xffff
#define IGB_82576_STAGGERED_QUEUE_SHIFT 8
+#define IGB_82576_VF_QUEUE_STRIDE 8
+#define IGB_82576_VF_QUEUES 2
#define IGB_I350_DTXCTL_ENABLE_SPOOF_QUEUE (1U << 2)
#define IGB_I350_LVMMC_MAC_VLAN_SPOOF (1U << 25)
#define IGB_I350_LVMMC_LAST_Q_SHIFT 29
#define IGB_I350_LVMMC_LAST_Q_MASK 0x7
+#define IGB_I350_NUM_QUEUES 8
#define IGB_I350_QUEUE_MASK 0xff
#define IGB_I350_RESET_ACK_TIMEOUT (100 * SBT_1MS)
+#define IGB_I350_VF_QUEUES 1
+#define IGB_IOV_QUEUE_DISABLE_BUSY_RETRIES 10
+#define IGB_IOV_QUEUE_DISABLE_DELAY_US 10
+#define IGB_IOV_QUEUE_DISABLE_PAUSE (100 * SBT_1US)
+#define IGB_IOV_QUEUE_DISABLE_RETRIES 20
+#define IGB_IOV_VF_QUEUES_MAX 2
#define IGB_VF_CTS (1U << 0)
#define IGB_VF_CAP_MAC (1U << 1)
@@ -59,6 +69,7 @@ struct igb_vf {
struct timeval last_mbx_log;
struct timeval last_spoof_log;
struct timeval last_mdd_log;
+ struct timeval last_queue_log;
sbintime_t mbx_retry_at;
sbintime_t mdd_notify_at;
sbintime_t vlan_token_time;
@@ -1022,12 +1033,103 @@ igb_iov_vf_vlan_is_default(const struct igb_vf *vf)
return (true);
}
-static void
+static bool
+igb_iov_sanitize_vf_queues(struct e1000_softc *sc,
+ struct igb_vf *vf)
+{
+ struct e1000_hw *hw;
+ u16 qid[IGB_IOV_VF_QUEUES_MAX];
+ u32 rxdctl, txdctl;
+ int i, nqueues, retry;
+
+ hw = &sc->hw;
+ switch (hw->mac.type) {
+ case e1000_82576:
+ nqueues = IGB_82576_VF_QUEUES;
+ qid[0] = vf->pool;
+ qid[1] = vf->pool + IGB_82576_VF_QUEUE_STRIDE;
+ break;
+ case e1000_i350:
+ nqueues = IGB_I350_VF_QUEUES;
+ qid[0] = vf->pool;
+ break;
+ default:
+ return (true);
+ }
+
+ /*
+ * I350 maps pool n to queue n. 82576 gives VF n physical queues n
+ * and n + 8, so both retained queue configurations must be cleared.
+ */
+ for (i = 0; i < nqueues; i++)
+ KASSERT(qid[i] < (hw->mac.type == e1000_82576 ?
+ IGB_82576_NUM_QUEUES : IGB_I350_NUM_QUEUES),
+ ("%s: invalid VF queue %u", __func__, qid[i]));
+
+ /*
+ * The 82576 and I350 specification updates, Software Clarification 3,
+ * note that VFLR does not reset the VF queue configuration. Clear the
+ * PF-programmable state before acknowledging the reset so a new VF
+ * owner cannot inherit it, particularly a descriptor-head write-back
+ * DMA address. The new VF driver initializes its active ring pointers
+ * during queue setup.
+ *
+ * Disable every queue first, then wait for outstanding DMA activity to
+ * stop before clearing TDWBAL/H and the remaining retained state.
+ * Spin only for the normal fast transition, then sleep so a VF that
+ * keeps asserting QUEUE_ENABLE cannot busy-wait the PF for 10 ms.
+ */
+ for (i = 0; i < nqueues; i++) {
+ E1000_WRITE_REG(hw, E1000_RXDCTL(qid[i]), 0);
+ E1000_WRITE_REG(hw, E1000_TXDCTL(qid[i]), 0);
+ }
+ E1000_WRITE_FLUSH(hw);
+ for (retry = 0; retry < IGB_IOV_QUEUE_DISABLE_RETRIES; retry++) {
+ for (i = 0; i < nqueues; i++) {
+ rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(qid[i]));
+ txdctl = E1000_READ_REG(hw, E1000_TXDCTL(qid[i]));
+ if ((rxdctl & E1000_RXDCTL_QUEUE_ENABLE) != 0 ||
+ (txdctl & E1000_TXDCTL_QUEUE_ENABLE) != 0)
+ break;
+ }
+ if (i == nqueues)
+ break;
+ if (retry + 1 < IGB_IOV_QUEUE_DISABLE_RETRIES) {
+ if (retry < IGB_IOV_QUEUE_DISABLE_BUSY_RETRIES)
+ DELAY(IGB_IOV_QUEUE_DISABLE_DELAY_US);
+ else
+ pause_sbt("igbqds",
+ IGB_IOV_QUEUE_DISABLE_PAUSE, 0,
+ C_PREL(1));
+ }
+ }
+ if (retry == IGB_IOV_QUEUE_DISABLE_RETRIES) {
+ if (ratecheck(&vf->last_queue_log,
+ &igb_iov_mbx_log_interval))
+ device_printf(sc->dev,
+ "could not disable queues for VF %u; "
+ "reset deferred\n", vf->pool);
+ return (false);
+ }
+
+ for (i = 0; i < nqueues; i++) {
+ E1000_WRITE_REG(hw, E1000_SRRCTL(qid[i]), 0);
+ E1000_WRITE_REG(hw, E1000_DCA_RXCTRL(qid[i]), 0);
+ E1000_WRITE_REG(hw, E1000_TDWBAL(qid[i]), 0);
+ E1000_WRITE_REG(hw, E1000_TDWBAH(qid[i]), 0);
+ E1000_WRITE_REG(hw, E1000_DCA_TXCTRL(qid[i]), 0);
+ }
+ E1000_WRITE_REG(hw, E1000_PSRTYPE(vf->pool), 0);
+ E1000_WRITE_FLUSH(hw);
+ return (true);
+}
+
+static bool
igb_iov_reset_event_common(struct e1000_softc *sc, struct igb_vf *vf,
bool reset_intrs)
{
struct e1000_hw *hw;
- bool rebuild_mta, rebuild_vlan;
+ bool rebuild_mta, rebuild_vlan, sanitized;
u32 reg;
hw = &sc->hw;
@@ -1040,6 +1142,7 @@ igb_iov_reset_event_common(struct e1000_softc *sc, struct igb_vf *vf,
if (reset_intrs)
E1000_WRITE_REG(hw, E1000_VTCTRL(vf->pool),
E1000_VTCTRL_RST);
+ sanitized = igb_iov_sanitize_vf_queues(sc, vf);
E1000_WRITE_REG(hw, E1000_VMVIR(vf->pool), 0);
igb_iov_clear_mac_filters(sc, vf);
igb_iov_clear_rar(sc, vf->rar_index);
@@ -1048,12 +1151,13 @@ igb_iov_reset_event_common(struct e1000_softc *sc, struct igb_vf *vf,
igb_iov_rebuild_mta(sc);
if (rebuild_vlan)
igb_iov_rebuild_vlan(sc);
+ return (sanitized);
}
-static void
+static bool
igb_iov_reset_event(struct e1000_softc *sc, struct igb_vf *vf)
{
- igb_iov_reset_event_common(sc, vf, true);
+ return (igb_iov_reset_event_common(sc, vf, true));
}
static void
@@ -1070,8 +1174,11 @@ igb_iov_mdd_reset_event(struct e1000_softc *sc, struct igb_vf *vf)
* message to make the guest reinitialize. FreeBSD and DPDK consume
* that message directly; Linux ACKs it and the PF's non-CTS ACK path
* replies with the NACK that schedules igbvf's reset task.
+ *
+ * Sanitization failure leaves the pool disabled. The VF reset
+ * handshake retries it and is NACKed while a queue remains active.
*/
- igb_iov_reset_event_common(sc, vf, false);
+ (void)igb_iov_reset_event_common(sc, vf, false);
}
static void
@@ -1081,7 +1188,11 @@ igb_iov_reset_msg(struct e1000_softc *sc, struct igb_vf *vf)
u32 msg[3], reg;
hw = &sc->hw;
- igb_iov_reset_event(sc, vf);
+ if (!igb_iov_reset_event(sc, vf)) {
+ msg[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_NACK;
+ e1000_write_mbx(hw, msg, 1, vf->pool);
+ return;
+ }
igb_iov_map_rar(sc, vf->rar_index, vf->mac, vf->pool);
igb_iov_set_anti_spoof(sc, vf);
@@ -1369,8 +1480,13 @@ igb_iov_handle_mbx(struct e1000_softc *sc)
if (!(vf->flags & IGB_VF_ACTIVE))
continue;
now = getsbinuptime();
- if (e1000_check_for_rst(hw, vf->pool) == 0)
- igb_iov_reset_event(sc, vf);
+ if (e1000_check_for_rst(hw, vf->pool) == 0) {
+ /*
+ * The old VF is gone. A new owner's reset handshake
+ * reruns sanitization before enabling its pool.
+ */
+ (void)igb_iov_reset_event(sc, vf);
+ }
if ((vf->flags &
(IGB_VF_MBX_PENDING | IGB_VF_MBX_GAVE_UP)) == 0 &&
e1000_check_for_msg(hw, vf->pool) == 0) {