In addition to application-initiated resets, during normal operation,
the device can indicate the need to reset by writing a value to a device
status register. This patch introduces an alarm that polls the
status of that register and informs the application of the need to
reset. The application is responsible for registering a callback that
will execute in the event that the alarm discovers that the device
requests a reset.

Signed-off-by: Jasper Tran O'Leary <[email protected]>
Reviewed-by: Joshua Washington <[email protected]>
---
Depends-on: patch-1bf64edce3 ("net/gve: add reset path")

 doc/guides/nics/gve.rst      | 32 ++++++++++++++-
 drivers/net/gve/gve_ethdev.c | 77 ++++++++++++++++++++++++++++++++++++
 drivers/net/gve/gve_ethdev.h |  3 ++
 3 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/doc/guides/nics/gve.rst b/doc/guides/nics/gve.rst
index d94e4cb..f564607 100644
--- a/doc/guides/nics/gve.rst
+++ b/doc/guides/nics/gve.rst
@@ -130,8 +130,8 @@ Security Protocols
 - Flow priorities are not supported (must be 0).
 - Masking is limited to full matches i.e. ``0x00...0`` or ``0xFF...F``.
 
-Application-Initiated Reset
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Device Reset
+^^^^^^^^^^^^
 
 The driver allows an application to reset the gVNIC device.
 This function will tear down and reinitialize the device's resources,
@@ -139,3 +139,31 @@ including queues and administrative queues.
 
 It is the application's responsibility to reinitialize
 and restart the device after resetting it.
+
+In addition, the driver supports device-requested resets, which are triggered
+when the device encounters an unrecoverable error. The driver detects this via
+a device status register and raises an ``RTE_ETH_EVENT_INTR_RESET`` event.
+
+The application must register a callback to handle this event. The callback
+should handle the reset process (reset, reconfigure, restart).
+
+.. code-block:: c
+
+   static int
+   dev_rst_req_callback(uint16_t port_id, enum rte_eth_event_type type,
+                        void *param, void *ret_param)
+   {
+       RTE_SET_USED(param);
+       RTE_SET_USED(ret_param);
+
+       if (type == RTE_ETH_EVENT_INTR_RESET) {
+           printf("Device requested reset on port %u\n", port_id);
+           rte_eth_dev_reset(port_id);
+           /* Reconfigure and restart port ... */
+       }
+       return 0;
+   }
+
+   /* Register this callback in the main execution flow */
+   rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_INTR_RESET,
+                     dev_rst_req_callback, NULL);
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index fce90ad..7970f46 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -13,6 +13,8 @@
 #include <ethdev_driver.h>
 
 static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device);
+static void gve_start_dev_status_polling(struct rte_eth_dev *dev);
+static void gve_stop_dev_status_polling(struct rte_eth_dev *dev);
 
 static void
 gve_write_version(uint8_t *driver_version_register)
@@ -411,6 +413,11 @@ gve_dev_start(struct rte_eth_dev *dev)
        struct gve_priv *priv;
        int ret;
 
+       if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+               PMD_DRV_LOG(WARNING, "Cannot start device in secondary.");
+               return -EPERM;
+       }
+
        ret = gve_start_queues(dev);
        if (ret != 0) {
                PMD_DRV_LOG(ERR, "Failed to start queues");
@@ -440,6 +447,8 @@ gve_dev_start(struct rte_eth_dev *dev)
                }
        }
 
+       gve_start_dev_status_polling(dev);
+
        return 0;
 }
 
@@ -448,6 +457,17 @@ gve_dev_stop(struct rte_eth_dev *dev)
 {
        struct gve_priv *priv = dev->data->dev_private;
 
+       if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+               PMD_DRV_LOG(WARNING, "Cannot stop device in secondary.");
+               return -EPERM;
+       }
+
+       /*
+        * Block until all polling callbacks have concluded before tearing down
+        * any device resources.
+        */
+       gve_stop_dev_status_polling(dev);
+
        dev->data->dev_started = 0;
        dev->data->dev_link.link_status = RTE_ETH_LINK_DOWN;
 
@@ -1311,6 +1331,63 @@ gve_set_default_ring_size_bounds(struct gve_priv *priv)
        priv->min_rx_desc_cnt = GVE_DEFAULT_MIN_RX_RING_SIZE;
 }
 
+static void
+gve_check_device_status(void *arg)
+{
+       struct rte_eth_dev *dev = arg;
+       struct gve_priv *priv = dev->data->dev_private;
+       uint32_t dev_status;
+       int ret;
+
+       dev_status = ioread32be(&priv->reg_bar0->device_status);
+
+       if (dev_status & GVE_DEVICE_STATUS_RESET_MASK) {
+               PMD_DRV_LOG(INFO,
+                       "Device on port %u requests a reset. Stopping device 
status polling.",
+                       dev->data->port_id);
+               rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_RESET,
+                       NULL);
+       } else {
+               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);
+               }
+       }
+}
+
+static void
+gve_start_dev_status_polling(struct rte_eth_dev *dev)
+{
+       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);
+
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Port %u: Failed to arm device reset polling alarm! 
Err=%d",
+                       dev->data->port_id, ret);
+       } else {
+               PMD_DRV_LOG(INFO,
+                       "Port %u: Armed device reset polling alarm.",
+                       dev->data->port_id);
+       }
+}
+
+static void
+gve_stop_dev_status_polling(struct rte_eth_dev *dev)
+{
+       /* Blocks until all in-progress callbacks have completed. */
+       rte_eal_alarm_cancel(gve_check_device_status, dev);
+}
+
 static int
 gve_init_priv(struct gve_priv *priv, bool skip_describe_device)
 {
diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h
index 8fd098c..0577f03 100644
--- a/drivers/net/gve/gve_ethdev.h
+++ b/drivers/net/gve/gve_ethdev.h
@@ -7,6 +7,7 @@
 
 #include <ethdev_driver.h>
 #include <ethdev_pci.h>
+#include <rte_alarm.h>
 #include <rte_ether.h>
 #include <rte_pci.h>
 #include <pthread.h>
@@ -57,6 +58,8 @@
        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 
*/
+
 /* A list of pages registered with the device during setup and used by a queue
  * as buffers
  */
-- 
2.53.0.1118.gaef5881109-goog

Reply via email to