All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] drm/msm: Fix VM_BIND job, queue and entity lifetimes
@ 2026-11-14 14:18 Jiwoo Kim
  2026-11-14 14:18 ` [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job Jiwoo Kim
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Jiwoo Kim @ 2026-11-14 14:18 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov
  Cc: Akhil P Oommen, Konrad Dybcio, Anna Maniscalco,
	Julius Bairaktaris, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, stable

These fix what happens to VM_BIND jobs, queues and entities when the
file or a VM_BIND queue is closed while jobs are still pending.

Patch 1 makes a VM_BIND job that the scheduler kills before it runs
apply its queued unmaps before it drops the references that keep the
unmapped pages alive, release its BO pins, and mark the VM unusable,
as the VM no longer matches its pgtables. Patch 2 destroys the VM_BIND
entity when its queue is closed, instead of on the final put, which can
come after the VM's scheduler has been torn down. Patch 3 puts the
queue last when a job is freed, as the queue may hold the last
reference to the VM.

Changes in v3:
- Patch 1: only apply the queued ops of a job that the scheduler frees
  after killing it, once its dependencies and the previous job of its
  entity are done. The error path of msm_ioctl_vm_bind() frees the job
  right away, so v2 could apply its unmaps too early and out of order
  (Sashiko). That path is left as it is in mainline, and the title now
  says which jobs the patch is about.
- Patch 3: rebased on patch 1, title adjusted.

Changes in v2:
- New patch 1, for an issue that Sashiko reported on v1 patch 1, which
  that patch makes easier to hit.
- Patch 2: changelog refers to patch 1.

v2: https://lore.kernel.org/r/20261114081902.1669-1-arpe1618@gmail.com
v1: https://lore.kernel.org/r/20261114062541.2001-1-arpe1618@gmail.com

Jiwoo Kim (3):
  drm/msm: Release the pins and ops of a killed VM_BIND job
  drm/msm: Destroy the VM_BIND entity when its queue is closed
  drm/msm: Put the VM_BIND queue last when freeing a VM_BIND job

 drivers/gpu/drm/msm/msm_gem_vma.c     | 99 +++++++++++++++++++++++----
 drivers/gpu/drm/msm/msm_gpu.h         |  6 ++
 drivers/gpu/drm/msm/msm_submitqueue.c | 50 ++++++++++----
 3 files changed, 128 insertions(+), 27 deletions(-)


base-commit: 1e59855738454b14021b64115f0090eafc7526ae
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job
  2026-11-14 14:18 [PATCH v3 0/3] drm/msm: Fix VM_BIND job, queue and entity lifetimes Jiwoo Kim
@ 2026-11-14 14:18 ` Jiwoo Kim
  2026-11-14 14:49   ` sashiko-bot
  2026-11-14 14:18 ` [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
  2026-11-14 14:18 ` [PATCH v3 3/3] drm/msm: Put the VM_BIND queue last when freeing a VM_BIND job Jiwoo Kim
  2 siblings, 1 reply; 6+ messages in thread
From: Jiwoo Kim @ 2026-11-14 14:18 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov
  Cc: Akhil P Oommen, Konrad Dybcio, Anna Maniscalco,
	Julius Bairaktaris, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, stable

msm_vma_job_run() applies the pgtable updates that vm_bind_job_prepare()
queued for a VM_BIND job, drops the GEM object references that
vm_op_enqueue() took for them, and unpins the BOs of the job. When the
entity of a job is killed, e.g. on SIGKILL while the process exits, the
scheduler frees the job without running it. msm_vma_job_free() then
frees the queued ops without applying them and without dropping their
references, and leaves the BOs pinned. Besides the leaks, the VM no
longer matches its pgtables: for example, the pages of a VMA that was
unmapped from the VM are still mapped.

The scheduler only frees a killed job once its dependencies have
signalled and the job queued before it on the same entity has run or
been killed, so the updates of the jobs that ran before it have all been
applied by then. In msm_vma_job_free(), apply the queued updates the way
msm_vma_job_run() does on error: skip the maps, but apply the unmaps, so
that their references can be dropped without freeing pages that are
still mapped, and mark the VM unusable. Killed jobs can be freed
concurrently, but as they only unmap, their order does not matter. Move
that part of msm_vma_job_run() into a helper for this. Also clear
bos_pinned once msm_vma_job_run() has unpinned the BOs, as msm_job_run()
does for submits, and unpin the BOs of a job that still has them pinned,
taking the object lock, as the job's fence does not keep them busy
anymore.

msm_ioctl_vm_bind() frees a job that fails right away, before its
dependencies and the previous jobs of its queue have completed, so the
queued updates of such a job must not be applied. Move the freeing
itself into vm_bind_job_free(), and call that directly from there.

The patch and its changelog were drafted with an AI coding assistant.
The issue has not been reproduced, and the change has only been
compile-tested.

Fixes: 2e6a8a1fe2b2 ("drm/msm: Add VM_BIND ioctl")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/r/sashiko-outbox-214234@kernel.org
Signed-off-by: Jiwoo Kim <arpe1618@gmail.com>
---
At commit 2e6a8a1fe2b2, msm_vma_job_run() still unpinned the BOs under
the object lock, and msm_gem_vm_unusable() did not exist yet, so older
stable trees need an adapted backport.

Julius Bairaktaris' "drm/msm: Keep objects of queued VM_BIND unmaps
busy" keeps the objects of queued unmaps busy with the job's fence:
https://lore.kernel.org/r/20261111080619.244138-1-julius@bairaktaris.de
For a killed job, the scheduler signals that fence before it calls
free_job(), so with both patches, these objects are no longer busy for
the short time until this patch applies the unmaps. Without this patch,
the unmaps of a killed job are never applied. Closing that window would
need the objects to stay pinned until their unmaps are applied. The two
patches apply in either order.

Not addressed here, as before this patch:
- The error path of msm_ioctl_vm_bind() still frees the queued ops of a
  failed job without applying them or dropping their references. Their
  unmaps could only be applied after the job's dependencies and the
  previous jobs of its queue, i.e. by queuing the job anyway.
- The issues in the error handling of vm_bind_job_prepare() that
  Sashiko listed for v2: a VMA left in the VM when vm_op_enqueue()
  fails, which a later remap of a MAP_NULL area turns into a NULL
  dereference, and the vm_bo reference in msm_gem_vm_sm_step_remap().

 drivers/gpu/drm/msm/msm_gem_vma.c | 87 ++++++++++++++++++++++++++-----
 1 file changed, 75 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 8b702422030a..0b9f2e8916b7 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -700,16 +700,16 @@ static const struct drm_gpuvm_ops msm_gpuvm_ops = {
 	.sm_step_unmap = msm_gem_vm_sm_step_unmap,
 };
 
-static struct dma_fence *
-msm_vma_job_run(struct drm_sched_job *_job)
+/*
+ * Apply the pgtable updates queued for a job, and drop the references that
+ * the ops hold.  If ret is an error, or one occurs, the remaining maps are
+ * skipped, but the unmaps are still applied, as the pages they unmap must
+ * not be freed while they are still mapped.
+ */
+static int
+vm_bind_job_apply_ops(struct msm_vm_bind_job *job, int ret)
 {
-	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
-	struct drm_device *dev = job->vm->drm;
 	struct msm_gem_vm *vm = to_msm_vm(job->vm);
-	struct drm_gem_object *obj;
-	int ret = vm->unusable ? -EINVAL : 0;
-
-	vm_dbg("");
 
 	mutex_lock(&vm->mmu_lock);
 	vm->mmu->prealloc = &job->prealloc;
@@ -740,6 +740,22 @@ msm_vma_job_run(struct drm_sched_job *_job)
 	vm->mmu->prealloc = NULL;
 	mutex_unlock(&vm->mmu_lock);
 
+	return ret;
+}
+
+static struct dma_fence *
+msm_vma_job_run(struct drm_sched_job *_job)
+{
+	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
+	struct drm_device *dev = job->vm->drm;
+	struct msm_gem_vm *vm = to_msm_vm(job->vm);
+	struct drm_gem_object *obj;
+	int ret;
+
+	vm_dbg("");
+
+	ret = vm_bind_job_apply_ops(job, vm->unusable ? -EINVAL : 0);
+
 	/*
 	 * We failed to perform at least _some_ of the pgtable updates, so
 	 * now the VM is in an undefined state.  Game over!
@@ -753,16 +769,22 @@ msm_vma_job_run(struct drm_sched_job *_job)
 		msm_gem_unpin_active(obj);
 	}
 
+	job->bos_pinned = false;
+
 	mutex_unlock(&dev->gem_lru_mutex);
 
 	/* VM_BIND ops are synchronous, so no fence to wait on: */
 	return NULL;
 }
 
+/*
+ * Free a job, whether it ran or not.  Ops that are still queued are freed
+ * without being applied, and without dropping their object references, as
+ * the pages of their unmaps may still be mapped.
+ */
 static void
-msm_vma_job_free(struct drm_sched_job *_job)
+vm_bind_job_free(struct msm_vm_bind_job *job)
 {
-	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
 	struct msm_gem_vm *vm = to_msm_vm(job->vm);
 	struct drm_gem_object *obj;
 
@@ -770,7 +792,20 @@ msm_vma_job_free(struct drm_sched_job *_job)
 
 	atomic_sub(job->prealloc.count, &vm->prealloc_throttle.in_flight);
 
-	drm_sched_job_cleanup(_job);
+	drm_sched_job_cleanup(&job->base);
+
+	/*
+	 * A job that was killed before it ran still has its BOs pinned.  The
+	 * job's fence does not keep them busy anymore, so take the object lock
+	 * to unpin them.
+	 */
+	if (job->bos_pinned) {
+		job_foreach_bo(obj, job) {
+			msm_gem_lock(obj);
+			msm_gem_unpin_locked(obj);
+			msm_gem_unlock(obj);
+		}
+	}
 
 	job_foreach_bo (obj, job)
 		drm_gem_object_put(obj);
@@ -791,6 +826,29 @@ msm_vma_job_free(struct drm_sched_job *_job)
 	kfree(job);
 }
 
+static void
+msm_vma_job_free(struct drm_sched_job *_job)
+{
+	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
+
+	/*
+	 * A job that the scheduler frees without having run it was killed,
+	 * e.g. because its entity was destroyed.  Its queued updates were
+	 * already applied to the VM's address space, but not to its pgtables.
+	 * The scheduler has waited for the job's dependencies, and for the job
+	 * before it on the entity to run or be killed, so apply its unmaps now,
+	 * before the references to the unmapped objects are dropped, and give
+	 * up on the VM, as its maps are skipped.  Killed jobs only unmap, so
+	 * the order in which they are freed does not matter.
+	 */
+	if (!list_empty(&job->vm_ops)) {
+		vm_bind_job_apply_ops(job, -ECANCELED);
+		msm_gem_vm_unusable(job->vm);
+	}
+
+	vm_bind_job_free(job);
+}
+
 static const struct drm_sched_backend_ops msm_vm_bind_ops = {
 	.run_job = msm_vma_job_run,
 	.free_job = msm_vma_job_free
@@ -1607,8 +1665,13 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file)
 	}
 
 	if (!IS_ERR_OR_NULL(job)) {
+		/*
+		 * Not msm_vma_job_free(): the job's dependencies and the
+		 * previous jobs of the queue may not have completed, so its
+		 * queued updates must not be applied here.
+		 */
 		if (ret)
-			msm_vma_job_free(&job->base);
+			vm_bind_job_free(job);
 	} else {
 		/*
 		 * If the submit hasn't yet taken ownership of the queue
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed
  2026-11-14 14:18 [PATCH v3 0/3] drm/msm: Fix VM_BIND job, queue and entity lifetimes Jiwoo Kim
  2026-11-14 14:18 ` [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job Jiwoo Kim
@ 2026-11-14 14:18 ` Jiwoo Kim
  2026-11-14 14:56   ` sashiko-bot
  2026-11-14 14:18 ` [PATCH v3 3/3] drm/msm: Put the VM_BIND queue last when freeing a VM_BIND job Jiwoo Kim
  2 siblings, 1 reply; 6+ messages in thread
From: Jiwoo Kim @ 2026-11-14 14:18 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov
  Cc: Akhil P Oommen, Konrad Dybcio, Anna Maniscalco,
	Julius Bairaktaris, 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. With the previous patch,
such jobs apply their unmaps and release their pins and references.

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. That would also keep the ioctl out of a
killable wait for the queued jobs, which the freezer cannot interrupt.

 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 0b9f2e8916b7..ce9d97582df5 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -1557,6 +1557,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;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 3/3] drm/msm: Put the VM_BIND queue last when freeing a VM_BIND job
  2026-11-14 14:18 [PATCH v3 0/3] drm/msm: Fix VM_BIND job, queue and entity lifetimes Jiwoo Kim
  2026-11-14 14:18 ` [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job Jiwoo Kim
  2026-11-14 14:18 ` [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
@ 2026-11-14 14:18 ` Jiwoo Kim
  2 siblings, 0 replies; 6+ messages in thread
From: Jiwoo Kim @ 2026-11-14 14:18 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov
  Cc: Akhil P Oommen, Konrad Dybcio, Anna Maniscalco,
	Julius Bairaktaris, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, stable

vm_bind_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
killed 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 vm_bind_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>
---
 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 ce9d97582df5..07f427c4afad 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -810,7 +810,6 @@ vm_bind_job_free(struct msm_vm_bind_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: */
@@ -823,6 +822,12 @@ vm_bind_job_free(struct msm_vm_bind_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] 6+ messages in thread

* Re: [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job
  2026-11-14 14:18 ` [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job Jiwoo Kim
@ 2026-11-14 14:49   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-14 14:49 UTC (permalink / raw)
  To: Jiwoo Kim; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/msm: race between msm_vma_job_free() and
  msm_vma_job_run() on shared VM
--

commit cd5ac020137d320cb412f26a0028c95401b23927
Author: Jiwoo Kim <arpe1618@gmail.com>

drm/msm: Release the pins and ops of a killed VM_BIND job

This commit applies queued unmap operations, drops GEM object references,
and unpins BOs when the DRM scheduler frees a killed VM_BIND job without
running it.

> diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
> index 8b702422030af..0b9f2e8916b7a 100644
> --- a/drivers/gpu/drm/msm/msm_gem_vma.c
> +++ b/drivers/gpu/drm/msm/msm_gem_vma.c

[ ... ]

> @@ -740,6 +740,22 @@ msm_vma_job_run(struct drm_sched_job *_job)
>  	vm->mmu->prealloc = NULL;
>  	mutex_unlock(&vm->mmu_lock);
>  
> +	return ret;
> +}
> +
> +static struct dma_fence *
> +msm_vma_job_run(struct drm_sched_job *_job)
> +{
> +	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
> +	struct drm_device *dev = job->vm->drm;
> +	struct msm_gem_vm *vm = to_msm_vm(job->vm);
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	vm_dbg("");
> +
> +	ret = vm_bind_job_apply_ops(job, vm->unusable ? -EINVAL : 0);

[ ... ]

> @@ -791,6 +826,29 @@ msm_vma_job_free(struct drm_sched_job *_job)
>  	kfree(job);
>  }
>  
> +static void
> +msm_vma_job_free(struct drm_sched_job *_job)
> +{
> +	struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
> +
> +	/*
> +	 * A job that the scheduler frees without having run it was killed,
> +	 * e.g. because its entity was destroyed.  Its queued updates were
> +	 * already applied to the VM's address space, but not to its pgtables.
> +	 * The scheduler has waited for the job's dependencies, and for the job
> +	 * before it on the entity to run or be killed, so apply its unmaps now,
> +	 * before the references to the unmapped objects are dropped, and give
> +	 * up on the VM, as its maps are skipped.  Killed jobs only unmap, so
> +	 * the order in which they are freed does not matter.
> +	 */
> +	if (!list_empty(&job->vm_ops)) {
> +		vm_bind_job_apply_ops(job, -ECANCELED);
> +		msm_gem_vm_unusable(job->vm);
> +	}

[Severity: Medium]
Can a dependent VM_BIND job on another entity of the same VM run before
msm_vma_job_free() applies a killed job's unmaps and marks the VM
unusable?

Multiple MSM_SUBMITQUEUE_VM_BIND queues share the same msm_gem_vm and
vm->sched.  When queue 1 is destroyed while job1 (which unmaps an IOVA
range) is waiting on an in-fence, drm_sched_entity_kill_jobs_work()
signals job1's finished fence before calling free_job().  If job2 on
queue 2 depends on job1's out-fence and maps the same IOVA range,
signaling job1's fence wakes queue 2 on vm->sched.submit_wq while
msm_vma_job_free(job1) runs on system_wq:

system_wq (queue 1 killed)        vm->sched.submit_wq (queue 2)
drm_sched_entity_kill_jobs_work()
  drm_sched_fence_finished(job1)
    ...
      drm_sched_entity_wakeup()
                                  drm_sched_run_job_work()
                                    msm_vma_job_run(job2)
                                      vm->unusable is false
                                      vm_bind_job_apply_ops(job2, 0)
                                        vm_map_op()
  msm_vma_job_free(job1)
    vm_bind_job_apply_ops(job1, -ECANCELED)
      vm_unmap_op()               <-- unmaps job2's new PTEs
    msm_gem_vm_unusable(vm)

job2's vm_map_op() either collides with PTEs that job1 has not unmapped
yet, or succeeds before job1's delayed vm_unmap_op() tears down the PTEs
that job2 just installed.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114141851.134290-1-arpe1618@gmail.com?part=1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed
  2026-11-14 14:18 ` [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
@ 2026-11-14 14:56   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-14 14:56 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: out-of-order VM_BIND ops when killing entity in
  close_vm_bind()
--

commit 85e072e9d1eb16c143e9ba2cbe9ba1651898e394
Author: Jiwoo Kim <arpe1618@gmail.com>

drm/msm: Destroy the VM_BIND entity when its queue is closed

This commit destroys a VM_BIND submitqueue's scheduler entity when the queue
is closed via MSM_SUBMITQUEUE_CLOSE or file close rather than on the final
kref put, 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]
Can destroying a single VM_BIND entity here cause page table updates on a
sibling VM_BIND queue of the same VM to run out of order with a killed job's
cleanup?

All VM_BIND queues in a context share ctx->vm and synchronously update its
drm_gpuvm state during msm_ioctl_vm_bind(), while deferring the actual IOMMU
page table updates to the scheduler.  When drm_sched_entity_destroy() is
interrupted by a fatal signal in msm_submitqueue_remove() (or times out or
is killed in msm_submitqueue_close()), drm_sched_entity_fini() kills the
entity's remaining jobs while sibling VM_BIND queues on the same VM can still
run jobs on vm->sched.

Because msm_gem_vm_unusable() is only called inside msm_vma_job_free() after
a killed job's in-fences signal and after vm_bind_job_apply_ops(job,
-ECANCELED) finishes, vm->unusable stays false in the meantime.  Even when a
job J2 on queue Q2 depends on the out-fence of job J1 on queue Q1,
drm_sched_entity_kill_jobs_work() signals J1's scheduled and finished fences
before calling free_job():

CPU0 (system_wq)                  CPU1 (vm->sched.submit_wq)
drm_sched_entity_kill_jobs_work()
  drm_sched_fence_scheduled(J1)
  drm_sched_fence_finished(J1)
                                  msm_vma_job_run(J2)
                                    /* vm->unusable is still false */
                                    vm_bind_job_apply_ops(J2, 0)
                                      vm_map_op(iova, BO_B)
  msm_vma_job_free(J1)
    vm_bind_job_apply_ops(J1, -ECANCELED)
      vm_unmap_op(iova, BO_0)     <-- unmaps BO_B's live mapping
    msm_gem_vm_unusable(job->vm)

Conversely, if J1 on Q2 maps BO_B at iova and J2 on Q1 unmaps it (removing
the VMA from drm_gpuvm in vm_bind_job_prepare()), killing Q1 lets
msm_vma_job_free(J2) run vm_unmap_op() before msm_vma_job_run(J1) acquires
vm->mmu_lock (having already read vm->unusable as false):

drivers/gpu/drm/msm/msm_gem_vma.c:msm_vma_job_run() {
    ...
	ret = vm_bind_job_apply_ops(job, vm->unusable ? -EINVAL : 0);
}

J1 then maps BO_B into the IOMMU page tables after J2's unmap has already
executed and after the VMA and GEM object references have been dropped,
leaving freed physical pages mapped in the GPU IOMMU.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114141851.134290-1-arpe1618@gmail.com?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-14 14:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 14:18 [PATCH v3 0/3] drm/msm: Fix VM_BIND job, queue and entity lifetimes Jiwoo Kim
2026-11-14 14:18 ` [PATCH v3 1/3] drm/msm: Release the pins and ops of a killed VM_BIND job Jiwoo Kim
2026-11-14 14:49   ` sashiko-bot
2026-11-14 14:18 ` [PATCH v3 2/3] drm/msm: Destroy the VM_BIND entity when its queue is closed Jiwoo Kim
2026-11-14 14:56   ` sashiko-bot
2026-11-14 14:18 ` [PATCH v3 3/3] drm/msm: Put the VM_BIND queue last when freeing a VM_BIND job Jiwoo Kim

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.