Report RTE_ETH_ERROR_HANDLE_MODE_PROACTIVE in device info and monitor for device reset requests during periodic status polling. When a reset is detected, spawn a dedicated control recovery thread to re-establish the control plane, re-create queues from saved configurations, redirect the fast-path operations table to prevent concurrent crashes, and emit proactive recovery DPDK lifecycle events (ERR_RECOVERING, RECOVERY_SUCCESS, RECOVERY_FAILED).
Proactive device reset supercedes the previously implemented passive device reset convention for the gve driver. Signed-off-by: Jasper Tran O'Leary <[email protected]> Reviewed-by: Joshua Washington <[email protected]> --- doc/guides/nics/gve.rst | 67 ++-- doc/guides/rel_notes/release_26_11.rst | 5 + drivers/net/gve/gve_ethdev.c | 384 ++++++++++++++------ 3 files changed, 321 insertions(+), 135 deletions(-) diff --git a/doc/guides/nics/gve.rst b/doc/guides/nics/gve.rst index c5e9ab70d..1b7a72cf1 100644 --- a/doc/guides/nics/gve.rst +++ b/doc/guides/nics/gve.rst @@ -153,37 +153,60 @@ require the DQO queue format. Device Reset ^^^^^^^^^^^^ -The driver allows an application to reset the gVNIC device. -This function will tear down and reinitialize the device's resources, -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). +The driver supports proactive error recovery +(``RTE_ETH_ERROR_HANDLE_MODE_PROACTIVE``) +for both device-requested resets and application-initiated resets +via ``rte_eth_dev_reset()``. + +A background control thread periodically polls the device status register +to detect when the device requests a reset. +When a reset is triggered, the driver automatically recovers the port +without requiring the application to reconfigure or restart the device: + +- Emits ``RTE_ETH_EVENT_ERR_RECOVERING`` and redirects Rx/Tx burst functions + to dummy handlers so concurrent datapath threads can continue polling safely. +- Tears down and re-initializes the control plane, + re-registers device resources, + and recreates Rx and Tx queues from their saved configurations. +- Restarts the device (if it was previously started), + restores the active Rx/Tx burst functions, + and emits ``RTE_ETH_EVENT_RECOVERY_SUCCESS`` + (or ``RTE_ETH_EVENT_RECOVERY_FAILED`` if recovery fails). + +Applications do not need to call ``rte_eth_dev_reset()`` in response to a +device-requested reset, +but may optionally register callbacks for the recovery lifecycle events +to monitor reset progress or handle unrecoverable failures: .. code-block:: c static int - dev_rst_req_callback(uint16_t port_id, enum rte_eth_event_type type, - void *param, void *ret_param) + dev_recovery_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 ... */ + switch (type) { + case RTE_ETH_EVENT_ERR_RECOVERING: + printf("Port %u: entering recovery\n", port_id); + break; + case RTE_ETH_EVENT_RECOVERY_SUCCESS: + printf("Port %u: recovery succeeded\n", port_id); + break; + case RTE_ETH_EVENT_RECOVERY_FAILED: + printf("Port %u: recovery failed\n", port_id); + break; + default: + break; } 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); + /* Optionally register callbacks in the main execution flow */ + rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_ERR_RECOVERING, + dev_recovery_callback, NULL); + rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_RECOVERY_SUCCESS, + dev_recovery_callback, NULL); + rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_RECOVERY_FAILED, + dev_recovery_callback, NULL); diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst index c62d9e897..abaad4677 100644 --- a/doc/guides/rel_notes/release_26_11.rst +++ b/doc/guides/rel_notes/release_26_11.rst @@ -146,6 +146,11 @@ New Features * Raised the maximum Tx and Rx ring descriptor counts from 4096/8192 to 16384 each. +* **Updated Google Virtual Ethernet (gve) ethernet driver.** + + * Added support for proactive device reset recovery + (``RTE_ETH_ERROR_HANDLE_MODE_PROACTIVE``). + * **Updated Intel iavf driver.** * Runtime Rx/Tx queue setup is now automatically disabled diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c index cfd043dbf..66a77c123 100644 --- a/drivers/net/gve/gve_ethdev.c +++ b/drivers/net/gve/gve_ethdev.c @@ -13,7 +13,6 @@ #include <ethdev_driver.h> #include <rte_cycles.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); @@ -907,50 +906,6 @@ gve_dev_close(struct rte_eth_dev *dev) return err; } -static int -gve_dev_reset(struct rte_eth_dev *dev) -{ - struct gve_process_private *process_priv = dev->process_private; - struct gve_priv *priv = dev->data->dev_private; - int err; - - if (rte_eal_process_type() != RTE_PROC_PRIMARY) { - PMD_DRV_LOG(ERR, - "Device reset on port %u not supported in secondary processes.", - dev->data->port_id); - return -EPERM; - } - - pthread_mutex_lock(&priv->reset_lock); - - /* Tear down all device resources before re-initializing. */ - if (gve_get_flow_subsystem_ok(priv)) - gve_teardown_flow_subsystem(priv); - - /* - * Note that gve_teardown_flow_subsystem does not destroy the - * flow_rule_lock. The lock is preserved across device resets and only - * destroyed on dev_close. - */ - gve_free_queues(dev); - gve_deconfigure_device_resources(priv); - process_priv->ctrl_ops->free_ctrl_plane(priv); - gve_free_device_resources(priv); - - err = gve_init_priv(priv, true); - if (err != 0) { - PMD_DRV_LOG(ERR, - "Failed to re-init device on port %u after reset.", - dev->data->port_id); - pthread_mutex_unlock(&priv->reset_lock); - return err; - } - - pthread_mutex_unlock(&priv->reset_lock); - - return 0; -} - static int gve_verify_driver_compatibility(struct gve_priv *priv) { @@ -1071,6 +1026,7 @@ gve_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) dev_info->flow_type_rss_offloads = GVE_RTE_RSS_OFFLOAD_ALL; dev_info->hash_key_size = GVE_RSS_HASH_KEY_SIZE; dev_info->reta_size = GVE_RSS_INDIR_SIZE; + dev_info->err_handle_mode = RTE_ETH_ERROR_HANDLE_MODE_PROACTIVE; return 0; } @@ -1529,63 +1485,6 @@ gve_read_clock(struct rte_eth_dev *dev, uint64_t *clock) return 0; } -static const struct eth_dev_ops gve_eth_dev_ops = { - .dev_configure = gve_dev_configure, - .dev_start = gve_dev_start, - .dev_stop = gve_dev_stop, - .dev_close = gve_dev_close, - .dev_reset = gve_dev_reset, - .dev_infos_get = gve_dev_info_get, - .rx_queue_setup = gve_rx_queue_setup, - .tx_queue_setup = gve_tx_queue_setup, - .rx_queue_release = gve_rx_queue_release, - .tx_queue_release = gve_tx_queue_release, - .rx_queue_start = gve_rx_queue_start, - .tx_queue_start = gve_tx_queue_start, - .rx_queue_stop = gve_rx_queue_stop, - .tx_queue_stop = gve_tx_queue_stop, - .flow_ops_get = gve_flow_ops_get, - .link_update = gve_link_update, - .stats_get = gve_dev_stats_get, - .stats_reset = gve_dev_stats_reset, - .mtu_set = gve_dev_mtu_set, - .xstats_get = gve_xstats_get, - .xstats_get_names = gve_xstats_get_names, - .rss_hash_update = gve_rss_hash_update, - .rss_hash_conf_get = gve_rss_hash_conf_get, - .reta_update = gve_rss_reta_update, - .reta_query = gve_rss_reta_query, -}; - -static const struct eth_dev_ops gve_eth_dev_ops_dqo = { - .dev_configure = gve_dev_configure, - .dev_start = gve_dev_start, - .dev_stop = gve_dev_stop, - .dev_close = gve_dev_close, - .dev_reset = gve_dev_reset, - .dev_infos_get = gve_dev_info_get, - .rx_queue_setup = gve_rx_queue_setup_dqo, - .tx_queue_setup = gve_tx_queue_setup_dqo, - .rx_queue_release = gve_rx_queue_release_dqo, - .tx_queue_release = gve_tx_queue_release_dqo, - .rx_queue_start = gve_rx_queue_start_dqo, - .tx_queue_start = gve_tx_queue_start_dqo, - .rx_queue_stop = gve_rx_queue_stop_dqo, - .tx_queue_stop = gve_tx_queue_stop_dqo, - .flow_ops_get = gve_flow_ops_get, - .link_update = gve_link_update, - .stats_get = gve_dev_stats_get, - .stats_reset = gve_dev_stats_reset, - .mtu_set = gve_dev_mtu_set, - .xstats_get = gve_xstats_get, - .xstats_get_names = gve_xstats_get_names, - .rss_hash_update = gve_rss_hash_update, - .rss_hash_conf_get = gve_rss_hash_conf_get, - .reta_update = gve_rss_reta_update, - .reta_query = gve_rss_reta_query, - .read_clock = gve_read_clock, -}; - static int pci_dev_msix_vec_count(struct rte_pci_device *pdev) { @@ -1708,6 +1607,171 @@ gve_set_default_ring_size_bounds(struct gve_priv *priv) priv->min_rx_desc_cnt = GVE_DEFAULT_MIN_RX_RING_SIZE; } +static int +gve_recreate_queues(struct rte_eth_dev *dev) +{ + struct gve_priv *priv = dev->data->dev_private; + uint16_t port_id = dev->data->port_id; + uint16_t i; + int ret; + + if (priv->rxq_configs == NULL || priv->txq_configs == NULL) { + PMD_DRV_LOG(ERR, "Port %u: Queue configurations not allocated", + port_id); + return -EINVAL; + } + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + struct gve_rxq_config *rx_cfg = &priv->rxq_configs[i]; + + if (!rx_cfg->allocated) + continue; + + ret = dev->dev_ops->rx_queue_setup(dev, i, rx_cfg->nb_descriptors, + rx_cfg->socket_id, + &rx_cfg->conf, + rx_cfg->mb_pool); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Port %u: Failed to re-create RX queue %u", + port_id, i); + return ret; + } + } + + for (i = 0; i < dev->data->nb_tx_queues; i++) { + struct gve_txq_config *tx_cfg = &priv->txq_configs[i]; + + if (!tx_cfg->allocated) + continue; + + ret = dev->dev_ops->tx_queue_setup(dev, i, tx_cfg->nb_descriptors, + tx_cfg->socket_id, + &tx_cfg->conf); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Port %u: Failed to re-create TX queue %u", + port_id, i); + return ret; + } + } + + return 0; +} + +static int +gve_internal_recover_device(struct rte_eth_dev *dev) +{ + struct gve_process_private *process_priv = dev->process_private; + struct gve_priv *priv = dev->data->dev_private; + uint16_t port_id = dev->data->port_id; + uint16_t i; + int ret; + + rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_ERR_RECOVERING, NULL); + + rte_eth_fp_ops[port_id].rx_pkt_burst = rte_eth_pkt_burst_dummy; + rte_eth_fp_ops[port_id].tx_pkt_burst = rte_eth_pkt_burst_dummy; + dev->rx_pkt_burst = rte_eth_pkt_burst_dummy; + dev->tx_pkt_burst = rte_eth_pkt_burst_dummy; + + /* Ensure dummy burst fns are visible before clearing queue pointers. */ + rte_atomic_thread_fence(rte_memory_order_release); + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + if (dev->data->rx_queues[i] != NULL) { + gve_add_zombie_rx_queue(priv, dev->data->rx_queues[i]); + dev->data->rx_queues[i] = NULL; + } + } + for (i = 0; i < dev->data->nb_tx_queues; i++) { + if (dev->data->tx_queues[i] != NULL) { + gve_add_zombie_tx_queue(priv, dev->data->tx_queues[i]); + dev->data->tx_queues[i] = NULL; + } + } + + gve_stop_dev_status_polling(dev); + + if (gve_is_gqi(priv)) + gve_free_stats_report(dev); + + gve_deconfigure_device_resources(priv); + + process_priv->ctrl_ops->free_ctrl_plane(priv); + + /* + * Keep device resources allocated, except for the RSS config which must + * be freed as the driver cache will be stale after reset. + */ + gve_free_rss_config(&priv->rss_config); + if (process_priv->ctrl_ops->query_rss != NULL) + priv->rss_cache_dirty = true; + + priv->reset_generation++; + priv->num_registered_pages = 0; + ret = process_priv->ctrl_ops->init_ctrl_plane(priv); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Port %u: Failed to re-init control plane after reset", + port_id); + goto recover_fail; + } + + ret = gve_configure_device_resources(priv); + if (ret != 0) + goto recover_fail; + + ret = gve_recreate_queues(dev); + if (ret != 0) + goto recover_fail; + + ret = gve_dev_start(dev); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Port %u: Failed to restart device after reset", + port_id); + goto recover_fail; + } + + if (gve_is_gqi(priv)) { + gve_set_rx_function(dev); + gve_set_tx_function(dev); + } else { + gve_set_rx_function_dqo(dev); + gve_set_tx_function_dqo(dev); + } + + rte_eth_fp_ops[port_id].rxq.data = dev->data->rx_queues; + rte_eth_fp_ops[port_id].txq.data = dev->data->tx_queues; + + /* Ensure queue data pointers are visible before restoring burst fns. */ + rte_atomic_thread_fence(rte_memory_order_release); + + rte_eth_fp_ops[port_id].rx_pkt_burst = dev->rx_pkt_burst; + rte_eth_fp_ops[port_id].tx_pkt_burst = dev->tx_pkt_burst; + + PMD_DRV_LOG(INFO, "Port %u: Proactive reset recovery successful", + port_id); + rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_RECOVERY_SUCCESS, NULL); + + return 0; + +recover_fail: + gve_free_queues(dev); + gve_deconfigure_device_resources(priv); + + process_priv->ctrl_ops->free_ctrl_plane(priv); + + rte_eth_fp_ops[port_id].rx_pkt_burst = rte_eth_pkt_burst_dummy; + rte_eth_fp_ops[port_id].tx_pkt_burst = rte_eth_pkt_burst_dummy; + PMD_DRV_LOG(ERR, "Port %u: Proactive reset recovery failed", + port_id); + rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_RECOVERY_FAILED, NULL); + + return ret; +} + static void gve_check_device_status(void *arg) { @@ -1716,22 +1780,39 @@ gve_check_device_status(void *arg) struct gve_priv *priv = dev->data->dev_private; int ret; - process_priv = rte_eth_devices[priv->port_id].process_private; + process_priv = dev->process_private; + + gve_clean_zombie_queues(priv, false); if (process_priv->ctrl_ops->check_device_needs_reset(priv)) { - 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 { + 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; + } + } + + pthread_mutex_unlock(&priv->reset_lock); + } + + if (dev->data->dev_started) { ret = rte_eal_alarm_set(GVE_DEV_POLL_INTERVAL_US, gve_check_device_status, dev); - if (ret != 0) { + if (ret != 0) PMD_DRV_LOG(ERR, - "Port %u: Failed to re-arm alarm poller!", - dev->data->port_id); - } + "Port %u: Failed to re-arm alarm poller!", + dev->data->port_id); } } @@ -1765,6 +1846,83 @@ gve_stop_dev_status_polling(struct rte_eth_dev *dev) rte_eal_alarm_cancel(gve_check_device_status, dev); } +static int +gve_dev_reset(struct rte_eth_dev *dev) +{ + struct gve_priv *priv = dev->data->dev_private; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + PMD_DRV_LOG(ERR, + "Device reset on port %u not supported in secondary processes.", + dev->data->port_id); + return -EPERM; + } + + pthread_mutex_lock(&priv->reset_lock); + ret = gve_internal_recover_device(dev); + pthread_mutex_unlock(&priv->reset_lock); + + return ret; +} + +static const struct eth_dev_ops gve_eth_dev_ops = { + .dev_configure = gve_dev_configure, + .dev_start = gve_dev_start, + .dev_stop = gve_dev_stop, + .dev_close = gve_dev_close, + .dev_reset = gve_dev_reset, + .dev_infos_get = gve_dev_info_get, + .rx_queue_setup = gve_rx_queue_setup, + .tx_queue_setup = gve_tx_queue_setup, + .rx_queue_release = gve_rx_queue_release, + .tx_queue_release = gve_tx_queue_release, + .rx_queue_start = gve_rx_queue_start, + .tx_queue_start = gve_tx_queue_start, + .rx_queue_stop = gve_rx_queue_stop, + .tx_queue_stop = gve_tx_queue_stop, + .flow_ops_get = gve_flow_ops_get, + .link_update = gve_link_update, + .stats_get = gve_dev_stats_get, + .stats_reset = gve_dev_stats_reset, + .mtu_set = gve_dev_mtu_set, + .xstats_get = gve_xstats_get, + .xstats_get_names = gve_xstats_get_names, + .rss_hash_update = gve_rss_hash_update, + .rss_hash_conf_get = gve_rss_hash_conf_get, + .reta_update = gve_rss_reta_update, + .reta_query = gve_rss_reta_query, +}; + +static const struct eth_dev_ops gve_eth_dev_ops_dqo = { + .dev_configure = gve_dev_configure, + .dev_start = gve_dev_start, + .dev_stop = gve_dev_stop, + .dev_close = gve_dev_close, + .dev_reset = gve_dev_reset, + .dev_infos_get = gve_dev_info_get, + .rx_queue_setup = gve_rx_queue_setup_dqo, + .tx_queue_setup = gve_tx_queue_setup_dqo, + .rx_queue_release = gve_rx_queue_release_dqo, + .tx_queue_release = gve_tx_queue_release_dqo, + .rx_queue_start = gve_rx_queue_start_dqo, + .tx_queue_start = gve_tx_queue_start_dqo, + .rx_queue_stop = gve_rx_queue_stop_dqo, + .tx_queue_stop = gve_tx_queue_stop_dqo, + .flow_ops_get = gve_flow_ops_get, + .link_update = gve_link_update, + .stats_get = gve_dev_stats_get, + .stats_reset = gve_dev_stats_reset, + .mtu_set = gve_dev_mtu_set, + .xstats_get = gve_xstats_get, + .xstats_get_names = gve_xstats_get_names, + .rss_hash_update = gve_rss_hash_update, + .rss_hash_conf_get = gve_rss_hash_conf_get, + .reta_update = gve_rss_reta_update, + .reta_query = gve_rss_reta_query, + .read_clock = gve_read_clock, +}; + static int gve_adminq_get_device_properties(struct gve_priv *priv) { -- 2.56.0.307.g5fe7bd680c-goog

