[PATCH] drm/msm: Keep objects of queued VM_BIND unmaps busy

From: Julius Bairaktaris

Date: Wed Nov 11 2026 - 03:06:48 EST


A VM_BIND UNMAP closes the VMA when the ioctl runs, but the pgtable
entries stay until the bind job runs vm_unmap_op(), and the job can wait
on a caller-supplied in-fence for as long as the caller likes. In that
window the unmapped object holds neither a pin nor the job fence:
vm_bind_job_pin_objects() and vm_bind_job_attach_fences() only cover the
objects named in the userspace ops, and an UNMAP op names none. The
shrinker then sees an idle object and purges or evicts it, and
put_iova_spaces() no longer finds the closed VMA, so the GPU keeps a
mapping of pages that are freed and reused. The unmap half of a remap
and a MAP that replaces a mapping take the same path.

Reserve a fence slot on the object when an UNMAP op is queued, and add
the job fence to it along with the fences of the mapped objects, so the
shrinker treats the object as busy until the job has run.

Fixes: 2e6a8a1fe2b2 ("drm/msm: Add VM_BIND ioctl")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Julius Bairaktaris <julius@xxxxxxxxxxxxxx>
---
I used Claude (Anthropic) to help write this patch.

drivers/gpu/drm/msm/msm_gem_vma.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 8b702422030af..d12df7e42fce8 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -473,7 +473,17 @@ struct op_arg {
static int
vm_op_enqueue(struct op_arg *arg, struct msm_vm_op _op)
{
- struct msm_vm_op *op = kmalloc_obj(*op);
+ struct msm_vm_op *op;
+
+ /* Slot for the job fence, see vm_bind_job_attach_fences(): */
+ if (_op.op == MSM_VM_OP_UNMAP && _op.obj) {
+ int ret = dma_resv_reserve_fences(_op.obj->resv, 1);
+
+ if (ret)
+ return ret;
+ }
+
+ op = kmalloc_obj(*op);
if (!op)
return -ENOMEM;

@@ -1419,6 +1429,8 @@ vm_bind_job_prepare(struct msm_vm_bind_job *job)
static void
vm_bind_job_attach_fences(struct msm_vm_bind_job *job)
{
+ struct msm_vm_op *op;
+
for (unsigned i = 0; i < job->nr_ops; i++) {
struct drm_gem_object *obj = job->ops[i].obj;

@@ -1428,6 +1440,17 @@ vm_bind_job_attach_fences(struct msm_vm_bind_job *job)
dma_resv_add_fence(obj->resv, job->fence,
DMA_RESV_USAGE_KERNEL);
}
+
+ /*
+ * The VMAs of unmapped objects are already closed, so the shrinker
+ * no longer sees the mapping. Keep the objects busy until the job
+ * has removed it from the pgtables.
+ */
+ list_for_each_entry(op, &job->vm_ops, node) {
+ if (op->op == MSM_VM_OP_UNMAP && op->obj)
+ dma_resv_add_fence(op->obj->resv, job->fence,
+ DMA_RESV_USAGE_KERNEL);
+ }
}

int

base-commit: 1e59855738454b14021b64115f0090eafc7526ae
--
2.53.0