[PATCH 3/9] net/gve: add deferred queue memory reclamation

Jasper Tran O'Leary jtranoleary at google.com
Tue Oct 27 19:54:12 CET 2026


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 <jtranoleary at google.com>
Reviewed-by: Joshua Washington <joshwash at google.com>
---
 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



More information about the dev mailing list