dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] drm/msm: fix dma-buf sharing on targets without per-process pgtables
@ 2026-08-22  8:48 Dmitry Baryshkov
  2026-08-22  8:48 ` [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper Dmitry Baryshkov
  2026-08-22  8:48 ` [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close Dmitry Baryshkov
  0 siblings, 2 replies; 5+ messages in thread
From: Dmitry Baryshkov @ 2026-08-22  8:48 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
	Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
	Konrad Dybcio, Akhil P Oommen, Sumit Semwal, Christian König,
	Antonino Maniscalco
  Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, linux-media,
	linaro-mm-sig

Targets without per-process pgtables -- everything pre-a6xx, plus a6xx
parts where split pagetables are unavailable -- hand every DRM file the
GPU's global VM.  The rest of the driver still treats a context VM as
private to that context, so closing a handle tears down mappings other
files are still using, and frees the iova for immediate reuse.

A dma-buf shared between two clients therefore keeps sampling an address
that the next allocation has taken over.  On a530 this is every
ext_image_dma_buf_import sampling test, reading back all zeros.

A VM shared between contexts needs the lazy, refcounted teardown the
driver already implements for kms->vm.  Rather than adding a second
mechanism, the first patch makes that machinery reusable and the second
one puts the shared GPU VM behind it.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Dmitry Baryshkov (2):
      drm/msm: factor out a locking put_iova_spaces() wrapper
      drm/msm: don't tear down shared VM mappings on handle close

 drivers/gpu/drm/msm/msm_gem.c | 45 ++++++++++++++++++++++++++++---------------
 drivers/gpu/drm/msm/msm_gpu.c |  4 +++-
 drivers/gpu/drm/msm/msm_gpu.h |  3 +++
 3 files changed, 36 insertions(+), 16 deletions(-)
---
base-commit: 6b8c8af514d739d0335f5579b585e02babe8a727
change-id: 20260822-msm-fix-export-c4a4cd9239fc

Best regards,
--  
With best wishes
Dmitry


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

* [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper
  2026-08-22  8:48 [PATCH 0/2] drm/msm: fix dma-buf sharing on targets without per-process pgtables Dmitry Baryshkov
@ 2026-08-22  8:48 ` Dmitry Baryshkov
  2026-08-22  9:03   ` sashiko-bot
  2026-08-22  8:48 ` [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close Dmitry Baryshkov
  1 sibling, 1 reply; 5+ messages in thread
From: Dmitry Baryshkov @ 2026-08-22  8:48 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
	Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
	Konrad Dybcio, Akhil P Oommen, Sumit Semwal, Christian König,
	Antonino Maniscalco
  Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, linux-media,
	linaro-mm-sig

put_iova_spaces() asserts that the caller already holds the VM and object
locks, but is not named accordingly.  Both of the callers which do not
hold those locks yet -- msm_gem_close() and msm_gem_vma_put() -- open-code
the same lock, tear down, unlock sequence around it.

Rename it to put_iova_spaces_locked() and give the plain name to a wrapper
taking both locks, mirroring the get_and_pin_iova_range_locked() /
msm_gem_get_and_pin_iova_range() pair in the same file.  No functional
change.

Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/gpu/drm/msm/msm_gem.c | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index c4cff3d53d81..f90afffe3442 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -47,13 +47,23 @@ static int msm_gem_open(struct drm_gem_object *obj, struct drm_file *file)
 	return 0;
 }
 
+static void put_iova_spaces_locked(struct drm_gem_object *obj,
+				   struct drm_gpuvm *vm, bool close,
+				   const char *reason);
+
 static void put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
-			    bool close, const char *reason);
+			    bool close, const char *reason)
+{
+	struct drm_exec exec;
+
+	msm_gem_lock_vm_and_obj(&exec, obj, vm);
+	put_iova_spaces_locked(obj, vm, close, reason);
+	drm_exec_fini(&exec);     /* drop locks */
+}
 
 static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
 {
 	struct msm_context *ctx = file->driver_priv;
-	struct drm_exec exec;
 
 	update_ctx_mem(file, -obj->size);
 	msm_gem_vma_put(obj);
@@ -81,9 +91,7 @@ static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
 	dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_BOOKKEEP, false,
 			      MAX_SCHEDULE_TIMEOUT);
 
-	msm_gem_lock_vm_and_obj(&exec, obj, ctx->vm);
 	put_iova_spaces(obj, ctx->vm, true, "close");
-	drm_exec_fini(&exec);     /* drop locks */
 }
 
 /*
@@ -106,11 +114,7 @@ void msm_gem_vma_put(struct drm_gem_object *obj)
 		return;
 
 #ifdef CONFIG_DRM_MSM_KMS
-	struct drm_exec exec;
-
-	msm_gem_lock_vm_and_obj(&exec, obj, priv->kms->vm);
 	put_iova_spaces(obj, priv->kms->vm, true, "vma_put");
-	drm_exec_fini(&exec);     /* drop locks */
 #endif
 }
 
@@ -409,8 +413,8 @@ static struct drm_gpuva *lookup_vma(struct drm_gem_object *obj,
  * mapping.
  */
 static void
-put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
-		bool close, const char *reason)
+put_iova_spaces_locked(struct drm_gem_object *obj, struct drm_gpuvm *vm,
+		       bool close, const char *reason)
 {
 	struct drm_gpuvm_bo *vm_bo, *tmp;
 
@@ -669,7 +673,7 @@ void msm_gem_unpin_iova(struct drm_gem_object *obj, struct drm_gpuvm *vm)
 		msm_gem_unpin_locked(obj);
 	}
 	if (!is_kms_vm(vm))
-		put_iova_spaces(obj, vm, true, "close");
+		put_iova_spaces_locked(obj, vm, true, "close");
 	drm_exec_fini(&exec);     /* drop locks */
 }
 
@@ -831,7 +835,7 @@ void msm_gem_purge(struct drm_gem_object *obj)
 	GEM_WARN_ON(!is_purgeable(msm_obj));
 
 	/* Get rid of any iommu mapping(s): */
-	put_iova_spaces(obj, NULL, false, "purge");
+	put_iova_spaces_locked(obj, NULL, false, "purge");
 
 	msm_gem_vunmap(obj);
 
@@ -869,7 +873,7 @@ void msm_gem_evict(struct drm_gem_object *obj)
 	GEM_WARN_ON(is_unevictable(msm_obj));
 
 	/* Get rid of any iommu mapping(s): */
-	put_iova_spaces(obj, NULL, false, "evict");
+	put_iova_spaces_locked(obj, NULL, false, "evict");
 
 	drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
 
@@ -1082,7 +1086,7 @@ static void msm_gem_free_object(struct drm_gem_object *obj)
 				drm_exec_retry_on_contention(&exec);
 			}
 		}
-		put_iova_spaces(obj, NULL, true, "free");
+		put_iova_spaces_locked(obj, NULL, true, "free");
 		drm_exec_fini(&exec);     /* drop locks */
 	}
 

-- 
2.47.3


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

* [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close
  2026-08-22  8:48 [PATCH 0/2] drm/msm: fix dma-buf sharing on targets without per-process pgtables Dmitry Baryshkov
  2026-08-22  8:48 ` [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper Dmitry Baryshkov
@ 2026-08-22  8:48 ` Dmitry Baryshkov
  2026-08-22  9:07   ` sashiko-bot
  1 sibling, 1 reply; 5+ messages in thread
From: Dmitry Baryshkov @ 2026-08-22  8:48 UTC (permalink / raw)
  To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
	Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
	Konrad Dybcio, Akhil P Oommen, Sumit Semwal, Christian König,
	Antonino Maniscalco
  Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, linux-media,
	linaro-mm-sig

On targets (like A530 / MSM8996) without per-process pgtables
msm_gpu_create_private_vm() uses the global VM, so all DRM files share
one GPU address space.  msm_gem_close() unmaps the object from ctx->vm,
which on those targets pulls the buffer out from under every other file
that still has it open, and frees the iova for immediate reuse.

A dma-buf imported into a second file hits this as soon as the exporter
closes its handle: the importer's texture keeps sampling the old
address, which the next allocation has taken over.  On a530 this is
every ext_image_dma_buf_import sampling test, reading back all zeros.

The VMA teardown a shared VM needs is the one already used for kms->vm
-- defer it to the @vma_ref drop, when the last handle and dma_buf
reference are gone.  That restores the pre-drm_gpuvm lifetime without
reintroducing the reference loop, since a BO with a live vma_ref is held
by userspace anyway.

Fixes: 111fdd2198e6 ("drm/msm: drm_gpuvm conversion")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/gpu/drm/msm/msm_gem.c | 13 ++++++++++++-
 drivers/gpu/drm/msm/msm_gpu.c |  4 +++-
 drivers/gpu/drm/msm/msm_gpu.h |  3 +++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index f90afffe3442..e73cf49c360e 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -63,6 +63,7 @@ static void put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
 
 static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
 {
+	struct msm_drm_private *priv = obj->dev->dev_private;
 	struct msm_context *ctx = file->driver_priv;
 
 	update_ctx_mem(file, -obj->size);
@@ -84,6 +85,10 @@ static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
 	if (msm_context_is_vmbind(ctx))
 		return;
 
+	/* A global VM's VMAs are torn down by the @vma_ref drop above */
+	if (priv->gpu && ctx->vm == priv->gpu->vm)
+		return;
+
 	/*
 	 * TODO we might need to kick this to a queue to avoid blocking
 	 * in CLOSE ioctl
@@ -95,7 +100,7 @@ static void msm_gem_close(struct drm_gem_object *obj, struct drm_file *file)
 }
 
 /*
- * Get/put for kms->vm VMA
+ * Get/put for VMAs in VMs shared between contexts: kms->vm, gpu->vm
  */
 
 void msm_gem_vma_get(struct drm_gem_object *obj)
@@ -110,6 +115,12 @@ void msm_gem_vma_put(struct drm_gem_object *obj)
 	if (atomic_dec_return(&to_msm_bo(obj)->vma_ref))
 		return;
 
+	if (priv->gpu && priv->gpu->vm_shared) {
+		dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_BOOKKEEP, false,
+				      MAX_SCHEDULE_TIMEOUT);
+		put_iova_spaces(obj, priv->gpu->vm, true, "vma_put");
+	}
+
 	if (!priv->kms)
 		return;
 
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 0c2c35636251..31d84e2b123a 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -879,8 +879,10 @@ msm_gpu_create_private_vm(struct msm_gpu *gpu, struct task_struct *task,
 			to_msm_vm(vm)->pid = get_pid(task_pid(task));
 	}
 
-	if (IS_ERR_OR_NULL(vm) && kernel_managed)
+	if (IS_ERR_OR_NULL(vm) && kernel_managed) {
 		vm = drm_gpuvm_get(gpu->vm);
+		gpu->vm_shared = true;
+	}
 
 	return vm;
 }
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index d27d54bdb7a7..7722776e9129 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -223,6 +223,9 @@ struct msm_gpu {
 
 	struct drm_gpuvm *vm;
 
+	/** @vm_shared: Has @vm been handed out as a context VM? */
+	bool vm_shared;
+
 	/* Power Control: */
 	struct regulator *gpu_reg, *gpu_cx;
 	struct clk_bulk_data *grp_clks;

-- 
2.47.3


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

* Re: [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper
  2026-08-22  8:48 ` [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper Dmitry Baryshkov
@ 2026-08-22  9:03   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-22  9:03 UTC (permalink / raw)
  To: Dmitry Baryshkov; +Cc: dri-devel, media-ci

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The VMA reference count (`vma_ref`) is decremented and checked outside of the VMA/object lock, leading to a TOCTOU race where an active VMA can be unmapped and freed while in use by another thread.
--

--- Patch [1]: [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper ---
commit 3d4e26e168daef686a19b61570238be2722c0b4d
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Subject: drm/msm: factor out a locking put_iova_spaces() wrapper

This commit factors out a locking put_iova_spaces_locked() wrapper and
updates callers. It aims to clean up redundant lock, tear down, and unlock
sequences around the teardown path.

> diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
> index de4e60e28a711..8b979d84bb958 100644
> --- a/drivers/gpu/drm/msm/msm_gem.c
> +++ b/drivers/gpu/drm/msm/msm_gem.c

[ ... ]

> @@ -105,11 +113,7 @@ void msm_gem_vma_put(struct drm_gem_object *obj)
>  		return;
>  
>  #ifdef CONFIG_DRM_MSM_KMS
> -	struct drm_exec exec;
> -
> -	msm_gem_lock_vm_and_obj(&exec, obj, priv->kms->vm);
>  	put_iova_spaces(obj, priv->kms->vm, true, "vma_put");
> -	drm_exec_fini(&exec);     /* drop locks */
>  #endif
>  }

[ ... ]

> @@ -408,8 +412,8 @@ static struct drm_gpuva *lookup_vma(struct drm_gem_object *obj,
>   * mapping.
>   */
>  static void
> -put_iova_spaces(struct drm_gem_object *obj, struct drm_gpuvm *vm,
> -		bool close, const char *reason)
> +put_iova_spaces_locked(struct drm_gem_object *obj, struct drm_gpuvm *vm,
> +		       bool close, const char *reason)
>  {
>  	struct drm_gpuvm_bo *vm_bo, *tmp;

[Severity: Critical]
This is a pre-existing issue, but can the lockless decrement of vma_ref in
msm_gem_vma_put() cause a race condition?

If CPU1 calls msm_gem_close() -> msm_gem_vma_put() and decrements vma_ref to
0, but gets preempted before taking the lock and calling put_iova_spaces():

CPU2 could concurrently open the GEM object (e.g., via flink), calling
msm_gem_open() -> msm_gem_vma_get(), incrementing vma_ref back to 1,
mapping the object, and submitting a job.

When CPU1 resumes, it continues into put_iova_spaces() and eventually
put_iova_spaces_locked():

drivers/gpu/drm/msm/msm_gem.c:put_iova_spaces_locked() {
        ...
        drm_gpuvm_bo_for_each_va_safe (vma, vmatmp, vm_bo) {
                msm_gem_vma_unmap(vma, reason);
                if (close)
                        msm_gem_vma_close(vma);
        }
        ...
}

Since vma_ref isn't re-checked inside the lock, the IOVA space is torn down
unconditionally. Could this result in tearing down an active IOVA mapping
while the GPU is executing a job, leading to an IOMMU fault or memory
corruption?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260822-msm-fix-export-v1-0-917b3cbedd43@oss.qualcomm.com?part=1

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

* Re: [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close
  2026-08-22  8:48 ` [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close Dmitry Baryshkov
@ 2026-08-22  9:07   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-08-22  9:07 UTC (permalink / raw)
  To: Dmitry Baryshkov; +Cc: dri-devel, media-ci

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Sleeping in atomic context
- [High] Deferred teardown TOCTOU race via flink name re-opening causing IOVA Use-After-Free
--

--- Patch [2]: [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close ---
commit c4a550033efcf938dc9587175385266d18df3979
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

drm/msm: don't tear down shared VM mappings on handle close

This patch defers the teardown of shared GPU VM mappings until the vma_ref
count drops to zero. This aims to prevent pulling the buffer out from under
other files that still have it open when sharing a global VM address space
on targets without per-process pgtables.

Fixes: 111fdd2198e6 ("drm/msm: drm_gpuvm conversion")

> diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
> index 8b979d84bb958..f17ecd97b72a3 100644
> --- a/drivers/gpu/drm/msm/msm_gem.c
> +++ b/drivers/gpu/drm/msm/msm_gem.c
[ ... ]
> @@ -109,6 +114,12 @@ void msm_gem_vma_put(struct drm_gem_object *obj)
>  	if (atomic_dec_return(&to_msm_bo(obj)->vma_ref))
>  		return;
>  
> +	if (priv->gpu && priv->gpu->vm_shared) {
> +		dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_BOOKKEEP, false,
> +				      MAX_SCHEDULE_TIMEOUT);
> +		put_iova_spaces(obj, priv->gpu->vm, true, "vma_put");
> +	}
> +

[Severity: High]
This is a pre-existing issue, but can this lockless vma_ref check lead to an
IOVA use-after-free if a GEM handle is concurrently closed and re-opened via
flink?

If Thread A closes the last handle, atomic_dec_return() drops vma_ref to 0
and it proceeds to unmap the buffer. However, the flink name remains valid
until drm_gem_object_handle_put_unlocked() runs. If Thread B calls
DRM_IOCTL_GEM_OPEN concurrently, msm_gem_open() can increment vma_ref back to
1 and submit a job using the IOVA. Thread A then acquires locks in
put_iova_spaces() and unconditionally unmaps the buffer via
put_iova_spaces_locked() without re-verifying vma_ref:

drivers/gpu/drm/msm/msm_gem.c:put_iova_spaces_locked() {
    ...
    drm_gpuvm_bo_for_each_va_safe (vma, vmatmp, vm_bo) {
        msm_gem_vma_unmap(vma, reason);
        if (close)
            msm_gem_vma_close(vma);
    }
}

Will this destroy Thread B's mapping while its job is running, freeing the
IOVA for immediate reuse?

[Severity: High]
This isn't a bug introduced by this patch, but does adding these blocking
calls cause a scheduling while atomic panic when tearing down a writeback
connector?

The drm core function drm_writeback_connector_cleanup() holds a spinlock with
IRQs disabled when tearing down pending jobs:

drivers/gpu/drm/drm_writeback.c:drm_writeback_connector_cleanup() {
    spin_lock_irqsave(&wb_connector->job_lock, flags);
    list_for_each_entry_safe(pos, n, &wb_connector->job_queue, list_entry) {
        list_del(&pos->list_entry);
        drm_writeback_cleanup_job(pos);
    }
}

This calls into msm_gem_vma_put() through the teardown path
(dpu_wb_conn_cleanup_job -> dpu_encoder_cleanup_wb_job ->
dpu_encoder_phys_wb_cleanup_wb_job -> msm_framebuffer_cleanup ->
msm_gem_vma_put).

Since dma_resv_wait_timeout() can wait indefinitely and put_iova_spaces()
acquires ww_mutexes via drm_exec, could this result in sleeping while holding
a spinlock with IRQs disabled?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260822-msm-fix-export-v1-0-917b3cbedd43@oss.qualcomm.com?part=2

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

end of thread, other threads:[~2026-08-22  9:08 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-22  8:48 [PATCH 0/2] drm/msm: fix dma-buf sharing on targets without per-process pgtables Dmitry Baryshkov
2026-08-22  8:48 ` [PATCH 1/2] drm/msm: factor out a locking put_iova_spaces() wrapper Dmitry Baryshkov
2026-08-22  9:03   ` sashiko-bot
2026-08-22  8:48 ` [PATCH 2/2] drm/msm: don't tear down shared VM mappings on handle close Dmitry Baryshkov
2026-08-22  9:07   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox