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

Reply via email to