git: 33fdcdb18eb6 - main - ixgbe: complete PF cleanup after VF FLR

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a75d37c.38b6f.2e1eec7d__37202.6468788803$1786106776$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by kbowling:

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

commit 33fdcdb18eb61f089b9a4786ef7aa9224ad0722c
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-01 06:03:17 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-07 12:45:43 +0000

    ixgbe: complete PF cleanup after VF FLR
    
    The 82599, X540, and X550 documentation identifies VF registers which
    retain state across VFLR and must be reconfigured before a VF is reused.
    The VF reset path already initializes its queue-owned registers, but the
    PF only cleared VF mailbox memory and transmit head write-back addresses
    after a cooperative mailbox reset.  A bare hardware VFLR therefore left
    both behind on affected devices.
    
    Move TDWBA cleanup into the common reset path.  Clear CTS when VFLR
    invalidates the mailbox session, and accept only VF_RESET during the
    reset pass before restoring VF traffic.
    
    Clear VFMBMEM through the PFU/VFU semaphore.  Recheck VFREQ while
    holding PFU so a reset event cannot erase a request posted between the
    initial mailbox check and the clear.  Dispatch an already-read message
    even if the residual clear fails, but keep cleanup pending until a
    synchronized clear succeeds.  Retry cleanup in the same admin pass
    after a failed message read or clear.
    
    The 82599 also retains VFMAILBOX.VFU across VFLR.  Leave a VF-owned
    mailbox intact initially so a live post-reset writer can finish.  Retry
    cleanup from the admin timer and, after a two-second grace period, use
    PFMAILBOX.RVFU only when VFU remains set and no request has been posted.
    Clear the mailbox under PFU afterward.  This recovers an abandoned
    pre-reset owner without sleeping under the iflib context lock or
    immediately stealing from a new reset request.
    
    Suppress mailbox dispatch once iflib has cleared IFF_DRV_RUNNING so a
    pending reset request cannot re-enable VF traffic inside the PF stop
    path.  Periodically sample aggregate VFREQ, VFACK, and VFLR registers,
    masked to active VFs, so work suppressed across a stop/restart and a
    bare 82599 VFLR without EICR_MAILBOX are both discovered without another
    interrupt edge.
    
    MFC after:      2 weeks
---
 sys/dev/ixgbe/if_ix.c       |   9 ++-
 sys/dev/ixgbe/if_sriov.c    | 190 +++++++++++++++++++++++++++++++++++++-------
 sys/dev/ixgbe/ixgbe.h       |   2 +
 sys/dev/ixgbe/ixgbe_mbx.c   |  66 +++++++++++++--
 sys/dev/ixgbe/ixgbe_mbx.h   |   1 +
 sys/dev/ixgbe/ixgbe_sriov.h |   2 +
 sys/dev/ixgbe/ixgbe_vf.c    |   4 +-
 7 files changed, 237 insertions(+), 37 deletions(-)

diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index f800be109fc3..d18f60984b8f 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -4831,7 +4831,14 @@ ixgbe_if_update_admin_status(if_ctx_t ctx)
 		ixgbe_handle_mod(ctx);
 	if (sc->task_requests & IXGBE_REQUEST_TASK_MSF)
 		ixgbe_handle_msf(ctx);
-	if (sc->task_requests & IXGBE_REQUEST_TASK_MBX)
+	/*
+	 * A reset request re-enables VF traffic, so do not service mailboxes
+	 * while the PF is stopped.  VFREQ, VFACK, and VFLR are hardware-latched
+	 * and ixgbe_mbx_pending() resamples them after the PF is running again.
+	 */
+	if ((if_getdrvflags(iflib_get_ifp(ctx)) & IFF_DRV_RUNNING) != 0 &&
+	    ((sc->task_requests & IXGBE_REQUEST_TASK_MBX) != 0 ||
+	    sc->iov_mbx_cleanup_pending || ixgbe_mbx_pending(sc)))
 		ixgbe_handle_mbx(ctx);
 	if (sc->task_requests & IXGBE_REQUEST_TASK_FDIR)
 		ixgbe_reinit_fdir(ctx);
diff --git a/sys/dev/ixgbe/if_sriov.c b/sys/dev/ixgbe/if_sriov.c
index b2325046d5e8..848a53df290c 100644
--- a/sys/dev/ixgbe/if_sriov.c
+++ b/sys/dev/ixgbe/if_sriov.c
@@ -41,6 +41,8 @@
 
 MALLOC_DEFINE(M_IXGBE_SRIOV, "ix_sriov", "ix SR-IOV allocations");
 
+#define IXGBE_VF_MBX_CLEANUP_GRACE	(2 * SBT_1S)
+
 /************************************************************************
  * ixgbe_define_iov_schemas
  ************************************************************************/
@@ -521,9 +523,13 @@ ixgbe_vf_frame_size_compatible(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 static void
 ixgbe_process_vf_reset(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 {
+	struct ixgbe_hw *hw;
 	bool rebuild_mta;
 	s32 error;
+	int i, queue_count;
 
+	hw = &sc->hw;
+	vf->flags &= ~IXGBE_VF_CTS;
 	rebuild_mta = vf->num_mc_hashes != 0;
 	vf->xcast_mode = IXGBEVF_XCAST_MODE_NONE;
 	vf->num_mc_hashes = 0;
@@ -537,9 +543,18 @@ ixgbe_process_vf_reset(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 		ixgbe_iov_rebuild_mta(sc);
 
 	ixgbe_vf_clear_mac_filters(sc, vf, true);
-	ixgbe_clear_rar(&sc->hw, vf->rar_index);
+	ixgbe_clear_rar(hw, vf->rar_index);
 	ixgbe_vf_set_anti_spoof(sc, vf);
-	ixgbe_toggle_txdctl(&sc->hw, vf->pool);
+	ixgbe_toggle_txdctl(hw, vf->pool);
+
+	/* VFLR does not clear transmit head write-back state. */
+	queue_count = ixgbe_vf_queues(sc->iov_mode);
+	for (i = 0; i < queue_count; i++) {
+		IXGBE_WRITE_REG(hw,
+		    IXGBE_PVFTDWBAHn(queue_count, vf->pool, i), 0);
+		IXGBE_WRITE_REG(hw,
+		    IXGBE_PVFTDWBALn(queue_count, vf->pool, i), 0);
+	}
 
 	vf->api_ver = IXGBE_API_VER_UNKNOWN;
 } /* ixgbe_process_vf_reset */
@@ -584,28 +599,10 @@ ixgbe_vf_reset_msg(struct ixgbe_softc *sc, struct ixgbe_vf *vf, uint32_t *msg)
 	struct ixgbe_hw *hw;
 	uint32_t ack;
 	uint32_t resp[IXGBE_VF_PERMADDR_MSG_LEN];
-	int i, queue_count;
 
 	hw = &sc->hw;
 
 	ixgbe_process_vf_reset(sc, vf);
-	/*
-	 * The reset request was consumed by ixgbe_process_vf_msg(), so it is
-	 * now safe to clear this VF's mailbox.
-	 */
-	ixgbe_clear_mbx(hw, vf->pool);
-
-	/*
-	 * VF reset does not clear the transmit head write-back addresses.
-	 * The queues were disabled by ixgbe_process_vf_reset().
-	 */
-	queue_count = ixgbe_vf_queues(sc->iov_mode);
-	for (i = 0; i < queue_count; i++) {
-		IXGBE_WRITE_REG(hw,
-		    IXGBE_PVFTDWBAHn(queue_count, vf->pool, i), 0);
-		IXGBE_WRITE_REG(hw,
-		    IXGBE_PVFTDWBALn(queue_count, vf->pool, i), 0);
-	}
 
 	if (ixgbe_validate_mac_addr(vf->ether_addr) == 0) {
 		ixgbe_set_rar(&sc->hw, vf->rar_index, vf->ether_addr,
@@ -936,8 +933,8 @@ ixgbe_vf_get_queues(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 } /* ixgbe_vf_get_queues */
 
 
-static void
-ixgbe_process_vf_msg(if_ctx_t ctx, struct ixgbe_vf *vf)
+static bool
+ixgbe_process_vf_msg(if_ctx_t ctx, struct ixgbe_vf *vf, bool reset_pending)
 {
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
 #ifdef KTR
@@ -945,25 +942,39 @@ ixgbe_process_vf_msg(if_ctx_t ctx, struct ixgbe_vf *vf)
 #endif
 	struct ixgbe_hw *hw;
 	uint32_t msg[IXGBE_VFMAILBOX_SIZE];
+	bool cleanup_complete;
 	int error;
 
 	hw = &sc->hw;
+	cleanup_complete = true;
 
 	error = ixgbe_read_mbx(hw, msg, IXGBE_VFMAILBOX_SIZE, vf->pool);
 
 	if (error != 0)
-		return;
+		return (false);
+	/*
+	 * Some devices do not clear VFMBMEM on VFLR.  Copy a pending request
+	 * first because the VF posts its mailbox reset request after raising
+	 * the reset event.
+	 */
+	if (reset_pending || msg[0] == IXGBE_VF_RESET)
+		cleanup_complete =
+		    ixgbe_clear_mbx(hw, vf->pool) == IXGBE_SUCCESS;
+	/* The successful read ACKed the request; dispatch it even if not clear. */
 
 	CTR3(KTR_MALLOC, "%s: received msg %x from %d", if_name(ifp),
 	    msg[0], vf->pool);
 	if (msg[0] == IXGBE_VF_RESET) {
 		ixgbe_vf_reset_msg(sc, vf, msg);
-		return;
+		return (cleanup_complete);
 	}
+	/* Discard requests from the mailbox session invalidated by VFLR. */
+	if (reset_pending)
+		return (cleanup_complete);
 
 	if (!(vf->flags & IXGBE_VF_CTS)) {
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
-		return;
+		return (true);
 	}
 
 	switch (msg[0] & IXGBE_VT_MSG_MASK) {
@@ -994,8 +1005,39 @@ ixgbe_process_vf_msg(if_ctx_t ctx, struct ixgbe_vf *vf)
 	default:
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
 	}
+	return (true);
 } /* ixgbe_process_vf_msg */
 
+static void
+ixgbe_cleanup_vf_mbx(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
+{
+	struct ixgbe_hw *hw;
+	sbintime_t now;
+
+	hw = &sc->hw;
+	if (ixgbe_clear_mbx(hw, vf->pool) == IXGBE_SUCCESS) {
+		vf->flags &= ~IXGBE_VF_MBX_CLEANUP;
+		return;
+	}
+
+	now = getsbinuptime();
+	if (now < vf->mbx_cleanup_deadline)
+		return;
+
+	/*
+	 * A functioning VF posts VFREQ within the grace interval.  Ownership
+	 * still held after that interval is residue from the old reset epoch.
+	 * The force-clear helper rechecks VFREQ before asserting RVFU.
+	 */
+	if (ixgbe_force_clear_mbx_pf(hw, vf->pool) == IXGBE_SUCCESS) {
+		vf->flags &= ~IXGBE_VF_MBX_CLEANUP;
+		return;
+	}
+
+	/* A request raced the force-clear attempt; give it another interval. */
+	vf->mbx_cleanup_deadline = now + IXGBE_VF_MBX_CLEANUP_GRACE;
+}
+
 /* Tasklet for handling VF -> PF mailbox messages */
 void
 ixgbe_handle_mbx(void *context)
@@ -1004,29 +1046,109 @@ ixgbe_handle_mbx(void *context)
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
 	struct ixgbe_hw *hw;
 	struct ixgbe_vf *vf;
+	bool cleanup_pending, reset_pending, reset_seen;
 	int i;
 
 	hw = &sc->hw;
+	cleanup_pending = false;
 
 	for (i = 0; i < sc->num_vfs; i++) {
 		vf = &sc->vfs[i];
 
 		if (vf->flags & IXGBE_VF_ACTIVE) {
-			if (hw->mbx.ops[vf->pool].check_for_rst(hw,
-			    vf->pool) == 0)
+			reset_seen = hw->mbx.ops[vf->pool].check_for_rst(hw,
+			    vf->pool) == 0;
+			if (reset_seen) {
+				vf->flags |= IXGBE_VF_MBX_CLEANUP;
+				vf->mbx_cleanup_deadline = getsbinuptime() +
+				    IXGBE_VF_MBX_CLEANUP_GRACE;
 				ixgbe_process_vf_reset(sc, vf);
+			}
+			reset_pending =
+			    (vf->flags & IXGBE_VF_MBX_CLEANUP) != 0;
 
 			if (hw->mbx.ops[vf->pool].check_for_msg(hw,
-			    vf->pool) == 0)
-				ixgbe_process_vf_msg(ctx, vf);
+			    vf->pool) == 0) {
+				if (ixgbe_process_vf_msg(ctx, vf, reset_pending))
+					vf->flags &= ~IXGBE_VF_MBX_CLEANUP;
+			}
+			if (reset_pending &&
+			    (vf->flags & IXGBE_VF_MBX_CLEANUP) != 0)
+				ixgbe_cleanup_vf_mbx(sc, vf);
 
 			if (hw->mbx.ops[vf->pool].check_for_ack(hw,
 			    vf->pool) == 0)
 				ixgbe_process_vf_ack(sc, vf);
+
+			if (vf->flags & IXGBE_VF_MBX_CLEANUP)
+				cleanup_pending = true;
 		}
 	}
+	sc->iov_mbx_cleanup_pending = cleanup_pending;
 } /* ixgbe_handle_mbx */
 
+/*
+ * VFREQ, VFACK, and a bare VFLR can remain latched without a usable shared
+ * mailbox interrupt across a PF stop/restart.  Sample their aggregate
+ * registers from the periodic admin pass so work does not depend on another
+ * edge.
+ */
+bool
+ixgbe_mbx_pending(struct ixgbe_softc *sc)
+{
+	struct ixgbe_hw *hw;
+	uint32_t active_mbx[4], active_rst[2], events;
+	int i, index;
+
+	if (sc->num_vfs == 0)
+		return (false);
+
+	bzero(active_mbx, sizeof(active_mbx));
+	bzero(active_rst, sizeof(active_rst));
+	for (i = 0; i < sc->num_vfs; i++) {
+		if ((sc->vfs[i].flags & IXGBE_VF_ACTIVE) == 0)
+			continue;
+		index = IXGBE_PFMBICR_INDEX(sc->vfs[i].pool);
+		active_mbx[index] |=
+		    IXGBE_PFMBICR_VFREQ_VF1 <<
+		    IXGBE_PFMBICR_SHIFT(sc->vfs[i].pool);
+		active_mbx[index] |=
+		    IXGBE_PFMBICR_VFACK_VF1 <<
+		    IXGBE_PFMBICR_SHIFT(sc->vfs[i].pool);
+		index = IXGBE_PFVFLRE_INDEX(sc->vfs[i].pool);
+		active_rst[index] |=
+		    1U << IXGBE_PFVFLRE_SHIFT(sc->vfs[i].pool);
+	}
+
+	hw = &sc->hw;
+	for (index = 0; index < nitems(active_mbx); index++) {
+		if (active_mbx[index] != 0 &&
+		    (IXGBE_READ_REG(hw, IXGBE_PFMBICR(index)) &
+		    active_mbx[index]) != 0)
+			return (true);
+	}
+	for (index = 0; index < nitems(active_rst); index++) {
+		if (active_rst[index] == 0)
+			continue;
+		switch (hw->mac.type) {
+		case ixgbe_mac_82599EB:
+			events = IXGBE_READ_REG(hw, IXGBE_PFVFLRE(index));
+			break;
+		case ixgbe_mac_X540:
+		case ixgbe_mac_X550:
+		case ixgbe_mac_X550EM_x:
+		case ixgbe_mac_X550EM_a:
+			events = IXGBE_READ_REG(hw, IXGBE_PFVFLREC(index));
+			break;
+		default:
+			return (false);
+		}
+		if ((events & active_rst[index]) != 0)
+			return (true);
+	}
+	return (false);
+}
+
 int
 ixgbe_iov_validate(struct ixgbe_softc *sc, u16 num_vfs)
 {
@@ -1114,6 +1236,7 @@ ixgbe_if_iov_init(if_ctx_t ctx, u16 num_vfs, const nvlist_t *config)
 	}
 
 	sc->num_vfs = num_vfs;
+	sc->iov_mbx_cleanup_pending = false;
 	ixgbe_init_mbx_params_pf(&sc->hw);
 
 	sc->feat_en |= IXGBE_FEATURE_SRIOV;
@@ -1129,6 +1252,7 @@ err_init_iov:
 	sc->num_vfs = 0;
 	sc->pool = 0;
 	sc->iov_mode = IXGBE_NO_VM;
+	sc->iov_mbx_cleanup_pending = false;
 
 	return (retval);
 } /* ixgbe_if_iov_init */
@@ -1204,6 +1328,7 @@ ixgbe_if_iov_uninit(if_ctx_t ctx)
 	ixgbe_align_all_queue_indices(sc);
 	sc->iov_vfta_valid = false;
 	sc->iov_vlan_promisc = false;
+	sc->iov_mbx_cleanup_pending = false;
 	(void)ixgbe_clear_vfta(hw);
 	ixgbe_setup_vlan_hw_support(ctx);
 } /* ixgbe_if_iov_uninit */
@@ -1405,4 +1530,11 @@ ixgbe_handle_mbx(void *context)
 	UNREFERENCED_PARAMETER(context);
 } /* ixgbe_handle_mbx */
 
+bool
+ixgbe_mbx_pending(struct ixgbe_softc *sc)
+{
+	UNREFERENCED_PARAMETER(sc);
+	return (false);
+}
+
 #endif
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 003021e928a9..20f8fce8f04b 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -366,6 +366,7 @@ struct ixgbe_vf {
 	uint16_t	default_vlan;
 	uint16_t	api_ver;
 	uint8_t		xcast_mode;
+	sbintime_t	mbx_cleanup_deadline;
 };
 
 /* Our softc structure */
@@ -461,6 +462,7 @@ struct ixgbe_softc {
 	bool			iov_mta_valid;
 	bool			iov_vfta_valid;
 	bool			iov_vlan_promisc;
+	bool			iov_mbx_cleanup_pending;
 
 	/* Bypass */
 	struct ixgbe_bp_data	bypass;
diff --git a/sys/dev/ixgbe/ixgbe_mbx.c b/sys/dev/ixgbe/ixgbe_mbx.c
index 810b282bbd0e..eefe4543f3fe 100644
--- a/sys/dev/ixgbe/ixgbe_mbx.c
+++ b/sys/dev/ixgbe/ixgbe_mbx.c
@@ -207,7 +207,8 @@ s32 ixgbe_check_for_rst(struct ixgbe_hw *hw, u16 mbx_id)
  * @hw: pointer to the HW structure
  * @mbx_id: id of mailbox to write
  *
- * Set VFMBMEM of given VF to 0x0.
+ * Set VFMBMEM of given VF to 0x0.  A peer-owned or newly posted mailbox is
+ * left intact.
  **/
 s32 ixgbe_clear_mbx(struct ixgbe_hw *hw, u16 mbx_id)
 {
@@ -1112,24 +1113,79 @@ static s32 ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
 }
 
 /**
- * ixgbe_clear_mbx_pf - Clear Mailbox Memory
+ * ixgbe_clear_mbx_pf - Clear idle Mailbox Memory
  * @hw: pointer to the HW structure
  * @vf_id: the VF index
  *
- * Set VFMBMEM of given VF to 0x0.
+ * Set VFMBMEM of given VF to 0x0 unless the VF owns it or has posted a new
+ * request.
  **/
 static s32 ixgbe_clear_mbx_pf(struct ixgbe_hw *hw, u16 vf_id)
 {
-	u16 mbx_size = hw->mbx.size;
+	s32 ret_val;
+	u16 mbx_size;
 	u16 i;
 
 	if (vf_id > 63)
 		return IXGBE_ERR_PARAM;
 
+	/*
+	 * Serialize against a VF composing a request.  After acquiring PFU,
+	 * recheck VFREQ to close the window between the caller's mailbox check
+	 * and this clear.  Leave a newly posted request intact for the normal
+	 * mailbox path to consume.
+	 */
+	ret_val = ixgbe_obtain_mbx_lock_pf(hw, vf_id);
+	if (ret_val != IXGBE_SUCCESS)
+		return ret_val;
+	if (ixgbe_check_for_msg_pf(hw, vf_id) == IXGBE_SUCCESS) {
+		ret_val = IXGBE_ERR_MBX;
+		goto out;
+	}
+
+	mbx_size = hw->mbx.size;
 	for (i = 0; i < mbx_size; ++i)
 		IXGBE_WRITE_REG_ARRAY(hw, IXGBE_PFMBMEM(vf_id), i, 0x0);
+	ret_val = IXGBE_SUCCESS;
 
-	return IXGBE_SUCCESS;
+out:
+	ixgbe_release_mbx_lock_pf(hw, vf_id);
+	return ret_val;
+}
+
+/**
+ * ixgbe_force_clear_mbx_pf - Revoke stale VF ownership and clear its mailbox
+ * @hw: pointer to the HW structure
+ * @vf_id: the VF index
+ *
+ * VFLR does not clear VFMAILBOX.VFU on some devices.  The caller must allow a
+ * live VF a bounded interval to finish posting its reset request before using
+ * this function.  Refuse to revoke ownership if a request is already posted,
+ * then use RVFU and the normal synchronized clear path.
+ **/
+s32 ixgbe_force_clear_mbx_pf(struct ixgbe_hw *hw, u16 vf_id)
+{
+	u32 pf_mailbox;
+
+	if (vf_id > 63)
+		return IXGBE_ERR_PARAM;
+
+	pf_mailbox = IXGBE_READ_REG(hw, IXGBE_PFMAILBOX(vf_id));
+	if ((pf_mailbox & IXGBE_PFMAILBOX_VFU) == 0)
+		return ixgbe_clear_mbx_pf(hw, vf_id);
+	if (ixgbe_check_for_msg_pf(hw, vf_id) == IXGBE_SUCCESS)
+		return IXGBE_ERR_MBX;
+
+	/*
+	 * Hardware has no atomic revoke-if-idle operation.  A VF that starts a
+	 * request after the grace interval can race RVFU; the synchronized clear
+	 * below detects a request that has reached VFREQ, and its failure makes
+	 * the caller retain cleanup state for another interval.
+	 */
+	IXGBE_WRITE_REG(hw, IXGBE_PFMAILBOX(vf_id), IXGBE_PFMAILBOX_RVFU);
+	IXGBE_WRITE_FLUSH(hw);
+
+	return ixgbe_clear_mbx_pf(hw, vf_id);
 }
 
 /**
diff --git a/sys/dev/ixgbe/ixgbe_mbx.h b/sys/dev/ixgbe/ixgbe_mbx.h
index e6519963242e..fc6831f67b34 100644
--- a/sys/dev/ixgbe/ixgbe_mbx.h
+++ b/sys/dev/ixgbe/ixgbe_mbx.h
@@ -196,6 +196,7 @@ s32 ixgbe_check_for_msg(struct ixgbe_hw *hw, u16 mbx_id);
 s32 ixgbe_check_for_ack(struct ixgbe_hw *hw, u16 mbx_id);
 s32 ixgbe_check_for_rst(struct ixgbe_hw *hw, u16 mbx_id);
 s32 ixgbe_clear_mbx(struct ixgbe_hw *hw, u16 vf_number);
+s32 ixgbe_force_clear_mbx_pf(struct ixgbe_hw *hw, u16 vf_number);
 void ixgbe_init_mbx_params_vf(struct ixgbe_hw *hw);
 void ixgbe_upgrade_mbx_params_vf(struct ixgbe_hw *hw);
 void ixgbe_init_mbx_params_pf(struct ixgbe_hw *hw);
diff --git a/sys/dev/ixgbe/ixgbe_sriov.h b/sys/dev/ixgbe/ixgbe_sriov.h
index 443bf78e9a1d..1ae54863befc 100644
--- a/sys/dev/ixgbe/ixgbe_sriov.h
+++ b/sys/dev/ixgbe/ixgbe_sriov.h
@@ -49,6 +49,7 @@
 #define IXGBE_VF_ACTIVE         (1 << 3) /* VF is active. */
 #define IXGBE_VF_ANTI_SPOOF     (1 << 4) /* Enforce source identity. */
 #define IXGBE_VF_ALLOW_PROMISC  (1 << 5) /* VF may request promiscuity. */
+#define IXGBE_VF_MBX_CLEANUP    (1 << 6) /* Reset mailbox cleanup pending. */
 #define IXGBE_VF_INDEX(vmdq)    ((vmdq) / 32)
 #define IXGBE_VF_BIT(vmdq)      (1 << ((vmdq) % 32))
 
@@ -106,5 +107,6 @@ u32  ixgbe_get_mrqc(int);
 
 void ixgbe_if_init(if_ctx_t ctx);
 void ixgbe_handle_mbx(void *);
+bool ixgbe_mbx_pending(struct ixgbe_softc *);
 
 #endif
diff --git a/sys/dev/ixgbe/ixgbe_vf.c b/sys/dev/ixgbe/ixgbe_vf.c
index 5f65ee038753..00f0cb131cbf 100644
--- a/sys/dev/ixgbe/ixgbe_vf.c
+++ b/sys/dev/ixgbe/ixgbe_vf.c
@@ -210,8 +210,8 @@ s32 ixgbe_reset_hw_vf(struct ixgbe_hw *hw)
 	ixgbe_virt_clr_reg(hw);
 
 	/*
-	 * VFLR does not clear VFMAILBOX.VFU. Drop stale ownership and
-	 * cached read-to-clear status.
+	 * Some devices do not clear VFMAILBOX.VFU on VFLR.  Drop stale
+	 * ownership and cached read-to-clear status.
 	 */
 	IXGBE_WRITE_REG(hw, IXGBE_VFMAILBOX, 0);
 	hw->mbx.vf_mailbox = 0;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.