git: 33fdcdb18eb6 - main - ixgbe: complete PF cleanup after VF FLR
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 07 Aug 2026 12:45:48 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=33fdcdb18eb61f089b9a4786ef7aa9224ad0722c
commit 33fdcdb18eb61f089b9a4786ef7aa9224ad0722c
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-01 06:03:17 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
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;