git: 3e8b8afbb6e9 - stable/14 - ixl: Track and recover MDD-blocked VFs
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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);