DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap
@ 2026-08-06  8:26 Anurag Mandal
  2026-08-06  8:26 ` [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset Anurag Mandal
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal

During PF-initiated reset or a remote/ToR switch link-flap, the VF
might miss the reset event, race on the no_poll gate, leak in-flight
Tx descriptors, and stay down if dev_start ran before the PF VSI was
ready.

This patch series build on the earlier reset-recovery fixes with the
following:

- Reset detection: complement the ARQLEN1 check with VFGEN_RSTAT
  (VIRTCHNL_VFR_INPROGRESS) and poll at a 5 ms interval, matching the
  kernel iavf driver, so fast ARQ flips are not missed. When the VFR
  is still not observed, recover anyway instead of bailing out,
  keeping PF and VF state in sync.

- no_poll: make the flag atomic (RTE_ATOMIC) with release/acquire
  ordering so the data-plane lcores observe gate changes reliably.

- Tx drain: add iavf_dev_tx_drain() to flush in-flight Tx descriptors
  on link-down and impending-reset events before teardown, preventing
  MDD events and descriptor leaks.

- Deferred start: when dev_start fails during recovery (PF VSI inactive),
  defer it via start_pending and resume on the next link-up event so the
  VF comes back without manual intervention.

- AdminQ: discard zeroed (opcode 0) descriptors seen during PF-initiated
  resets to avoid the "Request 0 is not supported" log flood.

Anurag Mandal (5):
  net/iavf: discard empty AdminQ descriptors on reset
  net/iavf: defer device start when PF VSI not ready
  net/iavf: drain in-flight Tx before reset
  net/iavf: change no_poll flag to atomic
  net/iavf: improve VF reset detection on fast ARQ flip

 drivers/net/intel/iavf/iavf.h        |   5 +-
 drivers/net/intel/iavf/iavf_ethdev.c | 103 ++++++++++++++++++++----
 drivers/net/intel/iavf/iavf_rxtx.c   | 112 ++++++++++++++++++++++++++-
 drivers/net/intel/iavf/iavf_rxtx.h   |   6 ++
 drivers/net/intel/iavf/iavf_vchnl.c  |  40 +++++++++-
 5 files changed, 246 insertions(+), 20 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset
  2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
@ 2026-08-06  8:26 ` Anurag Mandal
  2026-08-07 10:10   ` Loftus, Ciara
  2026-08-06  8:26 ` [PATCH 2/5] net/iavf: defer device start when PF VSI not ready Anurag Mandal
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal

During PF-initiated resets iavf_clean_arq_element() has been observed
to return success with a fully zeroed descriptor (opcode 0).
These fell through to the default arm of the dispatch switch and
produced a "Request 0 is not supported yet" log flood in the field.

Skip descriptors with opcode 0 so they are silently discarded and do not
flood log.

Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
 drivers/net/intel/iavf/iavf_vchnl.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index f346837bf1..56918ebcc1 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -614,6 +614,19 @@ iavf_handle_virtchnl_msg(struct rte_eth_dev *dev)
 			break;
 		}
 		aq_opc = rte_le_to_cpu_16(info.desc.opcode);
+
+		/*
+		 * During PF-initiated resets, iavf_clean_arq_element() has
+		 * been observed to return IAVF_SUCCESS with a fully zeroed
+		 * descriptor (opcode 0).
+		 * Without this guard, such descriptors would fall through
+		 * to the default case of the dispatch switch below and the
+		 * "Request 0 is not supported yet" log flood reported in
+		 * the field would be produced. These are discarded now.
+		 */
+		if (aq_opc == 0)
+			continue;
+
 		/* For the message sent from pf to vf, opcode is stored in
 		 * cookie_high of struct iavf_aq_desc, while return error code
 		 * are stored in cookie_low, Which is done by PF driver.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/5] net/iavf: defer device start when PF VSI not ready
  2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
  2026-08-06  8:26 ` [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset Anurag Mandal
@ 2026-08-06  8:26 ` Anurag Mandal
  2026-08-07 10:46   ` Loftus, Ciara
  2026-08-06  8:26 ` [PATCH 3/5] net/iavf: drain in-flight Tx before reset Anurag Mandal
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal

During reset recovery iavf_dev_start() might fail (typically -EIO
from VIRTCHNL_OP_CONFIG_VSI_QUEUES) because the PF VSI is not yet
active, leaving the VF down and requiring manual intervention
to recover.

Added a start_pending flag: when device start fails during recovery,
defer it instead of erroring out and resume it from newly added
iavf_resume_pending_start() on the next link-up event, so the
VF comes back automatically.

Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
 drivers/net/intel/iavf/iavf.h        |  2 +
 drivers/net/intel/iavf/iavf_ethdev.c | 56 ++++++++++++++++++++++++++--
 drivers/net/intel/iavf/iavf_vchnl.c  | 16 +++++++-
 3 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 293adaf6c9..e76c3bb410 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -293,6 +293,7 @@ struct iavf_info {
 	bool in_reset_recovery;
 	bool reset_pending;
 	bool pf_reset_in_progress;
+	bool start_pending;
 
 	uint32_t ptp_caps;
 	rte_spinlock_t phc_time_aq_lock;
@@ -533,4 +534,5 @@ void iavf_handle_hw_reset(struct rte_eth_dev *dev, bool vf_initiated_reset);
 void iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change);
 bool is_iavf_supported(struct rte_eth_dev *dev);
 void iavf_hash_uninit(struct iavf_adapter *ad);
+void iavf_resume_pending_start(struct rte_eth_dev *dev);
 #endif /* _IAVF_ETHDEV_H_ */
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index e475b64971..87b826c873 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -1093,6 +1093,9 @@ iavf_dev_start(struct rte_eth_dev *dev)
 
 	iavf_phc_sync_alarm_start(dev);
 
+	/* An explicit start supersedes any pending deferred start */
+	vf->start_pending = false;
+
 	return 0;
 
 error:
@@ -1131,6 +1134,9 @@ iavf_dev_stop(struct rte_eth_dev *dev)
 	adapter->stopped = 1;
 	dev->data->dev_started = 0;
 
+	/* An explicit stop cancels any pending deferred start */
+	vf->start_pending = false;
+
 	return 0;
 }
 
@@ -3425,6 +3431,7 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev, bool vf_initiated_reset)
 
 	vf->in_reset_recovery = true;
 	vf->pf_reset_in_progress = !vf_initiated_reset;
+	vf->start_pending = false;
 	iavf_set_no_poll(adapter, false);
 
 	/* Call the pre reset callback */
@@ -3445,10 +3452,17 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev, bool vf_initiated_reset)
 	if (!vf_initiated_reset || restart_device) {
 		/* start the device */
 		ret = iavf_dev_start(dev);
-		if (ret)
-			goto error;
-
-		dev->data->dev_started = 1;
+		if (ret == 0) {
+			dev->data->dev_started = 1;
+		} else {
+			PMD_DRV_LOG(WARNING,
+				    "dev_start failed during reset recovery (rc=%d);"
+				    "deferring to next link-up event",
+				    ret);
+			vf->start_pending = true;
+			dev->data->dev_started = 0;
+			ret = 0;
+		}
 	}
 
 	/* Restore settings after the reset */
@@ -3662,6 +3676,40 @@ bool is_iavf_supported(struct rte_eth_dev *dev)
 	return !strcmp(dev->device->driver->name, rte_iavf_pmd.driver.name);
 }
 
+void
+iavf_resume_pending_start(struct rte_eth_dev *dev)
+{
+	struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+	int ret;
+
+	if (!vf->start_pending)
+		return;
+	/*
+	 * If the application has already (re)started the port itself, the
+	 * deferred start is stale, the application's action is honoured
+	 * and resume pending is dropped to avoid starting an
+	 * already-running port a second time.
+	 */
+	if (dev->data->dev_started) {
+		vf->start_pending = false;
+		return;
+	}
+
+	if (!vf->link_up)
+		return;
+
+	PMD_DRV_LOG(DEBUG, "PF link back up; resuming deferred dev_start");
+	ret = iavf_dev_start(dev);
+	if (ret == 0) {
+		dev->data->dev_started = 1;
+		vf->start_pending = false;
+	} else {
+		PMD_DRV_LOG(ERR,
+			    "deferred dev_start failed (ret=%d); will retry on next link-up",
+			    ret);
+	}
+}
+
 RTE_PMD_REGISTER_PCI(net_iavf, rte_iavf_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(net_iavf, pci_id_iavf_map);
 RTE_PMD_REGISTER_KMOD_DEP(net_iavf, "* igb_uio | vfio-pci");
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 56918ebcc1..8e102b02aa 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -260,7 +260,7 @@ iavf_handle_link_change_event(struct rte_eth_dev *dev,
 	 * (link is down or a VF reset is in progress); the watchdog drives
 	 * auto-reset recovery, so it must remain armed in those cases.
 	 */
-	if (vf->link_up && !vf->vf_reset)
+	if (vf->link_up && !vf->vf_reset && !vf->in_reset_recovery)
 		iavf_dev_watchdog_disable(adapter);
 	else
 		iavf_dev_watchdog_enable(adapter);
@@ -271,6 +271,20 @@ iavf_handle_link_change_event(struct rte_eth_dev *dev,
 			    adapter->no_poll ? "on" : "off");
 	}
 
+	/*
+	 * Resume a deferred dev_start.
+	 * iavf_handle_hw_reset() sets vf->start_pending when
+	 * reset recovery completed dev_init() but iavf_dev_start()
+	 * itself failed (typically -EIO from VIRTCHNL_OP_CONFIG_VSI_QUEUES
+	 * when the PF VSI was inactive).
+	 * A link-up event implies the PF VSI is active again, so retry now.
+	 * Run before the LSC event post so the port is ready to accept Tx
+	 * by the time the app's link-up callback fires; no_poll has already
+	 * been cleared above so bursts go through as soon as
+	 * dev_start sets dev_started=1.
+	 */
+	iavf_resume_pending_start(dev);
+
 	iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_LSC, NULL, 0);
 
 	PMD_DRV_LOG(INFO, "Link status update:%s",
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/5] net/iavf: drain in-flight Tx before reset
  2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
  2026-08-06  8:26 ` [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset Anurag Mandal
  2026-08-06  8:26 ` [PATCH 2/5] net/iavf: defer device start when PF VSI not ready Anurag Mandal
@ 2026-08-06  8:26 ` Anurag Mandal
  2026-08-07 10:51   ` Loftus, Ciara
  2026-08-06  8:26 ` [PATCH 4/5] net/iavf: change no_poll flag to atomic Anurag Mandal
  2026-08-06  8:26 ` [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip Anurag Mandal
  4 siblings, 1 reply; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal

On a link-down or impending PF reset, in-flight Tx descriptors
were left pending when the queues were torn down, which could
trigger Malicious Driver Detection (MDD) events and
leak descriptors.

Added iavf_dev_tx_drain() to let already-posted Tx bursts
complete and flush the rings within a bounded budget,
and call it on link-down and reset-impending events
before teardown, preventing MDD events and descriptor leaks.
The drain selects the cleanup routine that matches the
active Tx path: the scalar path uses ci_tx_xmit_cleanup(),
while the vector and CTX paths use ci_tx_free_bufs_vec().
This matters because the scalar and vector paths track
their software rings differently
(ci_tx_entry vs ci_tx_entry_vec) and using the scalar
routine on a vector queue would walk the wrong ring
and free the wrong mbufs.

Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
 drivers/net/intel/iavf/iavf_rxtx.c  | 104 ++++++++++++++++++++++++++++
 drivers/net/intel/iavf/iavf_rxtx.h  |   6 ++
 drivers/net/intel/iavf/iavf_vchnl.c |   7 ++
 3 files changed, 117 insertions(+)

diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 4f2ffe6188..931bb8420d 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -32,6 +32,7 @@
 
 #include "iavf.h"
 #include "iavf_rxtx.h"
+#include "iavf_rxtx_vec_common.h"
 #include "iavf_ipsec_crypto.h"
 #include "rte_pmd_iavf.h"
 
@@ -4025,6 +4026,109 @@ iavf_tx_done_cleanup_full(struct ci_tx_queue *txq,
 	return (int)pkt_cnt;
 }
 
+/*
+ * Reclaim completed Tx descriptors for a single queue using the cleanup
+ * routine that matches the active Tx path.
+ * The scalar and vector paths track their software rings differently
+ * (ci_tx_entry vs ci_tx_entry_vec) and keep separate completion
+ * bookkeeping, so using the scalar routine on a vector queue
+ * (or vice versa) would free the wrong mbufs.
+ * Returns true if any descriptors were reclaimed.
+ */
+static bool
+iavf_tx_drain_cleanup(struct ci_tx_queue *txq,
+		      enum iavf_tx_func_type tx_func_type)
+{
+	switch (tx_func_type) {
+	case IAVF_TX_AVX2_CTX:
+	case IAVF_TX_AVX2_CTX_OFFLOAD:
+	case IAVF_TX_AVX512_CTX:
+	case IAVF_TX_AVX512_CTX_OFFLOAD:
+		return ci_tx_free_bufs_vec(txq, iavf_tx_desc_done, true) != 0;
+	case IAVF_TX_NEON:
+	case IAVF_TX_AVX2:
+	case IAVF_TX_AVX2_OFFLOAD:
+	case IAVF_TX_AVX512:
+	case IAVF_TX_AVX512_OFFLOAD:
+		return ci_tx_free_bufs_vec(txq, iavf_tx_desc_done, false) != 0;
+	case IAVF_TX_DEFAULT:
+	default:
+		return ci_tx_xmit_cleanup(txq) == 0;
+	}
+}
+
+/*
+ * iavf_dev_tx_drain - drain in-flight Tx descriptors after a link-down or
+ * impending PF reset event.
+ */
+void
+iavf_dev_tx_drain(struct rte_eth_dev *dev)
+{
+	struct iavf_adapter *adapter =
+		IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	enum iavf_tx_func_type tx_func_type = adapter->tx_func_type;
+	struct ci_tx_queue *txq;
+	uint64_t hz, deadline;
+	int idle_iters = 0;
+	uint16_t qid;
+
+	/*
+	 * Allow any Tx burst already in flight on a data-plane lcore to
+	 * write its remaining descriptors and notify. After
+	 * this window, the no_poll gate set by the caller is observed at
+	 * the next burst-entry and no new descriptors will be posted.
+	 */
+	rte_delay_us_block(IAVF_TX_DRAIN_SETTLE_US);
+
+	hz = rte_get_timer_hz();
+	deadline = rte_get_timer_cycles() +
+		(hz * IAVF_TX_DRAIN_TIMEOUT_US) / 1000000ULL;
+
+	while (rte_get_timer_cycles() < deadline) {
+		bool any_pending = false;
+		bool any_progress = false;
+
+		for (qid = 0; qid < dev->data->nb_tx_queues; qid++) {
+			txq = dev->data->tx_queues[qid];
+			if (txq == NULL ||
+			    dev->data->tx_queue_state[qid] !=
+				RTE_ETH_QUEUE_STATE_STARTED)
+				continue;
+
+			/*
+			 * nb_tx_free == nb_tx_desc - 1 means the ring is
+			 * empty (one descriptor is always reserved).
+			 */
+			if (txq->nb_tx_free >= txq->nb_tx_desc - 1)
+				continue;
+
+			any_pending = true;
+			if (iavf_tx_drain_cleanup(txq, tx_func_type))
+				any_progress = true;
+		}
+
+		if (!any_pending)
+			return;
+
+		if (any_progress) {
+			idle_iters = 0;
+		} else if (++idle_iters >= IAVF_TX_DRAIN_IDLE_MAX) {
+			/*
+			 * HW has not advanced the RS-bit write-back for
+			 * several polling intervals; either the queue is
+			 * quiescent except for the sub-rs_thresh tail
+			 * (which we cannot observe here) or HW is no
+			 * longer fetching. Further polling is unlikely to
+			 * help, and the PF teardown path has its own
+			 * grace period for the remainder.
+			 */
+			break;
+		}
+
+		rte_delay_us_block(IAVF_TX_DRAIN_POLL_US);
+	}
+}
+
 int
 iavf_dev_tx_done_cleanup(void *txq, uint32_t free_cnt)
 {
diff --git a/drivers/net/intel/iavf/iavf_rxtx.h b/drivers/net/intel/iavf/iavf_rxtx.h
index 22ea415f44..4088bc421c 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.h
+++ b/drivers/net/intel/iavf/iavf_rxtx.h
@@ -506,6 +506,11 @@ enum iavf_tx_ctx_desc_tunnel_l4_tunnel_type {
 /* Valid indicator bit for the time_stamp_low field */
 #define IAVF_RX_FLX_DESC_TS_VALID	(0x1UL)
 
+#define IAVF_TX_DRAIN_TIMEOUT_US	10000	/* total drain budget: 10 ms */
+#define IAVF_TX_DRAIN_SETTLE_US		100	/* let in-flight burst land  */
+#define IAVF_TX_DRAIN_POLL_US		50	/* poll interval             */
+#define IAVF_TX_DRAIN_IDLE_MAX		20	/* ~1 ms of no RS write-back */
+
 int iavf_dev_rx_queue_setup(struct rte_eth_dev *dev,
 			   uint16_t queue_idx,
 			   uint16_t nb_desc,
@@ -641,6 +646,7 @@ void iavf_set_default_ptype_table(struct rte_eth_dev *dev);
 void iavf_rx_queue_release_mbufs_vec(struct ci_rx_queue *rxq);
 void iavf_rx_queue_release_mbufs_neon(struct ci_rx_queue *rxq);
 enum rte_vect_max_simd iavf_get_max_simd_bitwidth(void);
+void iavf_dev_tx_drain(struct rte_eth_dev *dev);
 
 static inline
 void iavf_dump_rx_descriptor(struct ci_rx_queue *rxq,
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 8e102b02aa..d22990a524 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -269,6 +269,8 @@ iavf_handle_link_change_event(struct rte_eth_dev *dev,
 		iavf_set_no_poll(adapter, true);
 		PMD_DRV_LOG(DEBUG, "VF no poll turned %s",
 			    adapter->no_poll ? "on" : "off");
+		if (!vf->link_up)
+			iavf_dev_tx_drain(dev);
 	}
 
 	/*
@@ -341,6 +343,8 @@ iavf_read_msg_from_pf(struct iavf_adapter *adapter, uint16_t buf_len,
 			if (!vf->vf_reset) {
 				vf->vf_reset = true;
 				iavf_set_no_poll(adapter, false);
+				if (adapter->devargs.no_poll_on_link_down)
+					iavf_dev_tx_drain(vf->eth_dev);
 				iavf_dev_event_post(vf->eth_dev,
 					RTE_ETH_EVENT_INTR_RESET,
 					NULL, 0);
@@ -579,6 +583,9 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
 		if (!vf->vf_reset) {
 			vf->vf_reset = true;
 			iavf_set_no_poll(adapter, false);
+			iavf_dev_watchdog_enable(adapter);
+			if (adapter->devargs.no_poll_on_link_down)
+				iavf_dev_tx_drain(dev);
 			iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET,
 				NULL, 0);
 		}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/5] net/iavf: change no_poll flag to atomic
  2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
                   ` (2 preceding siblings ...)
  2026-08-06  8:26 ` [PATCH 3/5] net/iavf: drain in-flight Tx before reset Anurag Mandal
@ 2026-08-06  8:26 ` Anurag Mandal
  2026-08-07 10:56   ` Loftus, Ciara
  2026-08-06  8:26 ` [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip Anurag Mandal
  4 siblings, 1 reply; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal,
	stable

The no_poll gate that pauses the Rx/Tx data path during reset and
link-down was a plain bool written on the control path and read on
the data-plane lcores without synchronization, allowing stale reads
that either keep dropping traffic or trigger spurious reset detection.

Made no_poll an RTE_ATOMIC(bool) and access it with release stores and
acquire loads so data-plane lcores reliably observe gate changes.

Fixes: 5b3124a0a6ef ("net/iavf: support no polling when link down")
Cc: stable@dpdk.org

Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
 drivers/net/intel/iavf/iavf.h        | 2 +-
 drivers/net/intel/iavf/iavf_ethdev.c | 6 +++++-
 drivers/net/intel/iavf/iavf_rxtx.c   | 8 ++++++--
 drivers/net/intel/iavf/iavf_vchnl.c  | 4 +++-
 4 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index e76c3bb410..037bc8436f 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -392,7 +392,7 @@ struct iavf_adapter {
 	alignas(RTE_CACHE_LINE_MIN_SIZE) uint32_t ptype_tbl[IAVF_MAX_PKT_TYPE];
 	bool stopped;
 	bool closed;
-	bool no_poll;
+	RTE_ATOMIC(bool)no_poll;
 	enum iavf_rx_func_type rx_func_type;
 	enum iavf_tx_func_type tx_func_type;
 	uint16_t fdir_ref_cnt;
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 87b826c873..f6ce339b0c 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -3604,9 +3604,13 @@ void
 iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change)
 {
 	struct iavf_info *vf = &adapter->vf;
+	bool no_poll;
 
-	adapter->no_poll = (link_change & !vf->link_up) ||
+	no_poll = (link_change & !vf->link_up) ||
 		vf->vf_reset || vf->in_reset_recovery;
+
+	rte_atomic_store_explicit(&adapter->no_poll, no_poll,
+				  rte_memory_order_release);
 }
 
 static int
diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 931bb8420d..104197d082 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -3723,7 +3723,9 @@ iavf_recv_pkts_no_poll(void *rx_queue, struct rte_mbuf **rx_pkts,
 	struct ci_rx_queue *rxq = rx_queue;
 	enum iavf_rx_func_type rx_func_type;
 
-	if (!rxq->iavf_vsi || rxq->iavf_vsi->adapter->no_poll)
+	if (!rxq->iavf_vsi ||
+	    rte_atomic_load_explicit(&rxq->iavf_vsi->adapter->no_poll,
+				     rte_memory_order_acquire))
 		return 0;
 
 	rx_func_type = rxq->iavf_vsi->adapter->rx_func_type;
@@ -3739,7 +3741,9 @@ iavf_xmit_pkts_no_poll(void *tx_queue, struct rte_mbuf **tx_pkts,
 	struct ci_tx_queue *txq = tx_queue;
 	enum iavf_tx_func_type tx_func_type;
 
-	if (!txq->iavf_vsi || txq->iavf_vsi->adapter->no_poll)
+	if (!txq->iavf_vsi ||
+	    rte_atomic_load_explicit(&txq->iavf_vsi->adapter->no_poll,
+				     rte_memory_order_acquire))
 		return 0;
 
 	tx_func_type = txq->iavf_vsi->adapter->tx_func_type;
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index d22990a524..dee76f97cb 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -268,7 +268,9 @@ iavf_handle_link_change_event(struct rte_eth_dev *dev,
 	if (adapter->devargs.no_poll_on_link_down) {
 		iavf_set_no_poll(adapter, true);
 		PMD_DRV_LOG(DEBUG, "VF no poll turned %s",
-			    adapter->no_poll ? "on" : "off");
+			    rte_atomic_load_explicit(&adapter->no_poll,
+						     rte_memory_order_relaxed) ?
+						     "on" : "off");
 		if (!vf->link_up)
 			iavf_dev_tx_drain(dev);
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip
  2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
                   ` (3 preceding siblings ...)
  2026-08-06  8:26 ` [PATCH 4/5] net/iavf: change no_poll flag to atomic Anurag Mandal
@ 2026-08-06  8:26 ` Anurag Mandal
  2026-08-07 11:08   ` Loftus, Ciara
  4 siblings, 1 reply; 11+ messages in thread
From: Anurag Mandal @ 2026-08-06  8:26 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, vladimir.medvedkin, ciara.loftus, Anurag Mandal,
	stable

During PF-initiated reset or a remote/ToR switch link-flap,
the PF toggles the admin receive queue enable bit (ARQLEN1)
so quickly around a VF reset that the VF's sampling window
misses it, leaving the PF and the VF states out of sync
and the data path stalled.

Complement the ARQLEN1 check with VFGEN_RSTAT
(VIRTCHNL_VFR_INPROGRESS) and shorten the poll interval to
5 ms (with a proportionally larger count, keeping the ~10 s
budget), matching the Linux kernel iavf driver.
When the VFR is still not observed, proceed with recovery
instead of bailing out so the PF and  the VF states converge.

Fixes: ada64daa1a5b ("net/iavf: fix VF reset for flow director rule")
Fixes: 80fb3c920458 ("net/iavf: fix crash on VF start")
Cc: stable@dpdk.org

Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
---
 drivers/net/intel/iavf/iavf.h        |  1 +
 drivers/net/intel/iavf/iavf_ethdev.c | 41 +++++++++++++++++++++-------
 2 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 037bc8436f..5e49e81447 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -21,6 +21,7 @@
 #define IAVF_AQ_BUF_SZ            4096
 #define IAVF_RESET_WAIT_CNT       2000
 #define IAVF_RESET_DETECTED_CNT   500
+#define IAVF_RESET_POLL_SCALE     4  /* Poll-interval scale for reset detection */
 #define IAVF_BUF_SIZE_MIN         1024
 #define IAVF_FRAME_SIZE_MAX       9728
 #define IAVF_QUEUE_BASE_ADDR_UNIT 128
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index f6ce339b0c..21bac51467 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -3235,7 +3235,9 @@ iavf_dev_close(struct rte_eth_dev *dev)
 	/* remove RSS configuration */
 	iavf_hash_uninit(adapter);
 
-	iavf_flow_flush(dev, NULL);
+	/* Skip the virtchnl-emitting teardown on a PF-initiated reset */
+	if (!vf->pf_reset_in_progress)
+		iavf_flow_flush(dev, NULL);
 	iavf_flow_uninit(adapter);
 
 	/*
@@ -3358,8 +3360,26 @@ iavf_dev_reset(struct rte_eth_dev *dev)
 static inline bool
 iavf_is_reset(struct iavf_hw *hw)
 {
-	return !(IAVF_READ_REG(hw, IAVF_VF_ARQLEN1) &
-		IAVF_VF_ARQLEN1_ARQENABLE_MASK);
+	uint32_t rstat;
+
+	/* ARQ has been disabled by the PF as part of the VFR. */
+	if (!(IAVF_READ_REG(hw, IAVF_VF_ARQLEN1) &
+		IAVF_VF_ARQLEN1_ARQENABLE_MASK))
+		return true;
+
+	/*
+	 * VFGEN_RSTAT reports VIRTCHNL_VFR_INPROGRESS.
+	 * At times, the PF flips ARQENABLE so quickly
+	 * around a VFR that the ARQLEN1 sample window
+	 * misses it. Using VFGEN_RSTAT, as a
+	 * complementary indicator, prevents from
+	 * missing a reset that really did happen.
+	 */
+	rstat = (IAVF_READ_REG(hw, IAVF_VFGEN_RSTAT) &
+		 IAVF_VFGEN_RSTAT_VFR_STATE_MASK) >>
+		IAVF_VFGEN_RSTAT_VFR_STATE_SHIFT;
+
+	return rstat == VIRTCHNL_VFR_INPROGRESS;
 }
 
 static bool
@@ -3368,11 +3388,14 @@ iavf_is_reset_detected(struct iavf_adapter *adapter)
 	struct iavf_hw *hw = IAVF_DEV_PRIVATE_TO_HW(adapter);
 	int i;
 
-	/* poll until we see the reset actually happen */
-	for (i = 0; i < IAVF_RESET_DETECTED_CNT; i++) {
+	/*
+	 * Poll until the reset actually happen.
+	 * Poll every 5 ms to catch the fast ARQ flips.
+	 */
+	for (i = 0; i < IAVF_RESET_DETECTED_CNT * IAVF_RESET_POLL_SCALE; i++) {
 		if (iavf_is_reset(hw))
 			return true;
-		rte_delay_ms(20);
+		rte_delay_us(5000);
 	}
 
 	return false;
@@ -3423,10 +3446,8 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev, bool vf_initiated_reset)
 		if (!dev->data->dev_started)
 			return;
 
-		if (!iavf_is_reset_detected(adapter)) {
-			PMD_DRV_LOG(DEBUG, "reset not start");
-			return;
-		}
+		if (!iavf_is_reset_detected(adapter))
+			PMD_DRV_LOG(WARNING, "VFR not observed; recovering anyway");
 	}
 
 	vf->in_reset_recovery = true;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* RE: [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset
  2026-08-06  8:26 ` [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset Anurag Mandal
@ 2026-08-07 10:10   ` Loftus, Ciara
  0 siblings, 0 replies; 11+ messages in thread
From: Loftus, Ciara @ 2026-08-07 10:10 UTC (permalink / raw)
  To: Mandal, Anurag, dev@dpdk.org; +Cc: Richardson, Bruce, Medvedkin, Vladimir

> Subject: [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset
> 
> During PF-initiated resets iavf_clean_arq_element() has been observed
> to return success with a fully zeroed descriptor (opcode 0).
> These fell through to the default arm of the dispatch switch and
> produced a "Request 0 is not supported yet" log flood in the field.
> 
> Skip descriptors with opcode 0 so they are silently discarded and do not
> flood log.
> 
> Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
> ---
>  drivers/net/intel/iavf/iavf_vchnl.c | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/drivers/net/intel/iavf/iavf_vchnl.c
> b/drivers/net/intel/iavf/iavf_vchnl.c
> index f346837bf1..56918ebcc1 100644
> --- a/drivers/net/intel/iavf/iavf_vchnl.c
> +++ b/drivers/net/intel/iavf/iavf_vchnl.c
> @@ -614,6 +614,19 @@ iavf_handle_virtchnl_msg(struct rte_eth_dev *dev)
>  			break;
>  		}
>  		aq_opc = rte_le_to_cpu_16(info.desc.opcode);
> +
> +		/*
> +		 * During PF-initiated resets, iavf_clean_arq_element() has
> +		 * been observed to return IAVF_SUCCESS with a fully zeroed
> +		 * descriptor (opcode 0).
> +		 * Without this guard, such descriptors would fall through
> +		 * to the default case of the dispatch switch below and the
> +		 * "Request 0 is not supported yet" log flood reported in
> +		 * the field would be produced. These are discarded now.
> +		 */

This comment is too lengthy. This information is captured appropriately in
the commit message.

Something like "opcode 0 means the descriptor is empty so it can be
skipped" or similar. Assuming an opcode of zero always means an
empty/zeroed descriptor?

> +		if (aq_opc == 0)
> +			continue;
> +
>  		/* For the message sent from pf to vf, opcode is stored in
>  		 * cookie_high of struct iavf_aq_desc, while return error code
>  		 * are stored in cookie_low, Which is done by PF driver.
> --
> 2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH 2/5] net/iavf: defer device start when PF VSI not ready
  2026-08-06  8:26 ` [PATCH 2/5] net/iavf: defer device start when PF VSI not ready Anurag Mandal
@ 2026-08-07 10:46   ` Loftus, Ciara
  0 siblings, 0 replies; 11+ messages in thread
From: Loftus, Ciara @ 2026-08-07 10:46 UTC (permalink / raw)
  To: Mandal, Anurag, dev@dpdk.org; +Cc: Richardson, Bruce, Medvedkin, Vladimir

> Subject: [PATCH 2/5] net/iavf: defer device start when PF VSI not ready
> 
> During reset recovery iavf_dev_start() might fail (typically -EIO
> from VIRTCHNL_OP_CONFIG_VSI_QUEUES) because the PF VSI is not yet
> active, leaving the VF down and requiring manual intervention
> to recover.
> 
> Added a start_pending flag: when device start fails during recovery,
> defer it instead of erroring out and resume it from newly added
> iavf_resume_pending_start() on the next link-up event, so the
> VF comes back automatically.

It sounds like a fix, can you add a Fixes tag?

> 
> Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
> ---
>  drivers/net/intel/iavf/iavf.h        |  2 +
>  drivers/net/intel/iavf/iavf_ethdev.c | 56 ++++++++++++++++++++++++++--
>  drivers/net/intel/iavf/iavf_vchnl.c  | 16 +++++++-
>  3 files changed, 69 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
> index 293adaf6c9..e76c3bb410 100644
> --- a/drivers/net/intel/iavf/iavf.h
> +++ b/drivers/net/intel/iavf/iavf.h
> @@ -293,6 +293,7 @@ struct iavf_info {
>  	bool in_reset_recovery;
>  	bool reset_pending;
>  	bool pf_reset_in_progress;
> +	bool start_pending;
> 
>  	uint32_t ptp_caps;
>  	rte_spinlock_t phc_time_aq_lock;
> @@ -533,4 +534,5 @@ void iavf_handle_hw_reset(struct rte_eth_dev *dev,
> bool vf_initiated_reset);
>  void iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change);
>  bool is_iavf_supported(struct rte_eth_dev *dev);
>  void iavf_hash_uninit(struct iavf_adapter *ad);
> +void iavf_resume_pending_start(struct rte_eth_dev *dev);
>  #endif /* _IAVF_ETHDEV_H_ */
> diff --git a/drivers/net/intel/iavf/iavf_ethdev.c
> b/drivers/net/intel/iavf/iavf_ethdev.c
> index e475b64971..87b826c873 100644
> --- a/drivers/net/intel/iavf/iavf_ethdev.c
> +++ b/drivers/net/intel/iavf/iavf_ethdev.c
> @@ -1093,6 +1093,9 @@ iavf_dev_start(struct rte_eth_dev *dev)
> 
>  	iavf_phc_sync_alarm_start(dev);
> 
> +	/* An explicit start supersedes any pending deferred start */
> +	vf->start_pending = false;
> +
>  	return 0;
> 
>  error:
> @@ -1131,6 +1134,9 @@ iavf_dev_stop(struct rte_eth_dev *dev)
>  	adapter->stopped = 1;
>  	dev->data->dev_started = 0;
> 
> +	/* An explicit stop cancels any pending deferred start */
> +	vf->start_pending = false;
> +
>  	return 0;
>  }
> 
> @@ -3425,6 +3431,7 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev,
> bool vf_initiated_reset)
> 
>  	vf->in_reset_recovery = true;
>  	vf->pf_reset_in_progress = !vf_initiated_reset;
> +	vf->start_pending = false;
>  	iavf_set_no_poll(adapter, false);
> 
>  	/* Call the pre reset callback */
> @@ -3445,10 +3452,17 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev,
> bool vf_initiated_reset)
>  	if (!vf_initiated_reset || restart_device) {
>  		/* start the device */
>  		ret = iavf_dev_start(dev);
> -		if (ret)
> -			goto error;
> -
> -		dev->data->dev_started = 1;
> +		if (ret == 0) {
> +			dev->data->dev_started = 1;
> +		} else {
> +			PMD_DRV_LOG(WARNING,
> +				    "dev_start failed during reset recovery
> (rc=%d);"
> +				    "deferring to next link-up event",
> +				    ret);
> +			vf->start_pending = true;
> +			dev->data->dev_started = 0;
> +			ret = 0;
> +		}
>  	}
> 
>  	/* Restore settings after the reset */
> @@ -3662,6 +3676,40 @@ bool is_iavf_supported(struct rte_eth_dev *dev)
>  	return !strcmp(dev->device->driver->name,
> rte_iavf_pmd.driver.name);
>  }
> 
> +void
> +iavf_resume_pending_start(struct rte_eth_dev *dev)
> +{
> +	struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(dev->data-
> >dev_private);
> +	int ret;
> +
> +	if (!vf->start_pending)
> +		return;
> +	/*
> +	 * If the application has already (re)started the port itself, the
> +	 * deferred start is stale, the application's action is honoured
> +	 * and resume pending is dropped to avoid starting an
> +	 * already-running port a second time.
> +	 */
> +	if (dev->data->dev_started) {
> +		vf->start_pending = false;
> +		return;
> +	}
> +
> +	if (!vf->link_up)
> +		return;
> +
> +	PMD_DRV_LOG(DEBUG, "PF link back up; resuming deferred
> dev_start");
> +	ret = iavf_dev_start(dev);

This call to iavf_dev_start could result in another nested call to
iavf_resume_pending_start which would be problematic.
iavf_dev_start polls the ARQ eg.

iavf_dev_start -> iavf_configure_queues -> iavf_execute_vf_cmd_safe ->
iavf_wait_for_msg -> iavf_read_msg_from_pf -> iavf_clean_arq_element

If another LSC happens during this time, you could enter
iavf_handle_link_change_event again and end up calling
iavf_resume_pending_start again. It can maybe be prevented by clearing the
pending flag at the beginning of iavf_resume_pending_start and setting it
again if the start fails.

> +	if (ret == 0) {
> +		dev->data->dev_started = 1;
> +		vf->start_pending = false;
> +	} else {
> +		PMD_DRV_LOG(ERR,
> +			    "deferred dev_start failed (ret=%d); will retry on
> next link-up",
> +			    ret);
> +	}
> +}
> +
>  RTE_PMD_REGISTER_PCI(net_iavf, rte_iavf_pmd);
>  RTE_PMD_REGISTER_PCI_TABLE(net_iavf, pci_id_iavf_map);
>  RTE_PMD_REGISTER_KMOD_DEP(net_iavf, "* igb_uio | vfio-pci");
> diff --git a/drivers/net/intel/iavf/iavf_vchnl.c
> b/drivers/net/intel/iavf/iavf_vchnl.c
> index 56918ebcc1..8e102b02aa 100644
> --- a/drivers/net/intel/iavf/iavf_vchnl.c
> +++ b/drivers/net/intel/iavf/iavf_vchnl.c
> @@ -260,7 +260,7 @@ iavf_handle_link_change_event(struct rte_eth_dev
> *dev,
>  	 * (link is down or a VF reset is in progress); the watchdog drives
>  	 * auto-reset recovery, so it must remain armed in those cases.
>  	 */
> -	if (vf->link_up && !vf->vf_reset)
> +	if (vf->link_up && !vf->vf_reset && !vf->in_reset_recovery)

Is this change relevant to the rest of this patch?

>  		iavf_dev_watchdog_disable(adapter);
>  	else
>  		iavf_dev_watchdog_enable(adapter);
> @@ -271,6 +271,20 @@ iavf_handle_link_change_event(struct rte_eth_dev
> *dev,
>  			    adapter->no_poll ? "on" : "off");
>  	}
> 
> +	/*
> +	 * Resume a deferred dev_start.
> +	 * iavf_handle_hw_reset() sets vf->start_pending when
> +	 * reset recovery completed dev_init() but iavf_dev_start()
> +	 * itself failed (typically -EIO from
> VIRTCHNL_OP_CONFIG_VSI_QUEUES
> +	 * when the PF VSI was inactive).
> +	 * A link-up event implies the PF VSI is active again, so retry now.
> +	 * Run before the LSC event post so the port is ready to accept Tx
> +	 * by the time the app's link-up callback fires; no_poll has already
> +	 * been cleared above so bursts go through as soon as
> +	 * dev_start sets dev_started=1.
> +	 */

I think this comment is verbose, consider shortening it.

> +	iavf_resume_pending_start(dev);
> +
>  	iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_LSC, NULL, 0);
> 
>  	PMD_DRV_LOG(INFO, "Link status update:%s",
> --
> 2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH 3/5] net/iavf: drain in-flight Tx before reset
  2026-08-06  8:26 ` [PATCH 3/5] net/iavf: drain in-flight Tx before reset Anurag Mandal
@ 2026-08-07 10:51   ` Loftus, Ciara
  0 siblings, 0 replies; 11+ messages in thread
From: Loftus, Ciara @ 2026-08-07 10:51 UTC (permalink / raw)
  To: Mandal, Anurag, dev@dpdk.org; +Cc: Richardson, Bruce, Medvedkin, Vladimir



> -----Original Message-----
> From: Mandal, Anurag <anurag.mandal@intel.com>
> Sent: 06 August 2026 09:26
> To: dev@dpdk.org
> Cc: Richardson, Bruce <bruce.richardson@intel.com>; Medvedkin, Vladimir
> <vladimir.medvedkin@intel.com>; Loftus, Ciara <ciara.loftus@intel.com>;
> Mandal, Anurag <anurag.mandal@intel.com>
> Subject: [PATCH 3/5] net/iavf: drain in-flight Tx before reset
> 
> On a link-down or impending PF reset, in-flight Tx descriptors
> were left pending when the queues were torn down, which could
> trigger Malicious Driver Detection (MDD) events and
> leak descriptors.
> 
> Added iavf_dev_tx_drain() to let already-posted Tx bursts
> complete and flush the rings within a bounded budget,
> and call it on link-down and reset-impending events
> before teardown, preventing MDD events and descriptor leaks.
> The drain selects the cleanup routine that matches the
> active Tx path: the scalar path uses ci_tx_xmit_cleanup(),
> while the vector and CTX paths use ci_tx_free_bufs_vec().
> This matters because the scalar and vector paths track
> their software rings differently
> (ci_tx_entry vs ci_tx_entry_vec) and using the scalar
> routine on a vector queue would walk the wrong ring
> and free the wrong mbufs.

I think " The drain selects the cleanup routine that matches the
active Tx path" is sufficient detail, you can clip the rest out.

> 
> Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
> ---
>  drivers/net/intel/iavf/iavf_rxtx.c  | 104 ++++++++++++++++++++++++++++
>  drivers/net/intel/iavf/iavf_rxtx.h  |   6 ++
>  drivers/net/intel/iavf/iavf_vchnl.c |   7 ++
>  3 files changed, 117 insertions(+)
> 
> diff --git a/drivers/net/intel/iavf/iavf_rxtx.c
> b/drivers/net/intel/iavf/iavf_rxtx.c
> index 4f2ffe6188..931bb8420d 100644
> --- a/drivers/net/intel/iavf/iavf_rxtx.c
> +++ b/drivers/net/intel/iavf/iavf_rxtx.c
> @@ -32,6 +32,7 @@
> 
>  #include "iavf.h"
>  #include "iavf_rxtx.h"
> +#include "iavf_rxtx_vec_common.h"
>  #include "iavf_ipsec_crypto.h"
>  #include "rte_pmd_iavf.h"
> 
> @@ -4025,6 +4026,109 @@ iavf_tx_done_cleanup_full(struct ci_tx_queue
> *txq,
>  	return (int)pkt_cnt;
>  }
> 
> +/*
> + * Reclaim completed Tx descriptors for a single queue using the cleanup
> + * routine that matches the active Tx path.

> + * The scalar and vector paths track their software rings differently
> + * (ci_tx_entry vs ci_tx_entry_vec) and keep separate completion
> + * bookkeeping, so using the scalar routine on a vector queue
> + * (or vice versa) would free the wrong mbufs.

I think the above sentence can be removed (verbose).

> + * Returns true if any descriptors were reclaimed.
> + */
> +static bool
> +iavf_tx_drain_cleanup(struct ci_tx_queue *txq,
> +		      enum iavf_tx_func_type tx_func_type)
> +{
> +	switch (tx_func_type) {
> +	case IAVF_TX_AVX2_CTX:
> +	case IAVF_TX_AVX2_CTX_OFFLOAD:
> +	case IAVF_TX_AVX512_CTX:
> +	case IAVF_TX_AVX512_CTX_OFFLOAD:
> +		return ci_tx_free_bufs_vec(txq, iavf_tx_desc_done, true) != 0;
> +	case IAVF_TX_NEON:
> +	case IAVF_TX_AVX2:
> +	case IAVF_TX_AVX2_OFFLOAD:
> +	case IAVF_TX_AVX512:
> +	case IAVF_TX_AVX512_OFFLOAD:
> +		return ci_tx_free_bufs_vec(txq, iavf_tx_desc_done, false) != 0;
> +	case IAVF_TX_DEFAULT:
> +	default:
> +		return ci_tx_xmit_cleanup(txq) == 0;
> +	}
> +}
> +

[snip]

>  	/*
> @@ -341,6 +343,8 @@ iavf_read_msg_from_pf(struct iavf_adapter
> *adapter, uint16_t buf_len,
>  			if (!vf->vf_reset) {
>  				vf->vf_reset = true;
>  				iavf_set_no_poll(adapter, false);
> +				if (adapter->devargs.no_poll_on_link_down)
> +					iavf_dev_tx_drain(vf->eth_dev);
>  				iavf_dev_event_post(vf->eth_dev,
>  					RTE_ETH_EVENT_INTR_RESET,
>  					NULL, 0);
> @@ -579,6 +583,9 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev,
> uint8_t *msg,
>  		if (!vf->vf_reset) {
>  			vf->vf_reset = true;
>  			iavf_set_no_poll(adapter, false);
> +			iavf_dev_watchdog_enable(adapter);

Is this enabling of the watchdog intended?

> +			if (adapter->devargs.no_poll_on_link_down)
> +				iavf_dev_tx_drain(dev);
>  			iavf_dev_event_post(dev,
> RTE_ETH_EVENT_INTR_RESET,
>  				NULL, 0);
>  		}
> --
> 2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH 4/5] net/iavf: change no_poll flag to atomic
  2026-08-06  8:26 ` [PATCH 4/5] net/iavf: change no_poll flag to atomic Anurag Mandal
@ 2026-08-07 10:56   ` Loftus, Ciara
  0 siblings, 0 replies; 11+ messages in thread
From: Loftus, Ciara @ 2026-08-07 10:56 UTC (permalink / raw)
  To: Mandal, Anurag, dev@dpdk.org
  Cc: Richardson, Bruce, Medvedkin, Vladimir, stable@dpdk.org

> Subject: [PATCH 4/5] net/iavf: change no_poll flag to atomic
> 
> The no_poll gate that pauses the Rx/Tx data path during reset and
> link-down was a plain bool written on the control path and read on
> the data-plane lcores without synchronization, allowing stale reads
> that either keep dropping traffic or trigger spurious reset detection.
> 
> Made no_poll an RTE_ATOMIC(bool) and access it with release stores and
> acquire loads so data-plane lcores reliably observe gate changes.
> 
> Fixes: 5b3124a0a6ef ("net/iavf: support no polling when link down")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>

Minor nit below. Other than that 

Acked-by: Ciara Loftus <ciara.loftus@intel.com>

> ---
>  drivers/net/intel/iavf/iavf.h        | 2 +-
>  drivers/net/intel/iavf/iavf_ethdev.c | 6 +++++-
>  drivers/net/intel/iavf/iavf_rxtx.c   | 8 ++++++--
>  drivers/net/intel/iavf/iavf_vchnl.c  | 4 +++-
>  4 files changed, 15 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
> index e76c3bb410..037bc8436f 100644
> --- a/drivers/net/intel/iavf/iavf.h
> +++ b/drivers/net/intel/iavf/iavf.h
> @@ -392,7 +392,7 @@ struct iavf_adapter {
>  	alignas(RTE_CACHE_LINE_MIN_SIZE) uint32_t
> ptype_tbl[IAVF_MAX_PKT_TYPE];
>  	bool stopped;
>  	bool closed;
> -	bool no_poll;
> +	RTE_ATOMIC(bool)no_poll;

Add a space before no_poll

>  	enum iavf_rx_func_type rx_func_type;
>  	enum iavf_tx_func_type tx_func_type;
>  	uint16_t fdir_ref_cnt;
> diff --git a/drivers/net/intel/iavf/iavf_ethdev.c
> b/drivers/net/intel/iavf/iavf_ethdev.c
> index 87b826c873..f6ce339b0c 100644
> --- a/drivers/net/intel/iavf/iavf_ethdev.c
> +++ b/drivers/net/intel/iavf/iavf_ethdev.c
> @@ -3604,9 +3604,13 @@ void
>  iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change)
>  {
>  	struct iavf_info *vf = &adapter->vf;
> +	bool no_poll;
> 
> -	adapter->no_poll = (link_change & !vf->link_up) ||
> +	no_poll = (link_change & !vf->link_up) ||
>  		vf->vf_reset || vf->in_reset_recovery;
> +
> +	rte_atomic_store_explicit(&adapter->no_poll, no_poll,
> +				  rte_memory_order_release);
>  }
> 
>  static int
> diff --git a/drivers/net/intel/iavf/iavf_rxtx.c
> b/drivers/net/intel/iavf/iavf_rxtx.c
> index 931bb8420d..104197d082 100644
> --- a/drivers/net/intel/iavf/iavf_rxtx.c
> +++ b/drivers/net/intel/iavf/iavf_rxtx.c
> @@ -3723,7 +3723,9 @@ iavf_recv_pkts_no_poll(void *rx_queue, struct
> rte_mbuf **rx_pkts,
>  	struct ci_rx_queue *rxq = rx_queue;
>  	enum iavf_rx_func_type rx_func_type;
> 
> -	if (!rxq->iavf_vsi || rxq->iavf_vsi->adapter->no_poll)
> +	if (!rxq->iavf_vsi ||
> +	    rte_atomic_load_explicit(&rxq->iavf_vsi->adapter->no_poll,
> +				     rte_memory_order_acquire))
>  		return 0;
> 
>  	rx_func_type = rxq->iavf_vsi->adapter->rx_func_type;
> @@ -3739,7 +3741,9 @@ iavf_xmit_pkts_no_poll(void *tx_queue, struct
> rte_mbuf **tx_pkts,
>  	struct ci_tx_queue *txq = tx_queue;
>  	enum iavf_tx_func_type tx_func_type;
> 
> -	if (!txq->iavf_vsi || txq->iavf_vsi->adapter->no_poll)
> +	if (!txq->iavf_vsi ||
> +	    rte_atomic_load_explicit(&txq->iavf_vsi->adapter->no_poll,
> +				     rte_memory_order_acquire))
>  		return 0;
> 
>  	tx_func_type = txq->iavf_vsi->adapter->tx_func_type;
> diff --git a/drivers/net/intel/iavf/iavf_vchnl.c
> b/drivers/net/intel/iavf/iavf_vchnl.c
> index d22990a524..dee76f97cb 100644
> --- a/drivers/net/intel/iavf/iavf_vchnl.c
> +++ b/drivers/net/intel/iavf/iavf_vchnl.c
> @@ -268,7 +268,9 @@ iavf_handle_link_change_event(struct rte_eth_dev
> *dev,
>  	if (adapter->devargs.no_poll_on_link_down) {
>  		iavf_set_no_poll(adapter, true);
>  		PMD_DRV_LOG(DEBUG, "VF no poll turned %s",
> -			    adapter->no_poll ? "on" : "off");
> +			    rte_atomic_load_explicit(&adapter->no_poll,
> +
> rte_memory_order_relaxed) ?
> +						     "on" : "off");
>  		if (!vf->link_up)
>  			iavf_dev_tx_drain(dev);
>  	}
> --
> 2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip
  2026-08-06  8:26 ` [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip Anurag Mandal
@ 2026-08-07 11:08   ` Loftus, Ciara
  0 siblings, 0 replies; 11+ messages in thread
From: Loftus, Ciara @ 2026-08-07 11:08 UTC (permalink / raw)
  To: Mandal, Anurag, dev@dpdk.org
  Cc: Richardson, Bruce, Medvedkin, Vladimir, stable@dpdk.org

> Subject: [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip
> 
> During PF-initiated reset or a remote/ToR switch link-flap,
> the PF toggles the admin receive queue enable bit (ARQLEN1)
> so quickly around a VF reset that the VF's sampling window
> misses it, leaving the PF and the VF states out of sync
> and the data path stalled.
> 
> Complement the ARQLEN1 check with VFGEN_RSTAT
> (VIRTCHNL_VFR_INPROGRESS) and shorten the poll interval to
> 5 ms (with a proportionally larger count, keeping the ~10 s
> budget), matching the Linux kernel iavf driver.
> When the VFR is still not observed, proceed with recovery
> instead of bailing out so the PF and  the VF states converge.
> 
> Fixes: ada64daa1a5b ("net/iavf: fix VF reset for flow director rule")
> Fixes: 80fb3c920458 ("net/iavf: fix crash on VF start")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Anurag Mandal <anurag.mandal@intel.com>
> ---
>  drivers/net/intel/iavf/iavf.h        |  1 +
>  drivers/net/intel/iavf/iavf_ethdev.c | 41 +++++++++++++++++++++-------
>  2 files changed, 32 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
> index 037bc8436f..5e49e81447 100644
> --- a/drivers/net/intel/iavf/iavf.h
> +++ b/drivers/net/intel/iavf/iavf.h
> @@ -21,6 +21,7 @@
>  #define IAVF_AQ_BUF_SZ            4096
>  #define IAVF_RESET_WAIT_CNT       2000
>  #define IAVF_RESET_DETECTED_CNT   500
> +#define IAVF_RESET_POLL_SCALE     4  /* Poll-interval scale for reset detection
> */
>  #define IAVF_BUF_SIZE_MIN         1024
>  #define IAVF_FRAME_SIZE_MAX       9728
>  #define IAVF_QUEUE_BASE_ADDR_UNIT 128
> diff --git a/drivers/net/intel/iavf/iavf_ethdev.c
> b/drivers/net/intel/iavf/iavf_ethdev.c
> index f6ce339b0c..21bac51467 100644
> --- a/drivers/net/intel/iavf/iavf_ethdev.c
> +++ b/drivers/net/intel/iavf/iavf_ethdev.c
> @@ -3235,7 +3235,9 @@ iavf_dev_close(struct rte_eth_dev *dev)
>  	/* remove RSS configuration */
>  	iavf_hash_uninit(adapter);
> 
> -	iavf_flow_flush(dev, NULL);
> +	/* Skip the virtchnl-emitting teardown on a PF-initiated reset */
> +	if (!vf->pf_reset_in_progress)
> +		iavf_flow_flush(dev, NULL);

This seems like a valid change but unrelated to the rest of the commit.
Consider splitting it out.

>  	iavf_flow_uninit(adapter);
> 
>  	/*
> @@ -3358,8 +3360,26 @@ iavf_dev_reset(struct rte_eth_dev *dev)
>  static inline bool
>  iavf_is_reset(struct iavf_hw *hw)
>  {
> -	return !(IAVF_READ_REG(hw, IAVF_VF_ARQLEN1) &
> -		IAVF_VF_ARQLEN1_ARQENABLE_MASK);
> +	uint32_t rstat;
> +
> +	/* ARQ has been disabled by the PF as part of the VFR. */
> +	if (!(IAVF_READ_REG(hw, IAVF_VF_ARQLEN1) &
> +		IAVF_VF_ARQLEN1_ARQENABLE_MASK))
> +		return true;
> +
> +	/*
> +	 * VFGEN_RSTAT reports VIRTCHNL_VFR_INPROGRESS.
> +	 * At times, the PF flips ARQENABLE so quickly
> +	 * around a VFR that the ARQLEN1 sample window
> +	 * misses it. Using VFGEN_RSTAT, as a
> +	 * complementary indicator, prevents from
> +	 * missing a reset that really did happen.
> +	 */
> +	rstat = (IAVF_READ_REG(hw, IAVF_VFGEN_RSTAT) &
> +		 IAVF_VFGEN_RSTAT_VFR_STATE_MASK) >>
> +		IAVF_VFGEN_RSTAT_VFR_STATE_SHIFT;
> +
> +	return rstat == VIRTCHNL_VFR_INPROGRESS;
>  }
> 
>  static bool
> @@ -3368,11 +3388,14 @@ iavf_is_reset_detected(struct iavf_adapter
> *adapter)
>  	struct iavf_hw *hw = IAVF_DEV_PRIVATE_TO_HW(adapter);
>  	int i;
> 
> -	/* poll until we see the reset actually happen */
> -	for (i = 0; i < IAVF_RESET_DETECTED_CNT; i++) {
> +	/*
> +	 * Poll until the reset actually happen.
> +	 * Poll every 5 ms to catch the fast ARQ flips.
> +	 */
> +	for (i = 0; i < IAVF_RESET_DETECTED_CNT * IAVF_RESET_POLL_SCALE;
> i++) {
>  		if (iavf_is_reset(hw))
>  			return true;
> -		rte_delay_ms(20);
> +		rte_delay_us(5000);
>  	}
> 
>  	return false;
> @@ -3423,10 +3446,8 @@ iavf_handle_hw_reset(struct rte_eth_dev *dev,
> bool vf_initiated_reset)
>  		if (!dev->data->dev_started)
>  			return;
> 
> -		if (!iavf_is_reset_detected(adapter)) {
> -			PMD_DRV_LOG(DEBUG, "reset not start");
> -			return;
> -		}
> +		if (!iavf_is_reset_detected(adapter))
> +			PMD_DRV_LOG(WARNING, "VFR not observed;
> recovering anyway");
>  	}
> 
>  	vf->in_reset_recovery = true;
> --
> 2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-08-07 11:08 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06  8:26 [PATCH 0/5] net/iavf: harden reset recovery and data path on link flap Anurag Mandal
2026-08-06  8:26 ` [PATCH 1/5] net/iavf: discard empty AdminQ descriptors on reset Anurag Mandal
2026-08-07 10:10   ` Loftus, Ciara
2026-08-06  8:26 ` [PATCH 2/5] net/iavf: defer device start when PF VSI not ready Anurag Mandal
2026-08-07 10:46   ` Loftus, Ciara
2026-08-06  8:26 ` [PATCH 3/5] net/iavf: drain in-flight Tx before reset Anurag Mandal
2026-08-07 10:51   ` Loftus, Ciara
2026-08-06  8:26 ` [PATCH 4/5] net/iavf: change no_poll flag to atomic Anurag Mandal
2026-08-07 10:56   ` Loftus, Ciara
2026-08-06  8:26 ` [PATCH 5/5] net/iavf: improve VF reset detection on fast ARQ flip Anurag Mandal
2026-08-07 11:08   ` Loftus, Ciara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox