[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