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

