* [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed
@ 2026-11-14 6:25 Jiwoo Kim
2026-11-14 6:25 ` [PATCH 2/2] drm/msm: Put the VM_BIND queue last in msm_vma_job_free() Jiwoo Kim
2026-11-14 6:49 ` [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed sashiko-bot
0 siblings, 2 replies; 3+ messages in thread
From: Jiwoo Kim @ 2026-11-14 6:25 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov
Cc: Akhil P Oommen, Konrad Dybcio, Antonino Maniscalco, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie,
Simona Vetter, linux-arm-msm, dri-devel, freedreno, linux-kernel,
stable
The entity of a VM_BIND submitqueue is attached to the per-VM scheduler,
and msm_gem_vm_close() calls drm_sched_fini() on that scheduler when the
file is closed, which frees its run queues. The submitqueue itself is
only destroyed on its final put, though, and every VM_BIND job holds a
reference to it. msm_submitqueue_close() only flushes the entity, and a
queue that was closed earlier with MSM_SUBMITQUEUE_CLOSE is not flushed
at all.
The flush gives up after a timeout when the process exits, or on a
fatal signal, and it only kills the queued jobs when the process exits
because of SIGKILL. In these cases, and after MSM_SUBMITQUEUE_CLOSE,
the entity can still have a callback armed on the in-fence of a
VM_BIND job when drm_sched_fini() runs. When that fence signals,
drm_sched_entity_wakeup() dereferences entity->rq->sched, and so reads
a run queue that drm_sched_fini() has freed. If the jobs were killed,
the final put of the queue then calls drm_sched_entity_destroy(), which
reads the same freed run queue in drm_sched_entity_flush(). If they
were not, they never run, and keep the queue, its context and the VM
alive.
Destroy the VM_BIND entity when userspace closes the queue, on file
close or with MSM_SUBMITQUEUE_CLOSE, instead of on the final put, and
refuse new VM_BIND jobs on a closed queue. In the MSM_SUBMITQUEUE_CLOSE
path, do it after dropping ctxlock, as destroying the entity waits for
the jobs queued on it.
As a result, MSM_SUBMITQUEUE_CLOSE on a VM_BIND queue now waits
(killably) for the jobs already queued on it, and a fatal signal during
that wait cancels the remaining ones, where they used to still run. On
file close, the jobs still queued when the wait gives up are now always
cancelled, as they already were when the process was killed while
exiting, instead of being left on the entity.
This was found with an AI coding assistant, which also drafted this
patch and its changelog. It has not been reproduced, and the change
has only been compile-tested.
Fixes: 92395af63a99 ("drm/msm: Add VM_BIND submitqueue")
Cc: stable@vger.kernel.org
Signed-off-by: Jiwoo Kim <arpe1618@gmail.com>
---
The scoped_guard() in msm_submitqueue_remove() relies on commit
ea69d489d3a6 ("drm/msm: Rework queuelock"), so trees older than v7.3
need a manual backport.
If MSM_SUBMITQUEUE_CLOSE should rather not block, the destroy of closed
VM_BIND queues could instead be deferred to file close, e.g. with a
per-context list of them.
Not changed here: for a job that never ran, msm_vma_job_free() neither
unpins the BOs that msm_ioctl_vm_bind() pinned nor drops the GEM
references that vm_op_enqueue() took. That already happens to VM_BIND
jobs killed when the process exits, and with this patch also to the
ones cancelled by a fatal signal during MSM_SUBMITQUEUE_CLOSE.
Deferring drm_sched_fini() of the VM scheduler to msm_gem_vm_free() was
considered instead. It does not help: the stranded jobs keep the queue,
its context and the VM alive, so the entities would still have to be
destroyed at close.
drivers/gpu/drm/msm/msm_gem_vma.c | 5 +++
drivers/gpu/drm/msm/msm_gpu.h | 6 ++++
drivers/gpu/drm/msm/msm_submitqueue.c | 50 +++++++++++++++++++--------
3 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 8b702422030a..0cf518420656 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -1499,6 +1499,11 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file)
if (ret)
goto out_post_unlock;
+ if (queue->vm_bind_closed) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
if (args->flags & MSM_VM_BIND_FENCE_FD_IN) {
struct dma_fence *in_fence;
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index bc5b31eb1eca..561f2d998e99 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -596,6 +596,12 @@ struct msm_gpu_submitqueue {
struct kref ref;
struct drm_sched_entity *entity;
+ /**
+ * @vm_bind_closed: the queue was closed by userspace, and its VM_BIND
+ * entity is being or has been destroyed. Protected by @lock.
+ */
+ bool vm_bind_closed;
+
/** @_vm_bind_entity: used for @entity pointer for VM_BIND queues */
struct drm_sched_entity _vm_bind_entity[0];
};
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
index 8d550d281704..2dbe42551bd8 100644
--- a/drivers/gpu/drm/msm/msm_submitqueue.c
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -79,9 +79,6 @@ void msm_submitqueue_destroy(struct kref *kref)
idr_destroy(&queue->fence_idr);
- if (queue->entity == &queue->_vm_bind_entity[0])
- drm_sched_entity_destroy(queue->entity);
-
msm_context_put(queue->ctx);
kfree(queue);
@@ -107,6 +104,25 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_context *ctx,
return NULL;
}
+/*
+ * The VM_BIND scheduler is torn down by msm_gem_vm_close() when the file is
+ * closed, but a VM_BIND queue can outlive that, as long as a job still holds
+ * a reference to it. So destroy the VM_BIND entity here, when userspace
+ * closes the queue, rather than on the final put. Both ways a queue is taken
+ * off ctx->submitqueues come through here.
+ */
+static void msm_submitqueue_close_vm_bind(struct msm_gpu_submitqueue *queue)
+{
+ if (queue->entity != &queue->_vm_bind_entity[0])
+ return;
+
+ mutex_lock(&queue->lock);
+ queue->vm_bind_closed = true;
+ mutex_unlock(&queue->lock);
+
+ drm_sched_entity_destroy(queue->entity);
+}
+
void msm_submitqueue_close(struct msm_context *ctx)
{
struct msm_gpu_submitqueue *queue, *tmp;
@@ -119,8 +135,7 @@ void msm_submitqueue_close(struct msm_context *ctx)
* be any more user ioctls coming our way
*/
list_for_each_entry_safe(queue, tmp, &ctx->submitqueues, node) {
- if (queue->entity == &queue->_vm_bind_entity[0])
- drm_sched_entity_flush(queue->entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
+ msm_submitqueue_close_vm_bind(queue);
list_del(&queue->node);
msm_submitqueue_put(queue);
}
@@ -326,7 +341,7 @@ int msm_submitqueue_query(struct drm_device *drm, struct msm_context *ctx,
int msm_submitqueue_remove(struct msm_context *ctx, u32 id)
{
- struct msm_gpu_submitqueue *entry;
+ struct msm_gpu_submitqueue *entry, *queue = NULL;
if (!ctx)
return 0;
@@ -338,16 +353,23 @@ int msm_submitqueue_remove(struct msm_context *ctx, u32 id)
if (!id)
return -ENOENT;
- guard(rwsem_write)(&ctx->ctxlock);
-
- list_for_each_entry(entry, &ctx->submitqueues, node) {
- if (entry->id == id) {
- list_del(&entry->node);
- msm_submitqueue_put(entry);
- return 0;
+ scoped_guard(rwsem_write, &ctx->ctxlock) {
+ list_for_each_entry(entry, &ctx->submitqueues, node) {
+ if (entry->id == id) {
+ list_del(&entry->node);
+ queue = entry;
+ break;
+ }
}
}
- return -ENOENT;
+ if (!queue)
+ return -ENOENT;
+
+ /* Not under ctxlock: destroying the entity can wait for its jobs */
+ msm_submitqueue_close_vm_bind(queue);
+ msm_submitqueue_put(queue);
+
+ return 0;
}
base-commit: 1e59855738454b14021b64115f0090eafc7526ae
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 2/2] drm/msm: Put the VM_BIND queue last in msm_vma_job_free()
2026-11-14 6:25 [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
@ 2026-11-14 6:25 ` Jiwoo Kim
2026-11-14 6:49 ` [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed sashiko-bot
1 sibling, 0 replies; 3+ messages in thread
From: Jiwoo Kim @ 2026-11-14 6:25 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov
Cc: Akhil P Oommen, Konrad Dybcio, Antonino Maniscalco, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, David Airlie,
Simona Vetter, linux-arm-msm, dri-devel, freedreno, linux-kernel,
stable
msm_vma_job_free() puts the job's submitqueue and then wakes up
vm->prealloc_throttle.wait. The job does not hold a reference to the
VM of its own: the VM is kept alive by the context, which the queue
holds a reference to. When the job holds the last reference to the
queue, as it can once the file has been closed, for example when a
cancelled job is freed, the put can drop the last reference to the VM,
and the wakeup then uses a VM that msm_gem_vm_free() has released.
Since commit 01c8d1f385f7 ("drm/msm: RCU-free the scheduler-containing
ring and VM objects"), msm_gem_vm_free() frees the VM with kfree_rcu(),
but msm_vma_job_free() is not in an RCU read-side critical section, so
a grace period can still end before the wakeup, e.g. if the task is
preempted in between. Before that commit, the VM was freed right away.
Put the queue only after the last use of the VM.
This was found with an AI coding assistant, which also drafted this
patch and its changelog. It has not been reproduced, and the change
has only been compile-tested.
Fixes: b74fae5492d1 ("drm/msm: Add VM_BIND throttling")
Cc: stable@vger.kernel.org
Signed-off-by: Jiwoo Kim <arpe1618@gmail.com>
---
Independent of 1/2.
drivers/gpu/drm/msm/msm_gem_vma.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 0cf518420656..b4b284ca5b38 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -775,7 +775,6 @@ msm_vma_job_free(struct drm_sched_job *_job)
job_foreach_bo (obj, job)
drm_gem_object_put(obj);
- msm_submitqueue_put(job->queue);
dma_fence_put(job->fence);
/* In error paths, we could have unexecuted ops: */
@@ -788,6 +787,12 @@ msm_vma_job_free(struct drm_sched_job *_job)
wake_up(&vm->prealloc_throttle.wait);
+ /*
+ * The queue may hold the last reference to the context, and so to the
+ * VM, so put it only once we are done with the VM.
+ */
+ msm_submitqueue_put(job->queue);
+
kfree(job);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed
2026-11-14 6:25 [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
2026-11-14 6:25 ` [PATCH 2/2] drm/msm: Put the VM_BIND queue last in msm_vma_job_free() Jiwoo Kim
@ 2026-11-14 6:49 ` sashiko-bot
1 sibling, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-11-14 6:49 UTC (permalink / raw)
To: Jiwoo Kim; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] drm/msm: BO pin leak and VM state corruption in
msm_vma_job_free()
--
commit 805b414859330e06db0b87fe27330012423f8dcc
Author: Jiwoo Kim <arpe1618@gmail.com>
drm/msm: Destroy the VM_BIND entity when its queue is closed
This commit destroys the drm_sched_entity of a VM_BIND submitqueue when
userspace closes the queue via MSM_SUBMITQUEUE_CLOSE or file close
instead of on the final submitqueue reference drop, and rejects new
VM_BIND jobs on closed queues.
> diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
> index 8d550d281704e..2dbe42551bd8f 100644
> --- a/drivers/gpu/drm/msm/msm_submitqueue.c
> +++ b/drivers/gpu/drm/msm/msm_submitqueue.c
[ ... ]
> @@ -107,6 +104,25 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_context *ctx,
> return NULL;
> }
>
> +/*
> + * The VM_BIND scheduler is torn down by msm_gem_vm_close() when the file is
> + * closed, but a VM_BIND queue can outlive that, as long as a job still holds
> + * a reference to it. So destroy the VM_BIND entity here, when userspace
> + * closes the queue, rather than on the final put. Both ways a queue is taken
> + * off ctx->submitqueues come through here.
> + */
> +static void msm_submitqueue_close_vm_bind(struct msm_gpu_submitqueue *queue)
> +{
> + if (queue->entity != &queue->_vm_bind_entity[0])
> + return;
> +
> + mutex_lock(&queue->lock);
> + queue->vm_bind_closed = true;
> + mutex_unlock(&queue->lock);
> +
> + drm_sched_entity_destroy(queue->entity);
> +}
[Severity: High]
Does cancelling queued VM_BIND jobs in drm_sched_entity_destroy() leak
pinned GEM objects and leave job->vm out of sync with the IOMMU page
tables?
Previously, msm_submitqueue_remove() did not touch the entity, and
msm_submitqueue_close() only called drm_sched_entity_flush(), which left
remaining jobs on the entity unless the process exited via SIGKILL. Calling
drm_sched_entity_destroy() runs drm_sched_entity_fini() ->
drm_sched_entity_kill() whenever the flush times out on file close (for
example, while a job waits on an unsignaled in-fence) or is interrupted by
a fatal signal during MSM_SUBMITQUEUE_CLOSE.
Killed jobs bypass msm_vma_job_run() and go straight to
msm_vma_job_free(), which neither unpins job->bos_pinned BOs nor drops the
op->obj reference acquired by vm_op_enqueue():
drivers/gpu/drm/msm/msm_gem_vma.c:msm_vma_job_free() {
...
/* In error paths, we could have unexecuted ops: */
while (!list_empty(&job->vm_ops)) {
struct msm_vm_op *op =
list_first_entry(&job->vm_ops, struct msm_vm_op, node);
list_del(&op->node);
kfree(op);
}
...
}
In addition, vm_bind_job_prepare() has already updated the VMA state in
job->vm (marking new VMAs as mapped and closing unmapped ones), so
discarding job->vm_ops without applying the page-table updates or calling
msm_gem_vm_unusable(job->vm) leaves the VM state inconsistent while the
context can still be used after MSM_SUBMITQUEUE_CLOSE.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114062541.2001-1-arpe1618@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-14 6:49 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 6:25 [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
2026-11-14 6:25 ` [PATCH 2/2] drm/msm: Put the VM_BIND queue last in msm_vma_job_free() Jiwoo Kim
2026-11-14 6:49 ` [PATCH 1/2] drm/msm: Destroy the VM_BIND entity when its queue is closed sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.