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: [email protected]

Suggested-by: Declan Doherty <[email protected]>
Signed-off-by: Ciara Loftus <[email protected]>
---
 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], &notify_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

Reply via email to