* [PATCH v2 0/3] iavf reset path fixes
@ 2026-11-04 9:55 Ciara Loftus
2026-11-04 9:55 ` [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-04 9:55 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus
Three fixes related to the reset path in iavf. The first patch
was submitted as a standalone patch [1], but I've grouped it into
this series.
[1] https://patches.dpdk.org/project/dpdk/patch/20261102154558.78361-1-ciara.loftus@intel.com/
Ciara Loftus (3):
net/iavf: fix crash in Tx drain on PF reset
net/iavf: fix event handler leak on init failure
net/iavf: fix watchdog clearing reset state during recovery
drivers/net/intel/iavf/iavf.h | 4 ++-
drivers/net/intel/iavf/iavf_ethdev.c | 21 +++++++++---
drivers/net/intel/iavf/iavf_rxtx.c | 8 ++---
drivers/net/intel/iavf/iavf_vchnl.c | 48 ++++++++++++++++++++--------
4 files changed, 56 insertions(+), 25 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset
2026-11-04 9:55 [PATCH v2 0/3] iavf reset path fixes Ciara Loftus
@ 2026-11-04 9:55 ` Ciara Loftus
2026-11-04 17:51 ` Bruce Richardson
2026-11-04 9:55 ` [PATCH v2 2/3] net/iavf: fix event handler leak on init failure Ciara Loftus
` (2 subsequent siblings)
3 siblings, 1 reply; 20+ messages in thread
From: Ciara Loftus @ 2026-11-04 9:55 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable
The Tx cleanup helpers check the DD bit of the descriptor at the next
RS boundary and assume software has already posted it. The burst path
guarantees this by only cleaning up when nb_tx_free drops below
tx_free_thresh, but iavf_dev_tx_drain() called the helpers on any
non-empty queue.
With fewer than tx_rs_thresh descriptors outstanding, that descriptor
still held the DESC_DONE value set by reset_tx_queue(), so the helpers
treated the block as complete and freed NULL (or already freed) mbufs,
crashing on PF reset.
Only clean up a queue once a full RS block is outstanding.
Fixes: 30e1aab01bae ("net/iavf: drain in-flight Tx before reset")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
drivers/net/intel/iavf/iavf_rxtx.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 84b931e78a..8c84991b86 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -4098,11 +4098,9 @@ iavf_dev_tx_drain(struct rte_eth_dev *dev)
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)
+ /* Only clean up once a full RS block is outstanding. */
+ if ((uint16_t)(txq->nb_tx_desc - 1 - txq->nb_tx_free) <
+ txq->tx_rs_thresh)
continue;
any_pending = true;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 2/3] net/iavf: fix event handler leak on init failure
2026-11-04 9:55 [PATCH v2 0/3] iavf reset path fixes Ciara Loftus
2026-11-04 9:55 ` [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
@ 2026-11-04 9:55 ` Ciara Loftus
2026-11-04 17:52 ` Bruce Richardson
2026-11-04 9:55 ` [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
3 siblings, 1 reply; 20+ messages in thread
From: Ciara Loftus @ 2026-11-04 9:55 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable
iavf_dev_init() takes a reference on the shared event handler, but
does not release it if initialisation subsequently fails. The event
thread and its pipe are then never released.
Release the reference on these error paths. Skip it during reset
recovery, where no reference is taken.
Fixes: cb5c1b91f76f ("net/iavf: add thread for event callbacks")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
drivers/net/intel/iavf/iavf_ethdev.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index fd1067c252..38bf2e502a 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -3089,6 +3089,8 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
if (iavf_init_vf(eth_dev) != 0) {
PMD_INIT_LOG(ERR, "Init vf failed");
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
return -1;
}
@@ -3185,6 +3187,9 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
eth_dev->data->mac_addrs = NULL;
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
+
init_vf_err:
iavf_uninit_vf(eth_dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery
2026-11-04 9:55 [PATCH v2 0/3] iavf reset path fixes Ciara Loftus
2026-11-04 9:55 ` [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-04 9:55 ` [PATCH v2 2/3] net/iavf: fix event handler leak on init failure Ciara Loftus
@ 2026-11-04 9:55 ` Ciara Loftus
2026-11-04 18:00 ` Bruce Richardson
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
3 siblings, 1 reply; 20+ messages in thread
From: Ciara Loftus @ 2026-11-04 9:55 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Declan Doherty
Since the watchdog is kept armed across a PF reset, it can clear
vf_reset while the reset event is still queued or being handled, as
VFGEN_RSTAT toggles while the PF resets the VF. iavf_dev_close() then
skips re-enabling PCI bus mastering, which the VF reset cleared, so
VIRTCHNL_OP_VERSION never reaches the PF, iavf_dev_init() fails and the
port is left returning -EIO. Each toggle also posts a further reset
event, causing redundant recovery cycles.
Count reset events from before vf_reset is set until they have been
handled, and do not let the watchdog clear vf_reset while any are
outstanding.
Fixes: a0e296420a70 ("net/iavf: keep watchdog armed for the whole reset window")
Cc: stable@dpdk.org
Suggested-by: Declan Doherty <declan.doherty@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
drivers/net/intel/iavf/iavf.h | 4 ++-
drivers/net/intel/iavf/iavf_ethdev.c | 16 +++++++---
drivers/net/intel/iavf/iavf_vchnl.c | 48 ++++++++++++++++++++--------
3 files changed, 48 insertions(+), 20 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 8b826662c6..9cbbb5263a 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -290,6 +290,7 @@ struct iavf_info {
struct iavf_tm_conf tm_conf;
RTE_ATOMIC(bool)in_reset_recovery;
+ RTE_ATOMIC(uint32_t)reset_events_pending;
bool reset_pending;
bool pf_reset_in_progress;
bool start_pending;
@@ -454,9 +455,10 @@ struct iavf_cmd_info {
int iavf_check_api_version(struct iavf_adapter *adapter);
int iavf_get_vf_resource(struct iavf_adapter *adapter);
-void iavf_dev_event_post(struct rte_eth_dev *dev,
+int iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size);
+void iavf_dev_reset_event_post(struct rte_eth_dev *dev);
void iavf_dev_event_handler_fini(void);
int iavf_dev_event_handler_init(void);
void iavf_handle_virtchnl_msg(struct rte_eth_dev *dev);
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 38bf2e502a..e57a018906 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -336,11 +336,14 @@ iavf_dev_watchdog(void *cb_arg)
/* If in reset then poll vfr_inprogress register for completion */
if (adapter->vf.vf_reset) {
+ /* pair with the release fence before vf_reset is set */
+ rte_atomic_thread_fence(rte_memory_order_acquire);
vfr_inprogress = iavf_vfr_inprogress(hw);
- if (!vfr_inprogress) {
- PMD_DRV_LOG(INFO, "VF \"%s\" reset has completed",
- dev->data->name);
+ if (!vfr_inprogress &&
+ rte_atomic_load_explicit(&adapter->vf.reset_events_pending,
+ rte_memory_order_acquire) == 0) {
+ PMD_DRV_LOG(INFO, "VF \"%s\" reset has completed", dev->data->name);
adapter->vf.vf_reset = false;
iavf_set_no_poll(adapter, false);
}
@@ -354,12 +357,15 @@ iavf_dev_watchdog(void *cb_arg)
dev->data->name);
/* enter reset state with VFLR event */
+ rte_atomic_fetch_add_explicit(&adapter->vf.reset_events_pending, 1,
+ rte_memory_order_relaxed);
+ /* publish the count before vf_reset is visible */
+ rte_atomic_thread_fence(rte_memory_order_release);
adapter->vf.vf_reset = true;
iavf_set_no_poll(adapter, false);
adapter->vf.link_up = false;
- iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET,
- NULL, 0);
+ iavf_dev_reset_event_post(dev);
}
}
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 34961fe654..cbbab242af 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -81,14 +81,17 @@ iavf_dev_event_handle(void *param __rte_unused)
TAILQ_REMOVE(&pending, pos, next);
struct iavf_adapter *adapter = pos->dev->data->dev_private;
- if (pos->event == RTE_ETH_EVENT_INTR_RESET &&
- adapter->devargs.auto_reset) {
- iavf_handle_hw_reset(pos->dev, false);
- rte_free(pos);
- continue;
+ if (pos->event == RTE_ETH_EVENT_INTR_RESET) {
+ if (adapter->devargs.auto_reset)
+ iavf_handle_hw_reset(pos->dev, false);
+ else
+ rte_eth_dev_callback_process(pos->dev, pos->event,
+ pos->param);
+ rte_atomic_fetch_sub_explicit(&adapter->vf.reset_events_pending, 1,
+ rte_memory_order_release);
+ } else {
+ rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
}
-
- rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
rte_free(pos);
}
}
@@ -96,7 +99,7 @@ iavf_dev_event_handle(void *param __rte_unused)
return 0;
}
-void
+int
iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size)
@@ -105,7 +108,7 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
char notify_byte = 0;
struct iavf_event_element *elem = rte_malloc(NULL, sizeof(*elem) + param_alloc_size, 0);
if (!elem)
- return;
+ return -ENOMEM;
elem->dev = dev;
elem->event = event;
@@ -122,6 +125,18 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
ssize_t nw = write(handler->fd[1], ¬ify_byte, 1);
RTE_SET_USED(nw);
+
+ return 0;
+}
+
+void
+iavf_dev_reset_event_post(struct rte_eth_dev *dev)
+{
+ struct iavf_info *vf = IAVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+
+ if (iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET, NULL, 0) != 0)
+ rte_atomic_fetch_sub_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_release);
}
int
@@ -332,14 +347,16 @@ iavf_read_msg_from_pf(struct iavf_adapter *adapter, uint16_t buf_len,
*/
vf->link_up = false;
if (!vf->vf_reset) {
+ rte_atomic_fetch_add_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_relaxed);
+ /* publish the count before vf_reset is visible */
+ rte_atomic_thread_fence(rte_memory_order_release);
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);
+ iavf_dev_reset_event_post(dev);
}
PMD_DRV_LOG(DEBUG, "VF is resetting");
break;
@@ -573,13 +590,16 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_RESET_IMPENDING event");
vf->link_up = false;
if (!vf->vf_reset) {
+ rte_atomic_fetch_add_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_relaxed);
+ /* publish the count before vf_reset is visible */
+ rte_atomic_thread_fence(rte_memory_order_release);
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);
+ iavf_dev_reset_event_post(dev);
}
break;
case VIRTCHNL_EVENT_LINK_CHANGE:
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset
2026-11-04 9:55 ` [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
@ 2026-11-04 17:51 ` Bruce Richardson
0 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-04 17:51 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, stable
On Wed, Nov 04, 2026 at 09:55:07AM +0000, Ciara Loftus wrote:
> The Tx cleanup helpers check the DD bit of the descriptor at the next
> RS boundary and assume software has already posted it. The burst path
> guarantees this by only cleaning up when nb_tx_free drops below
> tx_free_thresh, but iavf_dev_tx_drain() called the helpers on any
> non-empty queue.
>
> With fewer than tx_rs_thresh descriptors outstanding, that descriptor
> still held the DESC_DONE value set by reset_tx_queue(), so the helpers
> treated the block as complete and freed NULL (or already freed) mbufs,
> crashing on PF reset.
>
> Only clean up a queue once a full RS block is outstanding.
>
> Fixes: 30e1aab01bae ("net/iavf: drain in-flight Tx before reset")
> Cc: stable@dpdk.org
>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
> drivers/net/intel/iavf/iavf_rxtx.c | 8 +++-----
> 1 file changed, 3 insertions(+), 5 deletions(-)
>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 2/3] net/iavf: fix event handler leak on init failure
2026-11-04 9:55 ` [PATCH v2 2/3] net/iavf: fix event handler leak on init failure Ciara Loftus
@ 2026-11-04 17:52 ` Bruce Richardson
0 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-04 17:52 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, stable
On Wed, Nov 04, 2026 at 09:55:08AM +0000, Ciara Loftus wrote:
> iavf_dev_init() takes a reference on the shared event handler, but
> does not release it if initialisation subsequently fails. The event
> thread and its pipe are then never released.
>
> Release the reference on these error paths. Skip it during reset
> recovery, where no reference is taken.
>
> Fixes: cb5c1b91f76f ("net/iavf: add thread for event callbacks")
> Cc: stable@dpdk.org
>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
> drivers/net/intel/iavf/iavf_ethdev.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery
2026-11-04 9:55 ` [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
@ 2026-11-04 18:00 ` Bruce Richardson
0 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-04 18:00 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, stable, Declan Doherty
On Wed, Nov 04, 2026 at 09:55:09AM +0000, Ciara Loftus wrote:
> Since the watchdog is kept armed across a PF reset, it can clear
> vf_reset while the reset event is still queued or being handled, as
> VFGEN_RSTAT toggles while the PF resets the VF. iavf_dev_close() then
> skips re-enabling PCI bus mastering, which the VF reset cleared, so
> VIRTCHNL_OP_VERSION never reaches the PF, iavf_dev_init() fails and the
> port is left returning -EIO. Each toggle also posts a further reset
> event, causing redundant recovery cycles.
>
> Count reset events from before vf_reset is set until they have been
> handled, and do not let the watchdog clear vf_reset while any are
> outstanding.
>
> Fixes: a0e296420a70 ("net/iavf: keep watchdog armed for the whole reset window")
> Cc: stable@dpdk.org
>
> Suggested-by: Declan Doherty <declan.doherty@intel.com>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
> drivers/net/intel/iavf/iavf.h | 4 ++-
> drivers/net/intel/iavf/iavf_ethdev.c | 16 +++++++---
> drivers/net/intel/iavf/iavf_vchnl.c | 48 ++++++++++++++++++++--------
> 3 files changed, 48 insertions(+), 20 deletions(-)
>
While the AI review in CI shows no issues, my own AI review flags that
there is potentially a problem due to vf_reset not being declared atomic.
/Bruce
---
The producer and watchdog can run concurrently: the PF reset path handles
messages in the interrupt callback (iavf_ethdev.c:2847), while the watchdog
runs separately.
A concrete interleaving is:
1. The event path increments reset_events_pending, executes the release
fence, then writes vf_reset = true (iavf_vchnl.c:349).
2. The watchdog reads vf_reset as true, checks that hardware reset is no
longer in progress, then loads reset_events_pending (iavf_ethdev.c:338).
3. That acquire load is allowed to observe the old zero value: it
synchronizes with the increment only if it reads from that increment. The
plain vf_reset read does not provide the atomic synchronization, because
the field is still declared as bool (iavf.h:264). The watchdog can
therefore clear vf_reset while the reset event remains pending, which is
exactly the state this commit aims to prevent. The release/acquire fences
don’t fix this: they do not make the plain flag accesses atomic or
guarantee that observing the flag publishes the counter.
The fix should make vf_reset atomic and use atomic release stores/acquire
loads consistently at its concurrent access sites.
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 0/4] iavf reset path fixes
2026-11-04 9:55 [PATCH v2 0/3] iavf reset path fixes Ciara Loftus
` (2 preceding siblings ...)
2026-11-04 9:55 ` [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
@ 2026-11-05 12:32 ` Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
` (4 more replies)
3 siblings, 5 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-05 12:32 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus
Four fixes related to the reset path in iavf.
v3:
* Patch 3: made the vf_reset flag atomic and moved entry to the reset
state into a helper.
* Added patch 4, fixing a race between watchdog enable and disable.
Ciara Loftus (4):
net/iavf: fix crash in Tx drain on PF reset
net/iavf: fix event handler leak on init failure
net/iavf: fix watchdog clearing reset state during recovery
net/iavf: fix watchdog enable racing disable
drivers/net/intel/iavf/iavf.h | 7 +-
drivers/net/intel/iavf/iavf_ethdev.c | 74 +++++++++--------
drivers/net/intel/iavf/iavf_rxtx.c | 8 +-
drivers/net/intel/iavf/iavf_vchnl.c | 120 ++++++++++++++++++---------
4 files changed, 128 insertions(+), 81 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 1/4] net/iavf: fix crash in Tx drain on PF reset
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
@ 2026-11-05 12:32 ` Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
` (3 subsequent siblings)
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-05 12:32 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Bruce Richardson
The Tx cleanup helpers check the DD bit of the descriptor at the next
RS boundary and assume software has already posted it. The burst path
guarantees this by only cleaning up when nb_tx_free drops below
tx_free_thresh, but iavf_dev_tx_drain() called the helpers on any
non-empty queue.
With fewer than tx_rs_thresh descriptors outstanding, that descriptor
still held the DESC_DONE value set by reset_tx_queue(), so the helpers
treated the block as complete and freed NULL (or already freed) mbufs,
crashing on PF reset.
Only clean up a queue once a full RS block is outstanding.
Fixes: 30e1aab01bae ("net/iavf: drain in-flight Tx before reset")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
---
drivers/net/intel/iavf/iavf_rxtx.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 84b931e78a..8c84991b86 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -4098,11 +4098,9 @@ iavf_dev_tx_drain(struct rte_eth_dev *dev)
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)
+ /* Only clean up once a full RS block is outstanding. */
+ if ((uint16_t)(txq->nb_tx_desc - 1 - txq->nb_tx_free) <
+ txq->tx_rs_thresh)
continue;
any_pending = true;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 2/4] net/iavf: fix event handler leak on init failure
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
@ 2026-11-05 12:32 ` Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
` (2 subsequent siblings)
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-05 12:32 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Bruce Richardson
iavf_dev_init() takes a reference on the shared event handler, but
does not release it if initialisation subsequently fails. The event
thread and its pipe are then never released.
Release the reference on these error paths. Skip it during reset
recovery, where no reference is taken.
Fixes: cb5c1b91f76f ("net/iavf: add thread for event callbacks")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
---
drivers/net/intel/iavf/iavf_ethdev.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 47aa6ecb6f..05b90a6230 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -3089,6 +3089,8 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
if (iavf_init_vf(eth_dev) != 0) {
PMD_INIT_LOG(ERR, "Init vf failed");
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
return -1;
}
@@ -3190,6 +3192,9 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
eth_dev->data->mac_addrs = NULL;
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
+
init_vf_err:
iavf_uninit_vf(eth_dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 3/4] net/iavf: fix watchdog clearing reset state during recovery
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
@ 2026-11-05 12:32 ` Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-05 12:32 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Declan Doherty
Since the watchdog is kept armed across a PF reset, it can clear the
`vf_reset` flag while the reset event is still queued or being handled,
as VFGEN_RSTAT toggles while the PF resets the VF. iavf_dev_close() then
skips re-enabling PCI bus mastering, which the VF reset cleared, so
VIRTCHNL_OP_VERSION never reaches the PF, iavf_dev_init() fails and the
port is left returning -EIO. Each toggle also posts a further reset
event, causing redundant recovery cycles.
Count reset events from before `vf_reset` is set until they have been
handled, and do not let the watchdog clear the flag while any are
outstanding. Also make the `vf_reset` flag atomic, as it is accessed from
multiple places: the watchdog alarm, the interrupt thread and the virtchnl
polling path.
Fixes: a0e296420a70 ("net/iavf: keep watchdog armed for the whole reset window")
Cc: stable@dpdk.org
Suggested-by: Declan Doherty <declan.doherty@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
v3:
* make vf_reset flag atomic
* add helper functions
---
drivers/net/intel/iavf/iavf.h | 6 +-
drivers/net/intel/iavf/iavf_ethdev.c | 32 ++++---
drivers/net/intel/iavf/iavf_vchnl.c | 120 ++++++++++++++++++---------
3 files changed, 97 insertions(+), 61 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 5550e57c35..04c2900afe 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -261,7 +261,7 @@ struct iavf_info {
uint16_t mc_addrs_num; /* Multicast mac addresses number */
struct iavf_vsi vsi;
- bool vf_reset; /* true for VF reset pending, false for no VF reset */
+ RTE_ATOMIC(bool)vf_reset; /* true for VF reset pending, false for no VF reset */
iavf_pre_reset_cb_t pre_reset_cb; /* Pre reset callback function ptr */
iavf_post_reset_cb_t post_reset_cb; /* Post reset callback function ptr */
void *pre_reset_cb_arg; /* Pre reset callback argument */
@@ -290,6 +290,7 @@ struct iavf_info {
struct iavf_tm_conf tm_conf;
RTE_ATOMIC(bool)in_reset_recovery;
+ RTE_ATOMIC(uint32_t)reset_events_pending;
bool reset_pending;
bool pf_reset_in_progress;
bool start_pending;
@@ -455,9 +456,10 @@ struct iavf_cmd_info {
int iavf_check_api_version(struct iavf_adapter *adapter);
int iavf_get_vf_resource(struct iavf_adapter *adapter);
-void iavf_dev_event_post(struct rte_eth_dev *dev,
+int iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size);
+bool iavf_dev_enter_reset(struct rte_eth_dev *dev, bool drain_tx);
void iavf_dev_event_handler_fini(void);
int iavf_dev_event_handler_init(void);
void iavf_handle_virtchnl_msg(struct rte_eth_dev *dev);
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 05b90a6230..83470a636d 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -335,32 +335,27 @@ iavf_dev_watchdog(void *cb_arg)
return;
/* If in reset then poll vfr_inprogress register for completion */
- if (adapter->vf.vf_reset) {
+ if (rte_atomic_load_explicit(&adapter->vf.vf_reset, rte_memory_order_acquire)) {
vfr_inprogress = iavf_vfr_inprogress(hw);
- if (!vfr_inprogress) {
+ if (!vfr_inprogress &&
+ rte_atomic_load_explicit(&adapter->vf.reset_events_pending,
+ rte_memory_order_acquire) == 0) {
PMD_DRV_LOG(INFO, "VF \"%s\" reset has completed",
dev->data->name);
- adapter->vf.vf_reset = false;
+ rte_atomic_store_explicit(&adapter->vf.vf_reset, false,
+ rte_memory_order_release);
iavf_set_no_poll(adapter, false);
}
/* If not in reset then poll vfr_inprogress register for VFLR event */
} else {
vfr_inprogress = iavf_vfr_inprogress(hw);
- if (vfr_inprogress) {
+ /* enter reset state with VFLR event */
+ if (vfr_inprogress && iavf_dev_enter_reset(dev, false))
PMD_DRV_LOG(INFO,
"VF \"%s\" reset event detected by watchdog",
dev->data->name);
-
- /* enter reset state with VFLR event */
- adapter->vf.vf_reset = true;
- iavf_set_no_poll(adapter, false);
- adapter->vf.link_up = false;
-
- iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET,
- NULL, 0);
- }
}
if (adapter->devargs.watchdog_period) {
@@ -3320,8 +3315,9 @@ iavf_dev_close(struct rte_eth_dev *dev)
* effect.
*/
out:
- if (vf->vf_reset && !rte_pci_set_bus_master(pci_dev, true)) {
- vf->vf_reset = false;
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) &&
+ !rte_pci_set_bus_master(pci_dev, true)) {
+ rte_atomic_store_explicit(&vf->vf_reset, false, rte_memory_order_release);
iavf_set_no_poll(adapter, false);
}
@@ -3657,7 +3653,7 @@ iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change)
bool no_poll;
no_poll = (link_change & !vf->link_up) ||
- vf->vf_reset ||
+ rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) ||
rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed);
rte_atomic_store_explicit(&adapter->no_poll, no_poll,
@@ -3740,8 +3736,8 @@ iavf_resume_pending_start(struct rte_eth_dev *dev)
if (!vf->start_pending)
return;
- if (vf->vf_reset || rte_atomic_load_explicit(&vf->in_reset_recovery,
- rte_memory_order_acquire))
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) ||
+ rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_acquire))
return;
/*
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 34961fe654..a8d17ed733 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -59,6 +59,26 @@ static struct iavf_event_handler event_handler = {
(var) = (tvar))
#endif
+static void
+iavf_dev_reset_event_handle(struct iavf_event_element *elem)
+{
+ struct rte_eth_dev *dev = elem->dev;
+ struct iavf_adapter *adapter = dev->data->dev_private;
+ uint16_t port_id = dev->data->port_id;
+
+ if (adapter->devargs.auto_reset)
+ iavf_handle_hw_reset(dev, false);
+ else
+ rte_eth_dev_callback_process(dev, elem->event, elem->param);
+
+ /* Ensure the port still exists before touching the adapter
+ * as the application callback may have closed it.
+ */
+ if (rte_eth_dev_is_valid_port(port_id))
+ rte_atomic_fetch_sub_explicit(&adapter->vf.reset_events_pending, 1,
+ rte_memory_order_release);
+}
+
static uint32_t
iavf_dev_event_handle(void *param __rte_unused)
{
@@ -80,15 +100,10 @@ iavf_dev_event_handle(void *param __rte_unused)
TAILQ_FOREACH_SAFE(pos, &pending, next, save_next) {
TAILQ_REMOVE(&pending, pos, next);
- struct iavf_adapter *adapter = pos->dev->data->dev_private;
- if (pos->event == RTE_ETH_EVENT_INTR_RESET &&
- adapter->devargs.auto_reset) {
- iavf_handle_hw_reset(pos->dev, false);
- rte_free(pos);
- continue;
- }
-
- rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
+ if (pos->event == RTE_ETH_EVENT_INTR_RESET)
+ iavf_dev_reset_event_handle(pos);
+ else
+ rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
rte_free(pos);
}
}
@@ -96,7 +111,7 @@ iavf_dev_event_handle(void *param __rte_unused)
return 0;
}
-void
+int
iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size)
@@ -105,7 +120,7 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
char notify_byte = 0;
struct iavf_event_element *elem = rte_malloc(NULL, sizeof(*elem) + param_alloc_size, 0);
if (!elem)
- return;
+ return -ENOMEM;
elem->dev = dev;
elem->event = event;
@@ -122,6 +137,53 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
ssize_t nw = write(handler->fd[1], ¬ify_byte, 1);
RTE_SET_USED(nw);
+
+ return 0;
+}
+
+/*
+ * Enter the VF reset state. If this is a new reset, rather than one already
+ * in progress, gate the datapath and post the reset event.
+ * Returns true if this call started the reset.
+ */
+bool
+iavf_dev_enter_reset(struct rte_eth_dev *dev, bool drain_tx)
+{
+ struct iavf_adapter *adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ struct iavf_info *vf = &adapter->vf;
+ bool expected = false;
+
+ /*
+ * Force link down to drop the cached link-up state; a fresh LSC up
+ * event will be re-issued by the PF once the VF is reinitialised.
+ */
+ vf->link_up = false;
+
+ /*
+ * Count the event before vf_reset becomes visible. The watchdog clears
+ * vf_reset once it sees it set with no events pending, so setting the
+ * flag first would open a window in which the event is lost. If another
+ * thread has already entered reset, back the count out: no event is
+ * posted on this path.
+ */
+ rte_atomic_fetch_add_explicit(&vf->reset_events_pending, 1, rte_memory_order_relaxed);
+ if (!rte_atomic_compare_exchange_strong_explicit(&vf->vf_reset, &expected, true,
+ rte_memory_order_release, rte_memory_order_acquire)) {
+ rte_atomic_fetch_sub_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_relaxed);
+ return false;
+ }
+
+ iavf_set_no_poll(adapter, false);
+ /* drain before posting the event, as the event handler tears down the queues */
+ if (drain_tx && adapter->devargs.no_poll_on_link_down)
+ iavf_dev_tx_drain(dev);
+
+ if (iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET, NULL, 0) != 0)
+ rte_atomic_fetch_sub_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_release);
+
+ return true;
}
int
@@ -260,7 +322,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 && !rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) &&
!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
iavf_dev_watchdog_disable(adapter);
else
@@ -324,23 +386,8 @@ iavf_read_msg_from_pf(struct iavf_adapter *adapter, uint16_t buf_len,
iavf_handle_link_change_event(dev, vpe);
break;
case VIRTCHNL_EVENT_RESET_IMPENDING:
- /*
- * Force link down on impending reset to drop
- * the cached link-up state; a fresh LSC up
- * event will be re-issued by the PF once the
- * VF is reinitialised.
- */
- vf->link_up = false;
- if (!vf->vf_reset) {
- vf->vf_reset = true;
- iavf_set_no_poll(adapter, false);
+ if (iavf_dev_enter_reset(dev, true))
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);
- }
PMD_DRV_LOG(DEBUG, "VF is resetting");
break;
case VIRTCHNL_EVENT_PF_DRIVER_CLOSE:
@@ -485,7 +532,7 @@ iavf_execute_vf_cmd(struct iavf_adapter *adapter, struct iavf_cmd_info *args)
bool poll = iavf_op_needs_poll(vf);
int err;
- if (vf->vf_reset)
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire))
return -EIO;
resp_count = iavf_get_cmd_resp_count(args->ops);
@@ -555,7 +602,6 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
{
struct iavf_adapter *adapter =
IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
- struct iavf_info *vf = &adapter->vf;
struct virtchnl_pf_event *pf_msg =
(struct virtchnl_pf_event *)msg;
@@ -571,16 +617,8 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
switch (pf_msg->event) {
case VIRTCHNL_EVENT_RESET_IMPENDING:
PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_RESET_IMPENDING event");
- vf->link_up = false;
- if (!vf->vf_reset) {
- vf->vf_reset = true;
- iavf_set_no_poll(adapter, false);
+ if (iavf_dev_enter_reset(dev, true))
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);
- }
break;
case VIRTCHNL_EVENT_LINK_CHANGE:
PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_LINK_CHANGE event");
@@ -2394,13 +2432,13 @@ iavf_request_queues(struct rte_eth_dev *dev, uint16_t num)
/* wait for interrupt notification vf is resetting */
while (i++ < MAX_TRY_TIMES) {
- if (vf->vf_reset)
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire))
break;
iavf_msec_delay(ASQ_DELAY_MS);
}
/* request queues succeeded, vf is resetting */
- if (vf->vf_reset) {
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire)) {
PMD_DRV_LOG(INFO, "vf is resetting");
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v3 4/4] net/iavf: fix watchdog enable racing disable
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
` (2 preceding siblings ...)
2026-11-05 12:32 ` [PATCH v3 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
@ 2026-11-05 12:32 ` Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-05 12:32 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable
iavf_dev_watchdog_enable() runs on the interrupt thread from the link
change and reset impending handlers, while iavf_dev_watchdog_disable()
runs on a control thread from dev_stop and dev_close. Disable clears
`watchdog_enabled` before cancelling the alarm. An enable landing in
between sets the flag again and arms a new alarm, which the cancel then
removes. The flag is left set with no alarm armed, so every later enable
is a no-op and the watchdog is dead until the port is re-probed.
Serialise enable and disable with a spinlock held across the flag
update and the alarm set or cancel.
Fixes: 4c9b1d2ff616 ("net/iavf: refactor watchdog")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
drivers/net/intel/iavf/iavf.h | 1 +
drivers/net/intel/iavf/iavf_ethdev.c | 37 +++++++++++++++++-----------
2 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 04c2900afe..0c98ae28e1 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -247,6 +247,7 @@ struct iavf_info {
/** iAVF watchdog enable */
bool watchdog_enabled;
+ rte_spinlock_t watchdog_lock;
/* Event from pf */
bool dev_closed;
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 83470a636d..10069feae2 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -374,16 +374,19 @@ iavf_dev_watchdog_enable(struct iavf_adapter *adapter)
{
if (!adapter->devargs.watchdog_period) {
PMD_DRV_LOG(INFO, "Device watchdog is disabled");
- } else {
- if (!adapter->vf.watchdog_enabled) {
- PMD_DRV_LOG(INFO, "Enabling device watchdog, period is %dμs",
- adapter->devargs.watchdog_period);
- adapter->vf.watchdog_enabled = true;
- if (rte_eal_alarm_set(adapter->devargs.watchdog_period,
- &iavf_dev_watchdog, (void *)adapter))
- PMD_DRV_LOG(ERR, "Failed to enable device watchdog");
- }
+ return;
+ }
+
+ rte_spinlock_lock(&adapter->vf.watchdog_lock);
+ if (!adapter->vf.watchdog_enabled) {
+ PMD_DRV_LOG(INFO, "Enabling device watchdog, period is %dμs",
+ adapter->devargs.watchdog_period);
+ adapter->vf.watchdog_enabled = true;
+ if (rte_eal_alarm_set(adapter->devargs.watchdog_period,
+ &iavf_dev_watchdog, (void *)adapter))
+ PMD_DRV_LOG(ERR, "Failed to enable device watchdog");
}
+ rte_spinlock_unlock(&adapter->vf.watchdog_lock);
}
void
@@ -391,13 +394,16 @@ iavf_dev_watchdog_disable(struct iavf_adapter *adapter)
{
if (!adapter->devargs.watchdog_period) {
PMD_DRV_LOG(INFO, "Device watchdog is not enabled");
- } else {
- if (adapter->vf.watchdog_enabled) {
- PMD_DRV_LOG(INFO, "Disabling device watchdog");
- adapter->vf.watchdog_enabled = false;
- rte_eal_alarm_cancel(&iavf_dev_watchdog, (void *)adapter);
- }
+ return;
+ }
+
+ rte_spinlock_lock(&adapter->vf.watchdog_lock);
+ if (adapter->vf.watchdog_enabled) {
+ PMD_DRV_LOG(INFO, "Disabling device watchdog");
+ adapter->vf.watchdog_enabled = false;
+ rte_eal_alarm_cancel(&iavf_dev_watchdog, (void *)adapter);
}
+ rte_spinlock_unlock(&adapter->vf.watchdog_lock);
}
static int
@@ -3077,6 +3083,7 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
adapter->mac_primary_set = false;
adapter->tpid = RTE_ETHER_TYPE_VLAN; /* VLAN TPID set to 0x8100 by default */
rte_spinlock_init(&adapter->phc_sync_lock);
+ rte_spinlock_init(&vf->watchdog_lock);
if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed) &&
iavf_dev_event_handler_init())
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 0/4] iavf reset path fixes
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
` (3 preceding siblings ...)
2026-11-05 12:32 ` [PATCH v3 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
@ 2026-11-06 9:40 ` Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
` (4 more replies)
4 siblings, 5 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-06 9:40 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus
Four fixes related to the reset path in iavf.
v4 ( patch 3):
* Remove overly defensive check after reset callback
* Added a log on event post failure
Ciara Loftus (4):
net/iavf: fix crash in Tx drain on PF reset
net/iavf: fix event handler leak on init failure
net/iavf: fix watchdog clearing reset state during recovery
net/iavf: fix watchdog enable racing disable
drivers/net/intel/iavf/iavf.h | 7 +-
drivers/net/intel/iavf/iavf_ethdev.c | 74 +++++++++--------
drivers/net/intel/iavf/iavf_rxtx.c | 8 +-
drivers/net/intel/iavf/iavf_vchnl.c | 117 +++++++++++++++++----------
4 files changed, 125 insertions(+), 81 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v4 1/4] net/iavf: fix crash in Tx drain on PF reset
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
@ 2026-11-06 9:40 ` Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
` (3 subsequent siblings)
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-06 9:40 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Bruce Richardson
The Tx cleanup helpers check the DD bit of the descriptor at the next
RS boundary and assume software has already posted it. The burst path
guarantees this by only cleaning up when nb_tx_free drops below
tx_free_thresh, but iavf_dev_tx_drain() called the helpers on any
non-empty queue.
With fewer than tx_rs_thresh descriptors outstanding, that descriptor
still held the DESC_DONE value set by reset_tx_queue(), so the helpers
treated the block as complete and freed NULL (or already freed) mbufs,
crashing on PF reset.
Only clean up a queue once a full RS block is outstanding.
Fixes: 30e1aab01bae ("net/iavf: drain in-flight Tx before reset")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
---
drivers/net/intel/iavf/iavf_rxtx.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 84b931e78a..8c84991b86 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -4098,11 +4098,9 @@ iavf_dev_tx_drain(struct rte_eth_dev *dev)
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)
+ /* Only clean up once a full RS block is outstanding. */
+ if ((uint16_t)(txq->nb_tx_desc - 1 - txq->nb_tx_free) <
+ txq->tx_rs_thresh)
continue;
any_pending = true;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 2/4] net/iavf: fix event handler leak on init failure
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
@ 2026-11-06 9:40 ` Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
` (2 subsequent siblings)
4 siblings, 0 replies; 20+ messages in thread
From: Ciara Loftus @ 2026-11-06 9:40 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Bruce Richardson
iavf_dev_init() takes a reference on the shared event handler, but
does not release it if initialisation subsequently fails. The event
thread and its pipe are then never released.
Release the reference on these error paths. Skip it during reset
recovery, where no reference is taken.
Fixes: cb5c1b91f76f ("net/iavf: add thread for event callbacks")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
---
drivers/net/intel/iavf/iavf_ethdev.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 47aa6ecb6f..05b90a6230 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -3089,6 +3089,8 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
if (iavf_init_vf(eth_dev) != 0) {
PMD_INIT_LOG(ERR, "Init vf failed");
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
return -1;
}
@@ -3190,6 +3192,9 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
eth_dev->data->mac_addrs = NULL;
+ if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
+ iavf_dev_event_handler_fini();
+
init_vf_err:
iavf_uninit_vf(eth_dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
@ 2026-11-06 9:40 ` Ciara Loftus
2026-11-10 10:47 ` Bruce Richardson
2026-11-06 9:40 ` [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
2026-11-10 10:51 ` [PATCH v4 0/4] iavf reset path fixes Bruce Richardson
4 siblings, 1 reply; 20+ messages in thread
From: Ciara Loftus @ 2026-11-06 9:40 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable, Declan Doherty
Since the watchdog is kept armed across a PF reset, it can clear the
`vf_reset` flag while the reset event is still queued or being handled,
as VFGEN_RSTAT toggles while the PF resets the VF. iavf_dev_close() then
skips re-enabling PCI bus mastering, which the VF reset cleared, so
VIRTCHNL_OP_VERSION never reaches the PF, iavf_dev_init() fails and the
port is left returning -EIO. Each toggle also posts a further reset
event, causing redundant recovery cycles.
Count reset events from before `vf_reset` is set until they have been
handled, and do not let the watchdog clear the flag while any are
outstanding. Also make the `vf_reset` flag atomic, as it is accessed from
multiple places: the watchdog alarm, the interrupt thread and the virtchnl
polling path.
Fixes: a0e296420a70 ("net/iavf: keep watchdog armed for the whole reset window")
Cc: stable@dpdk.org
Suggested-by: Declan Doherty <declan.doherty@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
v4:
* Remove overly defensive check after reset callback
* Added a log on event post failure
---
drivers/net/intel/iavf/iavf.h | 6 +-
drivers/net/intel/iavf/iavf_ethdev.c | 32 ++++----
drivers/net/intel/iavf/iavf_vchnl.c | 117 +++++++++++++++++----------
3 files changed, 94 insertions(+), 61 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 5550e57c35..04c2900afe 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -261,7 +261,7 @@ struct iavf_info {
uint16_t mc_addrs_num; /* Multicast mac addresses number */
struct iavf_vsi vsi;
- bool vf_reset; /* true for VF reset pending, false for no VF reset */
+ RTE_ATOMIC(bool)vf_reset; /* true for VF reset pending, false for no VF reset */
iavf_pre_reset_cb_t pre_reset_cb; /* Pre reset callback function ptr */
iavf_post_reset_cb_t post_reset_cb; /* Post reset callback function ptr */
void *pre_reset_cb_arg; /* Pre reset callback argument */
@@ -290,6 +290,7 @@ struct iavf_info {
struct iavf_tm_conf tm_conf;
RTE_ATOMIC(bool)in_reset_recovery;
+ RTE_ATOMIC(uint32_t)reset_events_pending;
bool reset_pending;
bool pf_reset_in_progress;
bool start_pending;
@@ -455,9 +456,10 @@ struct iavf_cmd_info {
int iavf_check_api_version(struct iavf_adapter *adapter);
int iavf_get_vf_resource(struct iavf_adapter *adapter);
-void iavf_dev_event_post(struct rte_eth_dev *dev,
+int iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size);
+bool iavf_dev_enter_reset(struct rte_eth_dev *dev, bool drain_tx);
void iavf_dev_event_handler_fini(void);
int iavf_dev_event_handler_init(void);
void iavf_handle_virtchnl_msg(struct rte_eth_dev *dev);
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 05b90a6230..83470a636d 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -335,32 +335,27 @@ iavf_dev_watchdog(void *cb_arg)
return;
/* If in reset then poll vfr_inprogress register for completion */
- if (adapter->vf.vf_reset) {
+ if (rte_atomic_load_explicit(&adapter->vf.vf_reset, rte_memory_order_acquire)) {
vfr_inprogress = iavf_vfr_inprogress(hw);
- if (!vfr_inprogress) {
+ if (!vfr_inprogress &&
+ rte_atomic_load_explicit(&adapter->vf.reset_events_pending,
+ rte_memory_order_acquire) == 0) {
PMD_DRV_LOG(INFO, "VF \"%s\" reset has completed",
dev->data->name);
- adapter->vf.vf_reset = false;
+ rte_atomic_store_explicit(&adapter->vf.vf_reset, false,
+ rte_memory_order_release);
iavf_set_no_poll(adapter, false);
}
/* If not in reset then poll vfr_inprogress register for VFLR event */
} else {
vfr_inprogress = iavf_vfr_inprogress(hw);
- if (vfr_inprogress) {
+ /* enter reset state with VFLR event */
+ if (vfr_inprogress && iavf_dev_enter_reset(dev, false))
PMD_DRV_LOG(INFO,
"VF \"%s\" reset event detected by watchdog",
dev->data->name);
-
- /* enter reset state with VFLR event */
- adapter->vf.vf_reset = true;
- iavf_set_no_poll(adapter, false);
- adapter->vf.link_up = false;
-
- iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET,
- NULL, 0);
- }
}
if (adapter->devargs.watchdog_period) {
@@ -3320,8 +3315,9 @@ iavf_dev_close(struct rte_eth_dev *dev)
* effect.
*/
out:
- if (vf->vf_reset && !rte_pci_set_bus_master(pci_dev, true)) {
- vf->vf_reset = false;
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) &&
+ !rte_pci_set_bus_master(pci_dev, true)) {
+ rte_atomic_store_explicit(&vf->vf_reset, false, rte_memory_order_release);
iavf_set_no_poll(adapter, false);
}
@@ -3657,7 +3653,7 @@ iavf_set_no_poll(struct iavf_adapter *adapter, bool link_change)
bool no_poll;
no_poll = (link_change & !vf->link_up) ||
- vf->vf_reset ||
+ rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) ||
rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed);
rte_atomic_store_explicit(&adapter->no_poll, no_poll,
@@ -3740,8 +3736,8 @@ iavf_resume_pending_start(struct rte_eth_dev *dev)
if (!vf->start_pending)
return;
- if (vf->vf_reset || rte_atomic_load_explicit(&vf->in_reset_recovery,
- rte_memory_order_acquire))
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) ||
+ rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_acquire))
return;
/*
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 34961fe654..2485f81253 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -59,6 +59,21 @@ static struct iavf_event_handler event_handler = {
(var) = (tvar))
#endif
+static void
+iavf_dev_reset_event_handle(struct iavf_event_element *elem)
+{
+ struct rte_eth_dev *dev = elem->dev;
+ struct iavf_adapter *adapter = dev->data->dev_private;
+
+ if (adapter->devargs.auto_reset)
+ iavf_handle_hw_reset(dev, false);
+ else
+ rte_eth_dev_callback_process(dev, elem->event, elem->param);
+
+ rte_atomic_fetch_sub_explicit(&adapter->vf.reset_events_pending, 1,
+ rte_memory_order_release);
+}
+
static uint32_t
iavf_dev_event_handle(void *param __rte_unused)
{
@@ -80,15 +95,10 @@ iavf_dev_event_handle(void *param __rte_unused)
TAILQ_FOREACH_SAFE(pos, &pending, next, save_next) {
TAILQ_REMOVE(&pending, pos, next);
- struct iavf_adapter *adapter = pos->dev->data->dev_private;
- if (pos->event == RTE_ETH_EVENT_INTR_RESET &&
- adapter->devargs.auto_reset) {
- iavf_handle_hw_reset(pos->dev, false);
- rte_free(pos);
- continue;
- }
-
- rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
+ if (pos->event == RTE_ETH_EVENT_INTR_RESET)
+ iavf_dev_reset_event_handle(pos);
+ else
+ rte_eth_dev_callback_process(pos->dev, pos->event, pos->param);
rte_free(pos);
}
}
@@ -96,7 +106,7 @@ iavf_dev_event_handle(void *param __rte_unused)
return 0;
}
-void
+int
iavf_dev_event_post(struct rte_eth_dev *dev,
enum rte_eth_event_type event,
void *param, size_t param_alloc_size)
@@ -105,7 +115,7 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
char notify_byte = 0;
struct iavf_event_element *elem = rte_malloc(NULL, sizeof(*elem) + param_alloc_size, 0);
if (!elem)
- return;
+ return -ENOMEM;
elem->dev = dev;
elem->event = event;
@@ -122,6 +132,55 @@ iavf_dev_event_post(struct rte_eth_dev *dev,
ssize_t nw = write(handler->fd[1], ¬ify_byte, 1);
RTE_SET_USED(nw);
+
+ return 0;
+}
+
+/*
+ * Enter the VF reset state. If this is a new reset, rather than one already
+ * in progress, gate the datapath and post the reset event.
+ * Returns true if this call started the reset.
+ */
+bool
+iavf_dev_enter_reset(struct rte_eth_dev *dev, bool drain_tx)
+{
+ struct iavf_adapter *adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ struct iavf_info *vf = &adapter->vf;
+ bool expected = false;
+
+ /*
+ * Force link down to drop the cached link-up state; a fresh LSC up
+ * event will be re-issued by the PF once the VF is reinitialised.
+ */
+ vf->link_up = false;
+
+ /*
+ * Count the event before vf_reset becomes visible. The watchdog clears
+ * vf_reset once it sees it set with no events pending, so setting the
+ * flag first would open a window in which the event is lost. If another
+ * thread has already entered reset, back the count out: no event is
+ * posted on this path.
+ */
+ rte_atomic_fetch_add_explicit(&vf->reset_events_pending, 1, rte_memory_order_relaxed);
+ if (!rte_atomic_compare_exchange_strong_explicit(&vf->vf_reset, &expected, true,
+ rte_memory_order_release, rte_memory_order_acquire)) {
+ rte_atomic_fetch_sub_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_relaxed);
+ return false;
+ }
+
+ iavf_set_no_poll(adapter, false);
+ /* drain before posting the event, as the event handler tears down the queues */
+ if (drain_tx && adapter->devargs.no_poll_on_link_down)
+ iavf_dev_tx_drain(dev);
+
+ if (iavf_dev_event_post(dev, RTE_ETH_EVENT_INTR_RESET, NULL, 0) != 0) {
+ PMD_DRV_LOG(ERR, "VF \"%s\" failed to post reset event", dev->data->name);
+ rte_atomic_fetch_sub_explicit(&vf->reset_events_pending, 1,
+ rte_memory_order_release);
+ }
+
+ return true;
}
int
@@ -260,7 +319,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 && !rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire) &&
!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed))
iavf_dev_watchdog_disable(adapter);
else
@@ -324,23 +383,8 @@ iavf_read_msg_from_pf(struct iavf_adapter *adapter, uint16_t buf_len,
iavf_handle_link_change_event(dev, vpe);
break;
case VIRTCHNL_EVENT_RESET_IMPENDING:
- /*
- * Force link down on impending reset to drop
- * the cached link-up state; a fresh LSC up
- * event will be re-issued by the PF once the
- * VF is reinitialised.
- */
- vf->link_up = false;
- if (!vf->vf_reset) {
- vf->vf_reset = true;
- iavf_set_no_poll(adapter, false);
+ if (iavf_dev_enter_reset(dev, true))
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);
- }
PMD_DRV_LOG(DEBUG, "VF is resetting");
break;
case VIRTCHNL_EVENT_PF_DRIVER_CLOSE:
@@ -485,7 +529,7 @@ iavf_execute_vf_cmd(struct iavf_adapter *adapter, struct iavf_cmd_info *args)
bool poll = iavf_op_needs_poll(vf);
int err;
- if (vf->vf_reset)
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire))
return -EIO;
resp_count = iavf_get_cmd_resp_count(args->ops);
@@ -555,7 +599,6 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
{
struct iavf_adapter *adapter =
IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
- struct iavf_info *vf = &adapter->vf;
struct virtchnl_pf_event *pf_msg =
(struct virtchnl_pf_event *)msg;
@@ -571,16 +614,8 @@ iavf_handle_pf_event_msg(struct rte_eth_dev *dev, uint8_t *msg,
switch (pf_msg->event) {
case VIRTCHNL_EVENT_RESET_IMPENDING:
PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_RESET_IMPENDING event");
- vf->link_up = false;
- if (!vf->vf_reset) {
- vf->vf_reset = true;
- iavf_set_no_poll(adapter, false);
+ if (iavf_dev_enter_reset(dev, true))
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);
- }
break;
case VIRTCHNL_EVENT_LINK_CHANGE:
PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_LINK_CHANGE event");
@@ -2394,13 +2429,13 @@ iavf_request_queues(struct rte_eth_dev *dev, uint16_t num)
/* wait for interrupt notification vf is resetting */
while (i++ < MAX_TRY_TIMES) {
- if (vf->vf_reset)
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire))
break;
iavf_msec_delay(ASQ_DELAY_MS);
}
/* request queues succeeded, vf is resetting */
- if (vf->vf_reset) {
+ if (rte_atomic_load_explicit(&vf->vf_reset, rte_memory_order_acquire)) {
PMD_DRV_LOG(INFO, "vf is resetting");
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
` (2 preceding siblings ...)
2026-11-06 9:40 ` [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
@ 2026-11-06 9:40 ` Ciara Loftus
2026-11-10 10:48 ` Bruce Richardson
2026-11-10 10:51 ` [PATCH v4 0/4] iavf reset path fixes Bruce Richardson
4 siblings, 1 reply; 20+ messages in thread
From: Ciara Loftus @ 2026-11-06 9:40 UTC (permalink / raw)
To: dev; +Cc: Ciara Loftus, stable
iavf_dev_watchdog_enable() runs on the interrupt thread from the link
change and reset impending handlers, while iavf_dev_watchdog_disable()
runs on a control thread from dev_stop and dev_close. Disable clears
`watchdog_enabled` before cancelling the alarm. An enable landing in
between sets the flag again and arms a new alarm, which the cancel then
removes. The flag is left set with no alarm armed, so every later enable
is a no-op and the watchdog is dead until the port is re-probed.
Serialise enable and disable with a spinlock held across the flag
update and the alarm set or cancel.
Fixes: 4c9b1d2ff616 ("net/iavf: refactor watchdog")
Cc: stable@dpdk.org
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
---
drivers/net/intel/iavf/iavf.h | 1 +
drivers/net/intel/iavf/iavf_ethdev.c | 37 +++++++++++++++++-----------
2 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 04c2900afe..0c98ae28e1 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -247,6 +247,7 @@ struct iavf_info {
/** iAVF watchdog enable */
bool watchdog_enabled;
+ rte_spinlock_t watchdog_lock;
/* Event from pf */
bool dev_closed;
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 83470a636d..10069feae2 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -374,16 +374,19 @@ iavf_dev_watchdog_enable(struct iavf_adapter *adapter)
{
if (!adapter->devargs.watchdog_period) {
PMD_DRV_LOG(INFO, "Device watchdog is disabled");
- } else {
- if (!adapter->vf.watchdog_enabled) {
- PMD_DRV_LOG(INFO, "Enabling device watchdog, period is %dμs",
- adapter->devargs.watchdog_period);
- adapter->vf.watchdog_enabled = true;
- if (rte_eal_alarm_set(adapter->devargs.watchdog_period,
- &iavf_dev_watchdog, (void *)adapter))
- PMD_DRV_LOG(ERR, "Failed to enable device watchdog");
- }
+ return;
+ }
+
+ rte_spinlock_lock(&adapter->vf.watchdog_lock);
+ if (!adapter->vf.watchdog_enabled) {
+ PMD_DRV_LOG(INFO, "Enabling device watchdog, period is %dμs",
+ adapter->devargs.watchdog_period);
+ adapter->vf.watchdog_enabled = true;
+ if (rte_eal_alarm_set(adapter->devargs.watchdog_period,
+ &iavf_dev_watchdog, (void *)adapter))
+ PMD_DRV_LOG(ERR, "Failed to enable device watchdog");
}
+ rte_spinlock_unlock(&adapter->vf.watchdog_lock);
}
void
@@ -391,13 +394,16 @@ iavf_dev_watchdog_disable(struct iavf_adapter *adapter)
{
if (!adapter->devargs.watchdog_period) {
PMD_DRV_LOG(INFO, "Device watchdog is not enabled");
- } else {
- if (adapter->vf.watchdog_enabled) {
- PMD_DRV_LOG(INFO, "Disabling device watchdog");
- adapter->vf.watchdog_enabled = false;
- rte_eal_alarm_cancel(&iavf_dev_watchdog, (void *)adapter);
- }
+ return;
+ }
+
+ rte_spinlock_lock(&adapter->vf.watchdog_lock);
+ if (adapter->vf.watchdog_enabled) {
+ PMD_DRV_LOG(INFO, "Disabling device watchdog");
+ adapter->vf.watchdog_enabled = false;
+ rte_eal_alarm_cancel(&iavf_dev_watchdog, (void *)adapter);
}
+ rte_spinlock_unlock(&adapter->vf.watchdog_lock);
}
static int
@@ -3077,6 +3083,7 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
adapter->mac_primary_set = false;
adapter->tpid = RTE_ETHER_TYPE_VLAN; /* VLAN TPID set to 0x8100 by default */
rte_spinlock_init(&adapter->phc_sync_lock);
+ rte_spinlock_init(&vf->watchdog_lock);
if (!rte_atomic_load_explicit(&vf->in_reset_recovery, rte_memory_order_relaxed) &&
iavf_dev_event_handler_init())
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery
2026-11-06 9:40 ` [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
@ 2026-11-10 10:47 ` Bruce Richardson
0 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-10 10:47 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, stable, Declan Doherty
On Fri, Nov 06, 2026 at 09:40:11AM +0000, Ciara Loftus wrote:
> Since the watchdog is kept armed across a PF reset, it can clear the
> `vf_reset` flag while the reset event is still queued or being handled,
> as VFGEN_RSTAT toggles while the PF resets the VF. iavf_dev_close() then
> skips re-enabling PCI bus mastering, which the VF reset cleared, so
> VIRTCHNL_OP_VERSION never reaches the PF, iavf_dev_init() fails and the
> port is left returning -EIO. Each toggle also posts a further reset
> event, causing redundant recovery cycles.
>
> Count reset events from before `vf_reset` is set until they have been
> handled, and do not let the watchdog clear the flag while any are
> outstanding. Also make the `vf_reset` flag atomic, as it is accessed from
> multiple places: the watchdog alarm, the interrupt thread and the virtchnl
> polling path.
>
> Fixes: a0e296420a70 ("net/iavf: keep watchdog armed for the whole reset window")
> Cc: stable@dpdk.org
>
> Suggested-by: Declan Doherty <declan.doherty@intel.com>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
> v4:
> * Remove overly defensive check after reset callback
> * Added a log on event post failure
> ---
> drivers/net/intel/iavf/iavf.h | 6 +-
> drivers/net/intel/iavf/iavf_ethdev.c | 32 ++++----
> drivers/net/intel/iavf/iavf_vchnl.c | 117 +++++++++++++++++----------
> 3 files changed, 94 insertions(+), 61 deletions(-)
>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable
2026-11-06 9:40 ` [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
@ 2026-11-10 10:48 ` Bruce Richardson
0 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-10 10:48 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev, stable
On Fri, Nov 06, 2026 at 09:40:12AM +0000, Ciara Loftus wrote:
> iavf_dev_watchdog_enable() runs on the interrupt thread from the link
> change and reset impending handlers, while iavf_dev_watchdog_disable()
> runs on a control thread from dev_stop and dev_close. Disable clears
> `watchdog_enabled` before cancelling the alarm. An enable landing in
> between sets the flag again and arms a new alarm, which the cancel then
> removes. The flag is left set with no alarm armed, so every later enable
> is a no-op and the watchdog is dead until the port is re-probed.
>
> Serialise enable and disable with a spinlock held across the flag
> update and the alarm set or cancel.
>
> Fixes: 4c9b1d2ff616 ("net/iavf: refactor watchdog")
> Cc: stable@dpdk.org
>
> Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
> ---
> drivers/net/intel/iavf/iavf.h | 1 +
> drivers/net/intel/iavf/iavf_ethdev.c | 37 +++++++++++++++++-----------
> 2 files changed, 23 insertions(+), 15 deletions(-)
>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4 0/4] iavf reset path fixes
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
` (3 preceding siblings ...)
2026-11-06 9:40 ` [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
@ 2026-11-10 10:51 ` Bruce Richardson
4 siblings, 0 replies; 20+ messages in thread
From: Bruce Richardson @ 2026-11-10 10:51 UTC (permalink / raw)
To: Ciara Loftus; +Cc: dev
On Fri, Nov 06, 2026 at 09:40:08AM +0000, Ciara Loftus wrote:
> Four fixes related to the reset path in iavf.
>
> v4 ( patch 3):
> * Remove overly defensive check after reset callback
> * Added a log on event post failure
>
> Ciara Loftus (4):
> net/iavf: fix crash in Tx drain on PF reset
> net/iavf: fix event handler leak on init failure
> net/iavf: fix watchdog clearing reset state during recovery
> net/iavf: fix watchdog enable racing disable
>
Series applied to dpdk-next-net-intel.
Thanks,
/Bruce
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-11-10 10:52 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 9:55 [PATCH v2 0/3] iavf reset path fixes Ciara Loftus
2026-11-04 9:55 ` [PATCH v2 1/3] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-04 17:51 ` Bruce Richardson
2026-11-04 9:55 ` [PATCH v2 2/3] net/iavf: fix event handler leak on init failure Ciara Loftus
2026-11-04 17:52 ` Bruce Richardson
2026-11-04 9:55 ` [PATCH v2 3/3] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
2026-11-04 18:00 ` Bruce Richardson
2026-11-05 12:32 ` [PATCH v3 0/4] iavf reset path fixes Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
2026-11-05 12:32 ` [PATCH v3 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 0/4] iavf reset path fixes Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 1/4] net/iavf: fix crash in Tx drain on PF reset Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 2/4] net/iavf: fix event handler leak on init failure Ciara Loftus
2026-11-06 9:40 ` [PATCH v4 3/4] net/iavf: fix watchdog clearing reset state during recovery Ciara Loftus
2026-11-10 10:47 ` Bruce Richardson
2026-11-06 9:40 ` [PATCH v4 4/4] net/iavf: fix watchdog enable racing disable Ciara Loftus
2026-11-10 10:48 ` Bruce Richardson
2026-11-10 10:51 ` [PATCH v4 0/4] iavf reset path fixes Bruce Richardson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).