[PATCH 1/2] drm/etnaviv: Reference count struct etnaviv_file_private
From: Christian Gmeiner
Date: Thu Jul 09 2026 - 11:00:15 EST
From: Christian Gmeiner <cgmeiner@xxxxxxxxxx>
The next commit updates per-context data from the GPU reset path, which
runs in the scheduler timeout worker. This can race with closing the DRM
file: drm_sched_entity_flush() only waits until the entity queue is
empty, it does not wait for jobs still running on the hardware. So the
context can already be freed while the reset path still needs it.
Reference count the context and let every submit hold a reference, the
same way a submit already keeps its mmu context and pid alive. No
functional change.
Signed-off-by: Christian Gmeiner <cgmeiner@xxxxxxxxxx>
---
drivers/gpu/drm/etnaviv/etnaviv_drv.c | 17 ++++++++++++++++-
drivers/gpu/drm/etnaviv/etnaviv_drv.h | 11 +++++++++++
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 5 ++++-
3 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
index 08aca9035fc1..a27ed014fb4e 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
@@ -60,6 +60,19 @@ static void load_gpu(struct drm_device *dev)
}
}
+static void etnaviv_file_private_release(struct kref *kref)
+{
+ struct etnaviv_file_private *ctx =
+ container_of(kref, struct etnaviv_file_private, refcount);
+
+ kfree(ctx);
+}
+
+void etnaviv_file_private_put(struct etnaviv_file_private *ctx)
+{
+ kref_put(&ctx->refcount, etnaviv_file_private_release);
+}
+
static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
{
struct etnaviv_drm_private *priv = dev->dev_private;
@@ -70,6 +83,8 @@ static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
if (!ctx)
return -ENOMEM;
+ kref_init(&ctx->refcount);
+
ret = xa_alloc_cyclic(&priv->active_contexts, &ctx->id, ctx,
xa_limit_32b, &priv->next_context_id, GFP_KERNEL);
if (ret < 0)
@@ -120,7 +135,7 @@ static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file)
xa_erase(&priv->active_contexts, ctx->id);
- kfree(ctx);
+ etnaviv_file_private_put(ctx);
}
/*
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h
index 55a9e745604d..cba4323ae589 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h
+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h
@@ -7,6 +7,7 @@
#define __ETNAVIV_DRV_H__
#include <linux/io.h>
+#include <linux/kref.h>
#include <linux/list.h>
#include <linux/mm_types.h>
#include <linux/sizes.h>
@@ -29,6 +30,7 @@ struct etnaviv_iommu_global;
#define ETNAVIV_SOFTPIN_START_ADDRESS SZ_4M /* must be >= SUBALLOC_SIZE */
struct etnaviv_file_private {
+ struct kref refcount;
int id;
struct etnaviv_iommu_context *mmu;
struct drm_sched_entity sched_entity[ETNA_MAX_PIPES];
@@ -53,6 +55,15 @@ struct etnaviv_drm_private {
struct etnaviv_cmdbuf *flop_reset_data_ppu;
};
+void etnaviv_file_private_put(struct etnaviv_file_private *ctx);
+
+static inline struct etnaviv_file_private *
+etnaviv_file_private_get(struct etnaviv_file_private *ctx)
+{
+ kref_get(&ctx->refcount);
+ return ctx;
+}
+
int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
struct drm_file *file);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index 1a77a09b3377..98f1f59a8b05 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -398,6 +398,9 @@ static void submit_cleanup(struct kref *kref)
put_pid(submit->pid);
+ if (submit->ctx)
+ etnaviv_file_private_put(submit->ctx);
+
kfree(submit->pmrs);
kfree(submit);
}
@@ -526,7 +529,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
if (ret)
goto err_submit_put;
- submit->ctx = file->driver_priv;
+ submit->ctx = etnaviv_file_private_get(file->driver_priv);
submit->mmu_context = etnaviv_iommu_context_get(submit->ctx->mmu);
submit->exec_state = args->exec_state;
submit->flags = args->flags;
--
2.54.0