git: 3e8b8afbb6e9 - stable/14 - ixl: Track and recover MDD-blocked VFs

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Mon, 24 Aug 2026 09:14:18 UTC
The branch stable/14 has been updated by kbowling:

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

commit 3e8b8afbb6e99f1f5e0a9a299b15e639deb33329
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-10 03:46:24 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-24 09:12:22 +0000

    ixl: Track and recover MDD-blocked VFs
    
    The hardware identifies each VF with TX and RX malicious-driver
    status latches, but the driver combined all events into one counter
    and reported only the last VF found.
    
    Consume every PF and VF latch, keep per-direction VF counters, and
    rate-limit per-VF diagnostics.  Keep the software block until a
    successful VF or PF reset reconstructs its resources.
    
    Match Linux i40e policy by leaving a detected VF blocked by default.
    Add an opt-in hw.ixl.mdd_auto_reset_vf tunable that notifies and
    resets the VF for installations that prefer availability.  DPDK
    provides the register clear and per-VF attribution precedent; Linux
    provides the recovery policy.
    
    (cherry picked from commit 93f1065920d806400ace6b60b025faf91926bdaa)
---
 share/man/man4/ixl.4      |   6 +++
 sys/dev/ixl/if_ixl.c      |   8 ++-
 sys/dev/ixl/ixl_pf.h      |   9 +++-
 sys/dev/ixl/ixl_pf_iov.c  |  30 +++++++++++
 sys/dev/ixl/ixl_pf_main.c | 132 ++++++++++++++++++++++------------------------
 5 files changed, 115 insertions(+), 70 deletions(-)

diff --git a/share/man/man4/ixl.4 b/share/man/man4/ixl.4
index ef52b9f8b456..37d8c0cf5db8 100644
--- a/share/man/man4/ixl.4
+++ b/share/man/man4/ixl.4
@@ -213,6 +213,12 @@ use a value written to memory by the hardware instead of scanning the
 descriptor ring for completed descriptors.
 Enabled by default; disable to mimic the TX behavior found in
 .Xr ix 4 .
+.It Va hw.ixl.mdd_auto_reset_vf
+Automatically reset a VF after malicious-driver detection blocks it.
+This is disabled by default so a non-cooperative VF remains unable to issue
+traffic until it performs an FLR or the SR-IOV configuration is recreated.
+Enabling this tunable favors availability but allows a persistently faulty or
+hostile VF to resume after each reset.
 .El
 .Sh SYSCTL PROCEDURES
 .Bl -tag -width indent
diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c
index bc929753944b..a0e1fefe09fc 100644
--- a/sys/dev/ixl/if_ixl.c
+++ b/sys/dev/ixl/if_ixl.c
@@ -251,6 +251,12 @@ SYSCTL_INT(_hw_ixl, OID_AUTO, enable_vf_loopback, CTLFLAG_RDTUN,
     &ixl_enable_vf_loopback, 0,
     IXL_SYSCTL_HELP_VF_LOOPBACK);
 
+static int ixl_mdd_auto_reset_vf;
+TUNABLE_INT("hw.ixl.mdd_auto_reset_vf", &ixl_mdd_auto_reset_vf);
+SYSCTL_INT(_hw_ixl, OID_AUTO, mdd_auto_reset_vf, CTLFLAG_RDTUN,
+    &ixl_mdd_auto_reset_vf, 0,
+    "Automatically reset VFs blocked by malicious-driver detection");
+
 /*
  * Different method for processing TX descriptor
  * completion.
@@ -1938,6 +1944,7 @@ ixl_save_pf_tunables(struct ixl_pf *pf)
 	pf->hw.debug_mask = ixl_shared_debug_mask;
 	pf->vsi.enable_head_writeback = !!(ixl_enable_head_writeback);
 	pf->enable_vf_loopback = !!(ixl_enable_vf_loopback);
+	pf->mdd_auto_reset_vf = !!(ixl_mdd_auto_reset_vf);
 #if 0
 	pf->dynamic_rx_itr = ixl_dynamic_rx_itr;
 	pf->dynamic_tx_itr = ixl_dynamic_tx_itr;
@@ -1987,4 +1994,3 @@ ixl_save_pf_tunables(struct ixl_pf *pf)
 			pf->fc = ixl_flow_control;
 	}
 }
-
diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h
index 695fc8858140..f7bcf121683f 100644
--- a/sys/dev/ixl/ixl_pf.h
+++ b/sys/dev/ixl/ixl_pf.h
@@ -103,7 +103,12 @@ enum ixl_state {
 struct ixl_vf {
 	struct ixl_vsi		vsi;
 	u32			vf_flags;
-	u32			num_mdd_events;
+	u64			mdd_tx_events;
+	u64			mdd_rx_events;
+	struct timeval		last_mdd_log;
+	bool			mdd_blocked;
+	bool			mdd_event_pending;
+	bool			mdd_reset_pending;
 
 	u8			mac[ETHER_ADDR_LEN];
 	u8			mac_filters[IXL_VF_MAX_MAC_FILTERS][ETHER_ADDR_LEN];
@@ -151,6 +156,7 @@ struct ixl_pf {
 	int			tx_itr;
 	int			rx_itr;
 	int			enable_vf_loopback;
+	int			mdd_auto_reset_vf;
 
 	bool			link_up;
 	int			advertised_speed;
@@ -384,6 +390,7 @@ int	ixl_pf_reset(struct ixl_pf *);
 #ifdef PCI_IOV
 void	ixl_notify_vfs_reset(struct ixl_pf *);
 int	ixl_rebuild_vfs_after_reset(struct ixl_pf *);
+int	ixl_reset_vf_on_mdd(struct ixl_pf *, uint16_t);
 #endif
 
 void	ixl_set_queue_rx_itr(struct ixl_rx_queue *);
diff --git a/sys/dev/ixl/ixl_pf_iov.c b/sys/dev/ixl/ixl_pf_iov.c
index aefb01cfadb9..560b559f40e2 100644
--- a/sys/dev/ixl/ixl_pf_iov.c
+++ b/sys/dev/ixl/ixl_pf_iov.c
@@ -497,6 +497,9 @@ ixl_reinit_vf(struct ixl_pf *pf, struct ixl_vf *vf)
 
 	wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_num), VIRTCHNL_VFR_VFACTIVE);
 	ixl_flush(hw);
+	vf->mdd_blocked = false;
+	vf->mdd_event_pending = false;
+	vf->mdd_reset_pending = false;
 	return (0);
 }
 
@@ -1766,6 +1769,10 @@ ixl_handle_vf_msg(struct ixl_pf *pf, struct i40e_arq_event_info *event)
 	    (vf->vf_flags & VF_FLAG_ENABLED) ? " " : " disabled ",
 	    vf_num, msg_size);
 
+	/* Only a reset outside the virtchnl dispatcher may unblock the VF. */
+	if (vf->mdd_blocked)
+		return;
+
 	/* Perform basic checks on the msg */
 	err = virtchnl_vc_validate_vf_msg(&vf->version, opcode, msg, msg_size);
 	if (err) {
@@ -2036,6 +2043,26 @@ ixl_notify_vfs_reset(struct ixl_pf *pf)
 	}
 }
 
+int
+ixl_reset_vf_on_mdd(struct ixl_pf *pf, uint16_t vfnum)
+{
+	struct virtchnl_pf_event event;
+	struct ixl_vf *vf;
+
+	if (vfnum >= pf->num_vfs)
+		return (EINVAL);
+	vf = &pf->vfs[vfnum];
+	if (!(vf->vf_flags & VF_FLAG_ENABLED))
+		return (ENXIO);
+
+	bzero(&event, sizeof(event));
+	event.event = VIRTCHNL_EVENT_RESET_IMPENDING;
+	event.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM;
+	ixl_send_vf_msg(pf, vf, VIRTCHNL_OP_EVENT, I40E_SUCCESS,
+	    &event, sizeof(event));
+	return (ixl_reset_vf(pf, vf));
+}
+
 int
 ixl_rebuild_vfs_after_reset(struct ixl_pf *pf)
 {
@@ -2064,6 +2091,9 @@ ixl_rebuild_vfs_after_reset(struct ixl_pf *pf)
 		bit_nclear(vf->vsi.vlans_map, 0,
 		    IXL_VLANS_MAP_LEN - 1);
 		vf->num_mac_filters = 0;
+		vf->mdd_blocked = false;
+		vf->mdd_event_pending = false;
+		vf->mdd_reset_pending = false;
 	}
 
 	error = ixl_setup_iov_switch(pf);
diff --git a/sys/dev/ixl/ixl_pf_main.c b/sys/dev/ixl/ixl_pf_main.c
index 092718a12d10..b06188545116 100644
--- a/sys/dev/ixl/ixl_pf_main.c
+++ b/sys/dev/ixl/ixl_pf_main.c
@@ -1853,12 +1853,8 @@ ixl_handle_tx_mdd_event(struct ixl_pf *pf)
 	struct i40e_hw *hw = &pf->hw;
 	device_t dev = pf->dev;
 	struct ixl_vf *vf;
-	bool mdd_detected = false;
-	bool pf_mdd_detected = false;
-	bool vf_mdd_detected = false;
 	u16 vf_num, queue;
 	u8 pf_num, event;
-	u8 pf_mdet_num, vp_mdet_num;
 	u32 reg;
 
 	/* find what triggered the MDD event */
@@ -1872,18 +1868,20 @@ ixl_handle_tx_mdd_event(struct ixl_pf *pf)
 		    I40E_GL_MDET_TX_EVENT_SHIFT;
 		queue = (reg & I40E_GL_MDET_TX_QUEUE_MASK) >>
 		    I40E_GL_MDET_TX_QUEUE_SHIFT;
+		if (queue >= hw->func_caps.base_queue)
+			queue -= hw->func_caps.base_queue;
+		device_printf(dev,
+		    "last TX malicious-driver cause %#x on queue %u, "
+		    "PF %#x, VF %#x\n", event, queue, pf_num, vf_num);
 		wr32(hw, I40E_GL_MDET_TX, 0xffffffff);
-		mdd_detected = true;
 	}
 
-	if (!mdd_detected)
-		return;
-
 	reg = rd32(hw, I40E_PF_MDET_TX);
 	if (reg & I40E_PF_MDET_TX_VALID_MASK) {
 		wr32(hw, I40E_PF_MDET_TX, 0xFFFF);
-		pf_mdet_num = hw->pf_id;
-		pf_mdd_detected = true;
+		device_printf(dev,
+		    "TX malicious-driver issue detected on PF-%u\n",
+		    hw->pf_id);
 	}
 
 	/* Check if MDD was caused by a VF */
@@ -1892,32 +1890,16 @@ ixl_handle_tx_mdd_event(struct ixl_pf *pf)
 		reg = rd32(hw, I40E_VP_MDET_TX(i));
 		if (reg & I40E_VP_MDET_TX_VALID_MASK) {
 			wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF);
-			vp_mdet_num = i;
-			vf->num_mdd_events++;
-			vf_mdd_detected = true;
+			if (!(vf->vf_flags & VF_FLAG_ENABLED))
+				continue;
+			vf->mdd_tx_events++;
+			vf->mdd_event_pending = true;
+			if (!vf->mdd_blocked) {
+				vf->mdd_blocked = true;
+				vf->mdd_reset_pending = true;
+			}
 		}
 	}
-
-	/* Print out an error message */
-	if (vf_mdd_detected && pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on TX queue %d, pf number %d (PF-%d), vf number %d (VF-%d)\n",
-		    event, queue, pf_num, pf_mdet_num, vf_num, vp_mdet_num);
-	else if (vf_mdd_detected && !pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on TX queue %d, pf number %d, vf number %d (VF-%d)\n",
-		    event, queue, pf_num, vf_num, vp_mdet_num);
-	else if (!vf_mdd_detected && pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on TX queue %d, pf number %d (PF-%d)\n",
-		    event, queue, pf_num, pf_mdet_num);
-	/* Theoretically shouldn't happen */
-	else
-		device_printf(dev,
-		    "TX Malicious Driver Detection event (unknown)\n");
 }
 
 static void
@@ -1926,12 +1908,8 @@ ixl_handle_rx_mdd_event(struct ixl_pf *pf)
 	struct i40e_hw *hw = &pf->hw;
 	device_t dev = pf->dev;
 	struct ixl_vf *vf;
-	bool mdd_detected = false;
-	bool pf_mdd_detected = false;
-	bool vf_mdd_detected = false;
 	u16 queue;
 	u8 pf_num, event;
-	u8 pf_mdet_num, vp_mdet_num;
 	u32 reg;
 
 	/*
@@ -1946,18 +1924,20 @@ ixl_handle_rx_mdd_event(struct ixl_pf *pf)
 		    I40E_GL_MDET_RX_EVENT_SHIFT;
 		queue = (reg & I40E_GL_MDET_RX_QUEUE_MASK) >>
 		    I40E_GL_MDET_RX_QUEUE_SHIFT;
+		if (queue >= hw->func_caps.base_queue)
+			queue -= hw->func_caps.base_queue;
+		device_printf(dev,
+		    "last RX malicious-driver cause %#x on queue %u, "
+		    "function %#x\n", event, queue, pf_num);
 		wr32(hw, I40E_GL_MDET_RX, 0xffffffff);
-		mdd_detected = true;
 	}
 
-	if (!mdd_detected)
-		return;
-
 	reg = rd32(hw, I40E_PF_MDET_RX);
 	if (reg & I40E_PF_MDET_RX_VALID_MASK) {
 		wr32(hw, I40E_PF_MDET_RX, 0xFFFF);
-		pf_mdet_num = hw->pf_id;
-		pf_mdd_detected = true;
+		device_printf(dev,
+		    "RX malicious-driver issue detected on PF-%u\n",
+		    hw->pf_id);
 	}
 
 	/* Check if MDD was caused by a VF */
@@ -1966,32 +1946,16 @@ ixl_handle_rx_mdd_event(struct ixl_pf *pf)
 		reg = rd32(hw, I40E_VP_MDET_RX(i));
 		if (reg & I40E_VP_MDET_RX_VALID_MASK) {
 			wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF);
-			vp_mdet_num = i;
-			vf->num_mdd_events++;
-			vf_mdd_detected = true;
+			if (!(vf->vf_flags & VF_FLAG_ENABLED))
+				continue;
+			vf->mdd_rx_events++;
+			vf->mdd_event_pending = true;
+			if (!vf->mdd_blocked) {
+				vf->mdd_blocked = true;
+				vf->mdd_reset_pending = true;
+			}
 		}
 	}
-
-	/* Print out an error message */
-	if (vf_mdd_detected && pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on RX queue %d, pf number %d (PF-%d), (VF-%d)\n",
-		    event, queue, pf_num, pf_mdet_num, vp_mdet_num);
-	else if (vf_mdd_detected && !pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on RX queue %d, pf number %d, (VF-%d)\n",
-		    event, queue, pf_num, vp_mdet_num);
-	else if (!vf_mdd_detected && pf_mdd_detected)
-		device_printf(dev,
-		    "Malicious Driver Detection event %d"
-		    " on RX queue %d, pf number %d (PF-%d)\n",
-		    event, queue, pf_num, pf_mdet_num);
-	/* Theoretically shouldn't happen */
-	else
-		device_printf(dev,
-		    "RX Malicious Driver Detection event (unknown)\n");
 }
 
 /**
@@ -2004,6 +1968,10 @@ void
 ixl_handle_mdd_event(struct ixl_pf *pf)
 {
 	struct i40e_hw *hw = &pf->hw;
+	struct ixl_vf *vf;
+	static const struct timeval log_interval = { 2, 0 };
+	bool reset;
+	int i;
 	u32 reg;
 
 	/*
@@ -2012,6 +1980,34 @@ ixl_handle_mdd_event(struct ixl_pf *pf)
 	 */
 	ixl_handle_tx_mdd_event(pf);
 	ixl_handle_rx_mdd_event(pf);
+	for (i = 0; i < pf->num_vfs; i++) {
+		vf = &pf->vfs[i];
+		if (!vf->mdd_event_pending)
+			continue;
+		vf->mdd_event_pending = false;
+		reset = vf->mdd_reset_pending;
+		vf->mdd_reset_pending = false;
+		if (ratecheck(&vf->last_mdd_log, &log_interval)) {
+			device_printf(pf->dev,
+			    "malicious-driver event from VF-%d "
+			    "(tx %ju, rx %ju); %s\n", i,
+			    (uintmax_t)vf->mdd_tx_events,
+			    (uintmax_t)vf->mdd_rx_events,
+			    pf->mdd_auto_reset_vf && reset ?
+			    "resetting VF" : "VF remains blocked");
+		}
+#ifdef PCI_IOV
+		if (pf->mdd_auto_reset_vf && reset) {
+			int error;
+
+			error = ixl_reset_vf_on_mdd(pf, i);
+			if (error != 0)
+				device_printf(pf->dev,
+				    "failed to reset MDD-blocked VF-%d: %d\n",
+				    i, error);
+		}
+#endif
+	}
 
 	ixl_clear_state(&pf->state, IXL_STATE_MDD_PENDING);