Page MenuHomeFreeBSD

D58904.id184239.diff
No OneTemporary

D58904.id184239.diff

Index: sys/dev/iavf/iavf_lib.h
===================================================================
--- sys/dev/iavf/iavf_lib.h
+++ sys/dev/iavf/iavf_lib.h
@@ -313,6 +313,9 @@
IAVF_STATE_INITIALIZED,
IAVF_STATE_RESET_REQUIRED,
IAVF_STATE_RESET_PENDING,
+ IAVF_STATE_RESET_RECOVERY,
+ IAVF_STATE_VC_POLLING,
+ IAVF_STATE_DMA_FENCED,
IAVF_STATE_RUNNING,
/* This entry must be last */
IAVF_STATE_LAST,
Index: sys/dev/iavf/iavf_lib.c
===================================================================
--- sys/dev/iavf/iavf_lib.c
+++ sys/dev/iavf/iavf_lib.c
@@ -338,8 +338,11 @@
int error = 0;
/* Ask the PF to reset us if we are initiating */
- if (!iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING))
- iavf_request_reset(sc);
+ if (!iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING)) {
+ error = iavf_request_reset(sc);
+ if (error != 0)
+ return (error);
+ }
iavf_msec_pause(100);
error = iavf_reset_complete(hw);
@@ -348,6 +351,12 @@
__func__);
return (error);
}
+ /*
+ * The completed reset stopped the old queues. Record that fact so
+ * stop and initialization need not issue DISABLE_QUEUES again.
+ */
+ iavf_set_state(&sc->state, IAVF_STATE_RESET_RECOVERY);
+ atomic_store_rel_32(&sc->queues_enabled, 0);
pci_enable_busmaster(dev);
error = iavf_shutdown_adminq(hw);
@@ -1473,8 +1482,9 @@
* to IAVF_MAX_DIS_Q_RETRY times if the response says that it wasn't
* successful. This is intended to workaround a bug that can appear on the PF.
*
- * @returns zero on success, or an error code if the request could not be sent
- * or acknowledged.
+ * @returns zero on success, EBUSY when the PF replied but did not confirm
+ * that the queues were disabled, or another error if the request could not
+ * be sent or acknowledged.
*/
int
iavf_disable_queues_with_retries(struct iavf_sc *sc)
@@ -1483,9 +1493,9 @@
int max_attempts = IAVF_MAX_DIS_Q_RETRY;
int error = 0, msg_count = 0;
- /* While the driver is detaching, it doesn't care if the queue
- * disable finishes successfully or not. Just send one message
- * to just notify the PF driver.
+ /*
+ * The detach path cannot wait for a reply. Send one request and let
+ * the caller reset or fence the VF if the queues remain unconfirmed.
*/
if (in_detach)
max_attempts = 1;
@@ -1504,13 +1514,13 @@
iavf_dbg_vc(sc, "DISABLE_QUEUES messages sent: %d\n",
msg_count);
- if (!in_detach && msg_count >= max_attempts &&
+ if (msg_count >= max_attempts &&
atomic_load_acq_32(&sc->queues_enabled)) {
- if (iavf_mbx_log_allowed(sc))
+ if (!in_detach && iavf_mbx_log_allowed(sc))
device_printf(sc->dev,
"%s: DISABLE_QUEUES may have failed\n", __func__);
if (error == 0)
- error = EIO;
+ error = EBUSY;
}
return (error);
}
Index: sys/dev/iavf/iavf_vc_common.c
===================================================================
--- sys/dev/iavf/iavf_vc_common.c
+++ sys/dev/iavf/iavf_vc_common.c
@@ -638,19 +638,32 @@
*
* Request that the PF reset this VF. No response is expected.
*
- * @returns zero
+ * @returns zero on success, or an error code if the request could not be
+ * submitted.
*/
int
iavf_request_reset(struct iavf_sc *sc)
{
+ struct iavf_hw *hw;
+ u32 old_rstat;
+ int error;
+
+ hw = &sc->hw;
+ old_rstat = rd32(hw, IAVF_VFGEN_RSTAT) &
+ IAVF_VFGEN_RSTAT_VFR_STATE_MASK;
/*
- ** Set the reset status to "in progress" before
- ** the request, this avoids any possibility of
- ** a mistaken early detection of completion.
- */
- wr32(&sc->hw, IAVF_VFGEN_RSTAT, VIRTCHNL_VFR_INPROGRESS);
- iavf_send_pf_msg(sc, VIRTCHNL_OP_RESET_VF, NULL, 0);
- return (0);
+ * The VF must publish INPROGRESS before asking the PF to reset it. This
+ * also prevents a stale VFACTIVE value from looking like completion.
+ */
+ wr32(hw, IAVF_VFGEN_RSTAT, VIRTCHNL_VFR_INPROGRESS);
+ iavf_flush(hw);
+ error = iavf_send_pf_msg(sc, VIRTCHNL_OP_RESET_VF, NULL, 0);
+ if (error != 0) {
+ /* No reset was submitted; do not leave a synthetic reset behind. */
+ wr32(hw, IAVF_VFGEN_RSTAT, old_rstat);
+ iavf_flush(hw);
+ }
+ return (error);
}
/**
Index: sys/dev/iavf/if_iavf_iflib.c
===================================================================
--- sys/dev/iavf/if_iavf_iflib.c
+++ sys/dev/iavf/if_iavf_iflib.c
@@ -90,6 +90,7 @@
static bool iavf_if_needs_restart(if_ctx_t, enum iflib_restart_event);
static void iavf_mbx_lost(struct iavf_sc *);
+static void iavf_enter_reset_recovery(struct iavf_sc *);
static void iavf_mbx_retry_detach(struct iavf_sc *);
static void iavf_mbx_retry_failed(if_ctx_t);
static void iavf_mbx_retry_prepare(struct iavf_sc *);
@@ -97,6 +98,8 @@
static void iavf_mbx_retry_succeeded(struct iavf_sc *);
static int iavf_reestablish_vc(struct iavf_sc *);
static void iavf_replay_filters(struct iavf_sc *);
+static void iavf_hold_vc_polling(struct iavf_sc *);
+static int iavf_quiesce_queues(struct iavf_sc *);
static int iavf_wait_asq(struct iavf_sc *, u32);
static int iavf_allocate_pci_resources(struct iavf_sc *);
@@ -510,6 +513,12 @@
bzero(&sc->vsi.eth_stats, sizeof(struct iavf_eth_stats));
iavf_add_device_sysctls(sc);
+ /*
+ * attach_pre completed the initial reset and resource handshake. Do
+ * not carry the runtime reset-recovery state into iflib's first
+ * stop/init cycle.
+ */
+ iavf_clear_state(&sc->state, IAVF_STATE_RESET_RECOVERY);
atomic_store_rel_32(&sc->queues_enabled, 0);
atomic_store_rel_32(&sc->mbx_ready, 1);
atomic_store_rel_32(&sc->vc_reinit_required, 0);
@@ -748,6 +757,7 @@
sc = arg;
if (atomic_readandclear_32(&sc->mbx_retry_pending) == 0 ||
atomic_load_acq_32(&sc->mbx_ready) != 0 ||
+ iavf_test_state(&sc->state, IAVF_STATE_DMA_FENCED) ||
iflib_in_detach(sc->vsi.ctx))
return;
ifp = iflib_get_ifp(sc->vsi.ctx);
@@ -803,6 +813,174 @@
sc->mbx_retry_stage = 0;
}
+static void
+iavf_enter_reset_recovery(struct iavf_sc *sc)
+{
+
+ iavf_set_state(&sc->state, IAVF_STATE_RESET_PENDING);
+ iavf_set_state(&sc->state, IAVF_STATE_RESET_RECOVERY);
+}
+
+/*
+ * Record that a reset has stopped the old LAN queues and invalidated the
+ * current virtchnl connection. The next initialization must rediscover the
+ * VF resources before it can rebuild and enable the LAN queues.
+ */
+static void
+iavf_reset_queues_stopped(struct iavf_sc *sc)
+{
+
+ iavf_enter_reset_recovery(sc);
+ atomic_store_rel_32(&sc->queues_enabled, 0);
+ atomic_store_rel_32(&sc->mbx_ready, 0);
+ atomic_store_rel_32(&sc->vc_reinit_required, 1);
+}
+
+/* Give synchronous reset/discovery code exclusive ownership of the AdminQ. */
+static void
+iavf_hold_vc_polling(struct iavf_sc *sc)
+{
+
+ iavf_set_state(&sc->state, IAVF_STATE_VC_POLLING);
+ iavf_disable_adminq_irq(&sc->hw);
+ taskqueue_drain(sc->vc_tq, &sc->vc_task);
+ /* A task already running when interrupts were masked can re-enable it. */
+ iavf_disable_adminq_irq(&sc->hw);
+}
+
+/*
+ * Prevent further DMA when neither virtchnl nor a VF reset can prove that the
+ * LAN queues stopped. Initialization deliberately leaves bus mastering off;
+ * detaching and reattaching the driver is required to recover this state.
+ */
+static int
+iavf_fence_dma(struct iavf_sc *sc, int cause)
+{
+ device_t dev;
+ u_int timeout;
+ int error;
+
+ dev = sc->dev;
+ error = pci_disable_busmaster(dev);
+ if (error != 0) {
+ device_printf(dev, "Unable to disable bus mastering: %d\n", error);
+ return (error);
+ }
+ if ((pci_read_config(dev, PCIR_COMMAND, 2) &
+ PCIM_CMD_BUSMASTEREN) != 0) {
+ device_printf(dev,
+ "Bus mastering remains enabled after DMA fence\n");
+ return (EIO);
+ }
+
+ timeout = max(pcie_get_max_completion_timeout(dev) / 1000, 10);
+ if (!pcie_wait_for_pending_transactions(dev, timeout))
+ device_printf(dev,
+ "Transactions remain pending after bus mastering was disabled\n");
+ else
+ device_printf(dev,
+ "VF queue reset failed; bus mastering remains disabled\n");
+
+ iavf_set_state(&sc->state, IAVF_STATE_DMA_FENCED);
+ atomic_store_rel_32(&sc->queues_enabled, 0);
+ atomic_store_rel_32(&sc->mbx_ready, 0);
+ atomic_store_rel_32(&sc->vc_reinit_required, 1);
+ return (cause != 0 ? cause : EIO);
+}
+
+/*
+ * Wait for a reset which has already started. A completed reset proves the
+ * old queues are stopped; a timeout falls back to fencing PCI DMA.
+ */
+static int
+iavf_wait_for_queue_reset(struct iavf_sc *sc)
+{
+ int error;
+
+ error = iavf_reset_complete(&sc->hw);
+ if (error != 0)
+ return (iavf_fence_dma(sc, error));
+ iavf_reset_queues_stopped(sc);
+ return (EAGAIN);
+}
+
+/*
+ * Quiesce the hardware before iflib releases or rewrites queue resources.
+ * Success means DISABLE_QUEUES was acknowledged and the current AdminQ is
+ * still usable. EAGAIN means a reset stopped the queues but virtchnl must be
+ * rediscovered. DMA_FENCED means PCI bus mastering was verified disabled;
+ * other errors do not prove that queue DMA has stopped.
+ */
+static int
+iavf_quiesce_queues(struct iavf_sc *sc)
+{
+ struct iavf_hw *hw;
+ bool asq_alive;
+ int error;
+
+ hw = &sc->hw;
+ if (iavf_test_state(&sc->state, IAVF_STATE_DMA_FENCED))
+ return (EIO);
+
+ if (iavf_test_state(&sc->state, IAVF_STATE_RESET_RECOVERY)) {
+ if (!iavf_reset_is_complete(hw)) {
+ iavf_hold_vc_polling(sc);
+ return (iavf_wait_for_queue_reset(sc));
+ }
+ atomic_store_rel_32(&sc->queues_enabled, 0);
+ return (EAGAIN);
+ }
+ if (!iavf_reset_is_complete(hw)) {
+ iavf_enter_reset_recovery(sc);
+ iavf_hold_vc_polling(sc);
+ return (iavf_wait_for_queue_reset(sc));
+ }
+ if (atomic_load_acq_32(&sc->queues_enabled) == 0)
+ return (0);
+
+ asq_alive = iavf_check_asq_alive(hw);
+ if (!asq_alive) {
+ /*
+ * A dead AdminQ does not prove that LAN DMA stopped. Rebuild
+ * the virtchnl connection so queue disable or VF reset remains
+ * available before iflib releases the queue resources.
+ */
+ error = iavf_reestablish_vc(sc);
+ if (error == 0) {
+ /* A PF reset may have discarded the VF's filters. */
+ atomic_store_rel_32(&sc->vc_reinit_required, 1);
+ atomic_store_rel_32(&sc->mbx_ready, 1);
+ asq_alive = true;
+ }
+ }
+ if (atomic_load_acq_32(&sc->mbx_ready) != 0 && asq_alive)
+ error = iavf_disable_queues_with_retries(sc);
+ else if (asq_alive)
+ error = ENXIO;
+ if (atomic_load_acq_32(&sc->queues_enabled) == 0)
+ return (0);
+
+ /* The PF might have started its advertised reset while we waited. */
+ if (!iavf_reset_is_complete(hw)) {
+ iavf_enter_reset_recovery(sc);
+ iavf_hold_vc_polling(sc);
+ return (iavf_wait_for_queue_reset(sc));
+ }
+
+ if (iavf_mbx_log_allowed(sc))
+ device_printf(sc->dev,
+ "Queue disable was not confirmed (%d); requesting a VF reset\n",
+ error);
+ iavf_set_state(&sc->state, IAVF_STATE_RESET_PENDING);
+ iavf_hold_vc_polling(sc);
+ error = iavf_request_reset(sc);
+ if (error != 0)
+ return (iavf_fence_dma(sc, error));
+ /* RESET_VF has no reply; allow the PF to begin VFR before polling. */
+ iavf_msec_pause(100);
+ return (iavf_wait_for_queue_reset(sc));
+}
+
static void
iavf_mbx_retry_failed(if_ctx_t ctx)
{
@@ -823,6 +1001,9 @@
}
iflib_init_failed(ctx);
+ /* A verified PCI DMA fence is terminal until the driver is reattached. */
+ if (iavf_test_state(&sc->state, IAVF_STATE_DMA_FENCED))
+ return;
ifp = iflib_get_ifp(ctx);
if (!sc->mbx_retry_initialized ||
(if_getflags(ifp) & IFF_UP) == 0)
@@ -854,6 +1035,8 @@
sc->mbx_last_log.tv_usec = 0;
iavf_clear_state(&sc->state, IAVF_STATE_RESET_REQUIRED);
iavf_clear_state(&sc->state, IAVF_STATE_RESET_PENDING);
+ iavf_clear_state(&sc->state, IAVF_STATE_RESET_RECOVERY);
+ iavf_clear_state(&sc->state, IAVF_STATE_VC_POLLING);
if (recovered)
device_printf(sc->dev, "PF mailbox communication restored\n");
}
@@ -907,10 +1090,9 @@
hw = &sc->hw;
vsi = &sc->vsi;
- iavf_disable_adminq_irq(hw);
- taskqueue_drain(sc->vc_tq, &sc->vc_task);
- /* A task already running when interrupts were masked can re-enable it. */
- iavf_disable_adminq_irq(hw);
+ /* Keep the ordinary AdminQ task from consuming polled replies. */
+ iavf_set_state(&sc->state, IAVF_STATE_RESET_PENDING);
+ iavf_hold_vc_polling(sc);
pci_enable_busmaster(sc->dev);
status = iavf_shutdown_adminq(hw);
@@ -960,12 +1142,18 @@
}
/*
- * RESET_PENDING prevents the ordinary AdminQ task from consuming
- * messages while the queue may still belong to the pre-reset device.
- * The successful VERSION and GET_VF_RESOURCES exchange above proves
- * that the reset has completed and this is the replacement AdminQ.
- * Clear the stale indication before normal virtchnl requests resume.
+ * VC_POLLING prevents the ordinary AdminQ task from consuming the
+ * replies above. The successful exchange proves that the reset has
+ * completed and this is the replacement AdminQ.
+ * If a reset stopped the old queues, consume that fact now that the
+ * replacement AdminQ is established. This permits ordinary AdminQ
+ * processing while making the following quiesce check a no-op.
*/
+ if (iavf_test_state(&sc->state, IAVF_STATE_RESET_RECOVERY)) {
+ atomic_store_rel_32(&sc->queues_enabled, 0);
+ iavf_clear_state(&sc->state, IAVF_STATE_RESET_RECOVERY);
+ }
+ iavf_clear_state(&sc->state, IAVF_STATE_VC_POLLING);
iavf_clear_state(&sc->state, IAVF_STATE_RESET_PENDING);
iavf_enable_adminq_irq(hw);
return (0);
@@ -1018,9 +1206,18 @@
sx_assert(iflib_ctx_lock_get(ctx), SA_XLOCKED);
iavf_mbx_retry_prepare(sc);
+ if (iavf_test_state(&sc->state, IAVF_STATE_DMA_FENCED)) {
+ if (iavf_mbx_log_allowed(sc))
+ device_printf(dev,
+ "DMA remains fenced after a failed VF reset; "
+ "reattach the device to recover\n");
+ iavf_mbx_retry_failed(ctx);
+ return;
+ }
replay_filters = atomic_load_acq_32(&sc->vc_reinit_required) != 0;
if (!iavf_reset_is_complete(hw)) {
+ iavf_enter_reset_recovery(sc);
atomic_store_rel_32(&sc->vc_reinit_required, 1);
if (iavf_mbx_log_allowed(sc))
device_printf(dev,
@@ -1042,10 +1239,12 @@
}
}
- /* Make sure queues are disabled */
- error = iavf_disable_queues_with_retries(sc);
- if (error != 0)
- goto fail;
+ if (!iavf_test_state(&sc->state, IAVF_STATE_RESET_RECOVERY)) {
+ /* Make sure queues are disabled. */
+ error = iavf_quiesce_queues(sc);
+ if (error != 0)
+ goto fail;
+ }
bcopy(if_getlladdr(ifp), tmpaddr, ETHER_ADDR_LEN);
if (!cmp_etheraddr(hw->mac.addr, tmpaddr) &&
@@ -1501,9 +1700,10 @@
int error = 0, loop = 0;
u32 reg;
- if (iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING)) {
- status = IAVF_ERR_ADMIN_QUEUE_ERROR;
- goto reenable_interrupt;
+ if (iavf_test_state(&sc->state, IAVF_STATE_VC_POLLING) ||
+ iavf_test_state(&sc->state, IAVF_STATE_RESET_RECOVERY)) {
+ *pending = 0;
+ return (IAVF_ERR_ADMIN_QUEUE_ERROR);
}
error = iavf_check_aq_errors(sc);
@@ -1686,6 +1886,8 @@
{
struct iavf_sc *sc = iavf_sc_from_ctx(ctx);
struct iavf_hw *hw = &sc->hw;
+ bool asq_alive;
+ bool reset_active;
u32 val;
if (qid != 0)
@@ -1694,16 +1896,25 @@
/* Check for a PF-triggered VF reset or a dead admin send queue. */
val = rd32(hw, IAVF_VFGEN_RSTAT) &
IAVF_VFGEN_RSTAT_VFR_STATE_MASK;
- if (iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING) ||
- !iavf_check_asq_alive(hw) ||
- (val != VIRTCHNL_VFR_VFACTIVE &&
- val != VIRTCHNL_VFR_COMPLETED)) {
+ reset_active = val != VIRTCHNL_VFR_VFACTIVE &&
+ val != VIRTCHNL_VFR_COMPLETED;
+ asq_alive = iavf_check_asq_alive(hw);
+ if (reset_active)
+ iavf_enter_reset_recovery(sc);
+ if (!asq_alive || reset_active) {
iavf_dbg_info(sc, "PF mailbox unavailable (reset state %d)\n",
val);
iavf_mbx_lost(sc);
return;
}
+ /*
+ * RESET_IMPENDING is only a warning. Preserve the running queues until
+ * RSTAT proves that the PF started the reset.
+ */
+ if (iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING))
+ return;
+
/* Fire off the adminq task */
iflib_admin_intr_deferred(ctx);
@@ -2113,6 +2324,7 @@
{
struct iavf_vsi *vsi;
bool mailbox_ready;
+ int error;
vsi = &sc->vsi;
iavf_mbx_retry_stop(sc);
@@ -2120,12 +2332,13 @@
iavf_disable_intr(vsi);
- mailbox_ready = atomic_load_acq_32(&sc->mbx_ready) != 0;
- if (mailbox_ready && iavf_reset_is_complete(&sc->hw) &&
- iavf_disable_queues_with_retries(sc) != 0)
- mailbox_ready = false;
- atomic_store_rel_32(&sc->mbx_ready, 0);
+ error = iavf_quiesce_queues(sc);
+ mailbox_ready = error == 0 &&
+ atomic_load_acq_32(&sc->mbx_ready) != 0 &&
+ !iavf_test_state(&sc->state, IAVF_STATE_RESET_RECOVERY) &&
+ !iavf_test_state(&sc->state, IAVF_STATE_DMA_FENCED);
if (!mailbox_ready) {
+ atomic_store_rel_32(&sc->mbx_ready, 0);
atomic_store_rel_32(&sc->vc_reinit_required, 1);
iavf_dbg_vc(sc, "PF mailbox unavailable while stopping\n");
}

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 19, 10:20 PM (3 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36963673
Default Alt Text
D58904.id184239.diff (16 KB)

Event Timeline