Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167080906
D58904.id184239.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
D58904.id184239.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58904: iavf: Handle queue disable during reset recovery
Attached
Detach File
Event Timeline
Log In to Comment