Add zombie queue infrastructure to defer freeing retired queue memory and descriptor rings across resets. This gives concurrent packet burst workers a grace period before memzones are released, preventing use-after-free.
Signed-off-by: Jasper Tran O'Leary <[email protected]> Reviewed-by: Joshua Washington <[email protected]> --- drivers/net/gve/gve_ethdev.c | 93 ++++++++++++++++++++++++++++++- drivers/net/gve/gve_ethdev.h | 23 ++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c index 47714844e..680fc9947 100644 --- a/drivers/net/gve/gve_ethdev.c +++ b/drivers/net/gve/gve_ethdev.c @@ -58,6 +58,76 @@ gve_free_txq(struct gve_priv *priv, struct gve_tx_queue *txq) gve_tx_queue_release_internal_dqo(txq); } +void +gve_add_zombie_rx_queue(struct gve_priv *priv, struct gve_rx_queue *rxq) +{ + uint16_t queue_id; + + if (!rxq) + return; + + queue_id = rxq->queue_id; + if (queue_id >= priv->max_nb_rxq) + return; + + if (priv->zombie_rx_queues[queue_id]) + gve_free_rxq(priv, priv->zombie_rx_queues[queue_id]); + + priv->zombie_rx_queues[queue_id] = rxq; + + rxq->zombie_retire_time = + rte_get_timer_cycles() + (rte_get_timer_hz() * ZOMBIE_QUEUE_TTL_S); +} + +void +gve_add_zombie_tx_queue(struct gve_priv *priv, struct gve_tx_queue *txq) +{ + uint16_t queue_id; + + if (!txq) + return; + + queue_id = txq->queue_id; + if (queue_id >= priv->max_nb_txq) + return; + + if (priv->zombie_tx_queues[queue_id]) + gve_free_txq(priv, priv->zombie_tx_queues[queue_id]); + + priv->zombie_tx_queues[queue_id] = txq; + + txq->zombie_retire_time = + rte_get_timer_cycles() + (rte_get_timer_hz() * ZOMBIE_QUEUE_TTL_S); +} + +void +gve_clean_zombie_queues(struct gve_priv *priv, bool force) +{ + uint64_t now = rte_get_timer_cycles(); + struct gve_rx_queue *rxq; + struct gve_tx_queue *txq; + int i; + + if (!priv || !priv->zombie_rx_queues || !priv->zombie_tx_queues) + return; + + for (i = 0; i < priv->max_nb_rxq; i++) { + rxq = priv->zombie_rx_queues[i]; + if (rxq && (rxq->zombie_retire_time <= now || force)) { + gve_free_rxq(priv, rxq); + priv->zombie_rx_queues[i] = NULL; + } + } + + for (i = 0; i < priv->max_nb_txq; i++) { + txq = priv->zombie_tx_queues[i]; + if (txq && (txq->zombie_retire_time <= now || force)) { + gve_free_txq(priv, txq); + priv->zombie_tx_queues[i] = NULL; + } + } +} + static int gve_alloc_using_malloc(void **bufs, uint32_t num_entries) { @@ -788,6 +858,12 @@ gve_dev_close(struct rte_eth_dev *dev) gve_teardown_flow_subsystem(priv); gve_free_queues(dev); + gve_clean_zombie_queues(priv, true); + rte_free(priv->zombie_rx_queues); + rte_free(priv->zombie_tx_queues); + priv->zombie_rx_queues = NULL; + priv->zombie_tx_queues = NULL; + gve_teardown_device_resources(priv); process_priv->ctrl_ops->free_ctrl_plane(priv); @@ -1765,7 +1841,18 @@ gve_init_priv(struct gve_priv *priv, bool skip_describe_device) sizeof(struct gve_txq_config) * priv->max_nb_txq, RTE_CACHE_LINE_SIZE); - if (!priv->rxq_configs || !priv->txq_configs) { + priv->zombie_rx_queues = + rte_zmalloc("gve_zombie_rxqs", + sizeof(struct gve_rx_queue *) * priv->max_nb_rxq, + RTE_CACHE_LINE_SIZE); + + priv->zombie_tx_queues = + rte_zmalloc("gve_zombie_txqs", + sizeof(struct gve_tx_queue *) * priv->max_nb_txq, + RTE_CACHE_LINE_SIZE); + + if (!priv->rxq_configs || !priv->txq_configs || + !priv->zombie_rx_queues || !priv->zombie_tx_queues) { err = -ENOMEM; goto free_queue_configs; } @@ -1790,6 +1877,10 @@ free_flow_subsystem: if (gve_get_flow_subsystem_ok(priv)) gve_teardown_flow_subsystem(priv); free_queue_configs: + rte_free(priv->zombie_tx_queues); + priv->zombie_tx_queues = NULL; + rte_free(priv->zombie_rx_queues); + priv->zombie_rx_queues = NULL; rte_free(priv->txq_configs); priv->txq_configs = NULL; rte_free(priv->rxq_configs); diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h index 8df4fa54c..c56a8a693 100644 --- a/drivers/net/gve/gve_ethdev.h +++ b/drivers/net/gve/gve_ethdev.h @@ -39,6 +39,8 @@ #define GVE_RX_MIN_BUF_SIZE_GQI 2048 #define GVE_RX_MAX_BUF_SIZE_GQI 4096 +#define ZOMBIE_QUEUE_TTL_S 5 + #define GVE_RSS_HASH_KEY_SIZE 40 #define GVE_RSS_INDIR_SIZE 128 @@ -199,6 +201,10 @@ struct gve_tx_queue { struct gve_tx_queue *complq; uint8_t is_gqi_qpl; + + /* For handling deferred freeing after device reset */ + struct gve_tx_queue *zombie_next; + uint64_t zombie_retire_time; }; struct gve_rx_ctx { @@ -262,6 +268,10 @@ struct gve_rx_queue { uint8_t is_gqi_qpl; bool timestamp_enabled; + + /* For handling deferred freeing after device reset */ + struct gve_rx_queue *zombie_next; + uint64_t zombie_retire_time; }; struct gve_flow { @@ -371,6 +381,10 @@ struct gve_priv { struct gve_txq_config *txq_configs; uint32_t reset_generation; + struct gve_rx_queue **zombie_rx_queues; + struct gve_tx_queue **zombie_tx_queues; + rte_spinlock_t zombie_lock; + struct gve_registers __iomem *reg_bar0; /* see gve_register.h */ rte_be32_t __iomem *db_bar; /* "array" of doorbells */ struct rte_pci_device *pci_dev; @@ -645,6 +659,15 @@ gve_tx_queue_release_internal(struct gve_tx_queue *txq); void gve_tx_queue_release_internal_dqo(struct gve_tx_queue *txq); +void +gve_add_zombie_rx_queue(struct gve_priv *priv, struct gve_rx_queue *rxq); + +void +gve_add_zombie_tx_queue(struct gve_priv *priv, struct gve_tx_queue *txq); + +void +gve_clean_zombie_queues(struct gve_priv *priv, bool force); + int gve_rx_queue_start_dqo(struct rte_eth_dev *dev, uint16_t rx_queue_id); -- 2.56.0.307.g5fe7bd680c-goog

