From: Joshua Washington <[email protected]>

The DPDK alarm and interrupts infrastructure make use of the same global
thread for processing. This causes problems in GVE related to the device
status poller, as the reset path invoked by that poller sends mailbox
messages and blocks waiting for their completion.

When the device status poller is on the alarm thread, that blocking call
leads to a temporary lockup, as the mailbox interrupt cannot come in
while the status poller holds the global alarm thread, causing message
timeouts. Because the interrupts infrastructure is managed by DPDK
itself, that leaves moving the device status poller off of the shared
global alarm/interrupt thread as the only viable option.

Spin off a device status thread on initialization that checks for
resets. The single thread will stay alive until the driver is torn down
in gve_dev_close.

Update the polling frequency to every 100ms for quicker error recovery,
since the poller is no longer on the shared thread.

Signed-off-by: Joshua Washington <[email protected]>
Signed-off-by: Jasper Tran O'Leary <[email protected]>
---
 drivers/net/gve/base/gve.h   |  1 +
 drivers/net/gve/gve_ethdev.c | 81 ++++++++++++++++---------------
 drivers/net/gve/gve_ethdev.h | 30 +++++++++++-
 3 files changed, 72 insertions(+), 40 deletions(-)

diff --git a/drivers/net/gve/base/gve.h b/drivers/net/gve/base/gve.h
index 18363fae3..4d9890625 100644
--- a/drivers/net/gve/base/gve.h
+++ b/drivers/net/gve/base/gve.h
@@ -52,6 +52,7 @@ enum gve_state_flags_bit {
        GVE_PRIV_FLAGS_DEVICE_RINGS_OK          = 3,
        GVE_PRIV_FLAGS_FLOW_SUBSYSTEM_OK        = 4,
        GVE_PRIV_FLAGS_NAPI_ENABLED             = 5,
+       GVE_PRIV_FLAGS_STATUS_POLLER_OK         = 7,
 };
 
 enum gve_rss_hash_algorithm {
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index c2cfd18dc..7c335a72d 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -590,7 +590,6 @@ gve_dev_start(struct rte_eth_dev *dev)
                }
        }
 
-       gve_start_dev_status_polling(dev);
        ret = 0;
 
 unlock_and_return:
@@ -703,14 +702,6 @@ gve_dev_stop(struct rte_eth_dev *dev)
                return -EPERM;
        }
 
-       /*
-        * Block until all polling callbacks have concluded before tearing down
-        * any device resources. Do so before trying to acquire the reset lock
-        * because a polling callback could be invoking a reset which will
-        * itself try to acquire the lock.
-        */
-       gve_stop_dev_status_polling(dev);
-
        pthread_mutex_lock(&priv->reset_lock);
 
        dev->data->dev_started = 0;
@@ -869,6 +860,16 @@ gve_dev_close(struct rte_eth_dev *dev)
                return 0;
        }
 
+       /*
+        * Block until all polling callbacks have concluded before tearing down
+        * any device resources. Do so before trying to acquire the reset lock
+        * in gve_dev_stop() because a polling callback could be invoking a
+        * reset which will itself try to acquire the lock, leading to a
+        * deadlock.
+        */
+       if (gve_get_dev_status_poller_ok(priv))
+               gve_stop_dev_status_polling(dev);
+
        if (dev->data->dev_started) {
                err = gve_dev_stop(dev);
                if (err != 0)
@@ -1691,8 +1692,6 @@ gve_internal_recover_device(struct rte_eth_dev *dev)
                }
        }
 
-       gve_stop_dev_status_polling(dev);
-
        if (gve_is_gqi(priv))
                gve_free_stats_report(dev);
 
@@ -1772,63 +1771,59 @@ recover_fail:
        return ret;
 }
 
-static void
+static unsigned int
 gve_check_device_status(void *arg)
 {
        struct gve_process_private *process_priv;
        struct rte_eth_dev *dev = arg;
        struct gve_priv *priv = dev->data->dev_private;
-       int ret;
 
        process_priv = dev->process_private;
 
-       gve_clean_zombie_queues(priv, false);
+       while (gve_get_dev_status_poller_ok(priv)) {
+               gve_clean_zombie_queues(priv, false);
+
+               if (!process_priv->ctrl_ops->check_device_needs_reset(priv))
+                       goto sleep;
 
-       if (process_priv->ctrl_ops->check_device_needs_reset(priv)) {
                pthread_mutex_lock(&priv->reset_lock);
 
                /*
                 * Defend against TOCTOU by checking if a reset is still needed
                 * after acquiring the lock.
                 */
-               if (process_priv->ctrl_ops->check_device_needs_reset(priv)) {
-                       PMD_DRV_LOG(NOTICE,
-                                   "Port %u: Device requested reset. Executing 
proactive recovery.",
-                                   dev->data->port_id);
-
-                       ret = gve_internal_recover_device(dev);
-                       if (ret == 0) {
-                               pthread_mutex_unlock(&priv->reset_lock);
-                               return;
-                       }
-               }
+               if (!process_priv->ctrl_ops->check_device_needs_reset(priv))
+                       goto unlock;
+
+               PMD_DRV_LOG(NOTICE,
+                           "Port %u: Device requested reset. Executing 
proactive recovery.",
+                           dev->data->port_id);
 
+               gve_internal_recover_device(dev);
+unlock:
                pthread_mutex_unlock(&priv->reset_lock);
+sleep:
+               rte_delay_us_sleep(GVE_DEV_POLL_INTERVAL_US);
        }
 
-       if (dev->data->dev_started) {
-               ret = rte_eal_alarm_set(GVE_DEV_POLL_INTERVAL_US,
-                                       gve_check_device_status, dev);
-               if (ret != 0)
-                       PMD_DRV_LOG(ERR,
-                                   "Port %u: Failed to re-arm alarm poller!",
-                                   dev->data->port_id);
-       }
+       return 0;
 }
 
 static void
 gve_start_dev_status_polling(struct rte_eth_dev *dev)
 {
+       struct gve_priv *priv = dev->data->dev_private;
        int ret;
 
        if (rte_eal_process_type() != RTE_PROC_PRIMARY)
                return;
 
-       ret = rte_eal_alarm_set(GVE_DEV_POLL_INTERVAL_US,
-                               gve_check_device_status,
-                               dev);
-
+       gve_set_dev_status_poller_ok(priv);
+       ret = rte_thread_create_internal_control(&priv->dev_status_thread,
+                                                "gve-dev-status",
+                                                gve_check_device_status, dev);
        if (ret != 0) {
+               gve_clear_dev_status_poller_ok(priv);
                PMD_DRV_LOG(ERR,
                        "Port %u: Failed to arm device reset polling alarm! 
Err=%d",
                        dev->data->port_id, ret);
@@ -1842,8 +1837,12 @@ gve_start_dev_status_polling(struct rte_eth_dev *dev)
 static void
 gve_stop_dev_status_polling(struct rte_eth_dev *dev)
 {
+       struct gve_priv *priv = dev->data->dev_private;
+
        /* Blocks until all in-progress callbacks have completed. */
-       rte_eal_alarm_cancel(gve_check_device_status, dev);
+       gve_clear_dev_status_poller_ok(priv);
+
+       rte_thread_join(priv->dev_status_thread, NULL);
 }
 
 static int
@@ -1859,9 +1858,11 @@ gve_dev_reset(struct rte_eth_dev *dev)
                return -EPERM;
        }
 
+       gve_stop_dev_status_polling(dev);
        pthread_mutex_lock(&priv->reset_lock);
        ret = gve_internal_recover_device(dev);
        pthread_mutex_unlock(&priv->reset_lock);
+       gve_start_dev_status_polling(dev);
 
        return ret;
 }
@@ -2178,6 +2179,8 @@ gve_dev_init(struct rte_eth_dev *eth_dev)
 
        eth_dev->data->mac_addrs = &priv->dev_addr;
 
+       gve_start_dev_status_polling(eth_dev);
+
        return 0;
 
 free_process_priv:
diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h
index 495b60c11..560a9501b 100644
--- a/drivers/net/gve/gve_ethdev.h
+++ b/drivers/net/gve/gve_ethdev.h
@@ -66,7 +66,7 @@
        RTE_ETH_RSS_NONFRAG_IPV6_UDP |  \
        RTE_ETH_RSS_IPV6_UDP_EX)
 
-#define GVE_DEV_POLL_INTERVAL_US (1 * 1000 * 1000) /* 1 second in microseconds 
*/
+#define GVE_DEV_POLL_INTERVAL_US (100 * 1000) /* 100ms in microseconds */
 
 /* A list of pages registered with the device during setup and used by a queue
  * as buffers
@@ -386,6 +386,7 @@ struct gve_priv {
        struct gve_tx_queue **zombie_tx_queues;
        rte_spinlock_t zombie_lock;
 
+       rte_thread_t dev_status_thread;
        pthread_mutex_t reset_lock;
 
        struct gve_registers __iomem *reg_bar0; /* see gve_register.h */
@@ -581,6 +582,33 @@ gve_clear_flow_subsystem_ok(struct gve_priv *priv)
                                &priv->state_flags);
 }
 
+static inline bool
+gve_get_dev_status_poller_ok(struct gve_priv *priv)
+{
+       bool ret;
+
+       ret = !!rte_bit_relaxed_get32(GVE_PRIV_FLAGS_STATUS_POLLER_OK,
+                                     &priv->state_flags);
+       rte_atomic_thread_fence(rte_memory_order_acquire);
+       return ret;
+}
+
+static inline void
+gve_set_dev_status_poller_ok(struct gve_priv *priv)
+{
+       rte_atomic_thread_fence(rte_memory_order_release);
+       rte_bit_relaxed_set32(GVE_PRIV_FLAGS_STATUS_POLLER_OK,
+                             &priv->state_flags);
+}
+
+static inline void
+gve_clear_dev_status_poller_ok(struct gve_priv *priv)
+{
+       rte_atomic_thread_fence(rte_memory_order_release);
+       rte_bit_relaxed_clear32(GVE_PRIV_FLAGS_STATUS_POLLER_OK,
+                               &priv->state_flags);
+}
+
 int
 gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id, uint16_t 
nb_desc,
                   unsigned int socket_id, const struct rte_eth_rxconf *conf,
-- 
2.56.0.307.g5fe7bd680c-goog

Reply via email to