* [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* 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
* [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* 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
* [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* 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
* [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* 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
* [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 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