* [PATCH] drm/xe/dma-buf: Validate BOs on export to charge the exporter
@ 2026-11-11 10:09 Thomas Hellström
2026-11-11 12:18 ` Matthew Auld
0 siblings, 1 reply; 3+ messages in thread
From: Thomas Hellström @ 2026-11-11 10:09 UTC (permalink / raw)
To: intel-xe
Cc: Thomas Hellström, Matthew Brost, Matthew Auld, Dave Airlie,
Rodrigo Vivi, stable
xe_gem_prime_export() calls ttm_bo_setup_export() to make sure the
exporter is charged for the memory backing an exported BO. However,
that only populates a TTM_PL_SYSTEM tt. A BO that has been evicted, or
that has deferred backing, will instead get its memory allocated when
the importer maps it. For p2p imports this happens through
xe_bo_validate() in xe_dma_buf_map(), so the importer, not the
exporter, is charged for the allocation.
Call xe_bo_validate() at export time instead, so that the BO is moved
into its preferred placement and the exporter is charged.
Do the purgeable checks and the validation in the same
xe_validation_guard() transaction, to avoid locking the BO twice.
The commit message and code was drafted using LLM.
Link: https://patchwork.freedesktop.org/patch/747480/?series=172524&rev=1
Fixes: 91494dee1091 ("xe: populate buffers before exporting them.")
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: intel-xe@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.18+
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
drivers/gpu/drm/xe/xe_dma_buf.c | 76 +++++++++++++++++----------------
1 file changed, 39 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 5bf13dc4b61d..90c9b90de552 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -263,60 +263,62 @@ struct drm_gem_object *xe_dma_buf_to_gem(struct drm_device *dev,
struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
+ struct xe_validation_ctx ctx;
+ struct drm_exec exec;
struct dma_buf *buf;
- struct ttm_operation_ctx ctx = {
- .interruptible = true,
- .no_wait_gpu = true,
- /* We opt to avoid OOM on system pages allocations */
- .gfp_retry_mayfail = true,
- .allow_res_evict = false,
- };
- int ret;
+ int ret = 0;
if (bo->vm)
return ERR_PTR(-EPERM);
- /*
- * Reject exporting purgeable BOs. DONTNEED BOs can be purged
- * at any time, making the exported dma-buf unusable. Purged BOs
- * have no backing store and are permanently invalid.
- */
- ret = xe_bo_lock(bo, true);
- if (ret)
- return ERR_PTR(ret);
+ xe_validation_guard(&ctx, &xe_bo_device(bo)->val, &exec,
+ (struct xe_val_flags) {.interruptible = true}, ret) {
+ ret = drm_exec_lock_obj(&exec, &bo->ttm.base);
+ drm_exec_retry_on_contention(&exec);
+ if (ret)
+ break;
- if (xe_bo_madv_is_dontneed(bo)) {
- ret = -EBUSY;
- goto out_unlock;
- }
+ /*
+ * Reject exporting purgeable BOs. DONTNEED BOs can be purged
+ * at any time, making the exported dma-buf unusable. Purged BOs
+ * have no backing store and are permanently invalid.
+ */
+ if (xe_bo_madv_is_dontneed(bo)) {
+ ret = -EBUSY;
+ break;
+ }
- if (xe_bo_is_purged(bo)) {
- ret = -EINVAL;
- goto out_unlock;
- }
+ if (xe_bo_is_purged(bo)) {
+ ret = -EINVAL;
+ break;
+ }
- xe_bo_willneed_get_locked(bo);
- xe_bo_unlock(bo);
+ /*
+ * Make sure the BO is backed in its preferred placement so that
+ * the exporter, rather than the importer, is charged for the
+ * memory allocation.
+ */
+ ret = xe_bo_validate(bo, NULL, false, &exec);
+ drm_exec_retry_on_contention(&exec);
+ xe_validation_retry_on_oom(&ctx, &ret);
+ if (ret)
+ break;
- ret = ttm_bo_setup_export(&bo->ttm, &ctx);
+ xe_bo_willneed_get_locked(bo);
+ }
if (ret)
- goto out_put;
+ return ERR_PTR(ret);
buf = drm_gem_prime_export(obj, flags);
if (IS_ERR(buf)) {
- ret = PTR_ERR(buf);
- goto out_put;
+ xe_bo_lock(bo, false);
+ xe_bo_willneed_put_locked(bo);
+ xe_bo_unlock(bo);
+ return buf;
}
buf->ops = &xe_dmabuf_ops;
return buf;
-
-out_put:
- xe_bo_lock(bo, false);
- xe_bo_willneed_put_locked(bo);
-out_unlock:
- xe_bo_unlock(bo);
- return ERR_PTR(ret);
}
static struct drm_gem_object *
--
2.56.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] drm/xe/dma-buf: Validate BOs on export to charge the exporter
2026-11-11 10:09 [PATCH] drm/xe/dma-buf: Validate BOs on export to charge the exporter Thomas Hellström
@ 2026-11-11 12:18 ` Matthew Auld
2026-11-11 12:22 ` Thomas Hellström
0 siblings, 1 reply; 3+ messages in thread
From: Matthew Auld @ 2026-11-11 12:18 UTC (permalink / raw)
To: Thomas Hellström, intel-xe
Cc: Matthew Brost, Dave Airlie, Rodrigo Vivi, stable
On 11/11/2026 10:09, Thomas Hellström wrote:
> xe_gem_prime_export() calls ttm_bo_setup_export() to make sure the
> exporter is charged for the memory backing an exported BO. However,
> that only populates a TTM_PL_SYSTEM tt. A BO that has been evicted, or
> that has deferred backing, will instead get its memory allocated when
> the importer maps it. For p2p imports this happens through
> xe_bo_validate() in xe_dma_buf_map(), so the importer, not the
> exporter, is charged for the allocation.
>
> Call xe_bo_validate() at export time instead, so that the BO is moved
> into its preferred placement and the exporter is charged.
Is it maybe also worth mentioning somewhere the unreclaimable pages
angle? I could be mistaken here, but I think that was maybe also the
issue I saw in a workload. For example:
1. Create say 100G VRAM buffer. Use DEFERRED. This leaves lingering tt
structure.
2. Validate it on native side. This is now resident in VRAM. But still
has lingering tt.
3. Export it. This now populates 100G worth of TT pages due to lingering
tt. But we now have a populated VRAM buffer which is also populated with
100G TT pages. That 100G TT pages is not reclaimable and is hidden from
shrinker since this still just looks like normal VRAM buffer.
>
> Do the purgeable checks and the validation in the same
> xe_validation_guard() transaction, to avoid locking the BO twice.
>
> The commit message and code was drafted using LLM.
>
> Link: https://patchwork.freedesktop.org/patch/747480/?series=172524&rev=1
> Fixes: 91494dee1091 ("xe: populate buffers before exporting them.")
> Cc: Matthew Brost <matthew.brost@intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
> Cc: intel-xe@lists.freedesktop.org
> Cc: <stable@vger.kernel.org> # v6.18+
> Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
> ---
> drivers/gpu/drm/xe/xe_dma_buf.c | 76 +++++++++++++++++----------------
> 1 file changed, 39 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
> index 5bf13dc4b61d..90c9b90de552 100644
> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> @@ -263,60 +263,62 @@ struct drm_gem_object *xe_dma_buf_to_gem(struct drm_device *dev,
> struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
> {
> struct xe_bo *bo = gem_to_xe_bo(obj);
> + struct xe_validation_ctx ctx;
> + struct drm_exec exec;
> struct dma_buf *buf;
> - struct ttm_operation_ctx ctx = {
> - .interruptible = true,
> - .no_wait_gpu = true,
> - /* We opt to avoid OOM on system pages allocations */
> - .gfp_retry_mayfail = true,
> - .allow_res_evict = false,
> - };
> - int ret;
> + int ret = 0;
>
> if (bo->vm)
> return ERR_PTR(-EPERM);
>
> - /*
> - * Reject exporting purgeable BOs. DONTNEED BOs can be purged
> - * at any time, making the exported dma-buf unusable. Purged BOs
> - * have no backing store and are permanently invalid.
> - */
> - ret = xe_bo_lock(bo, true);
> - if (ret)
> - return ERR_PTR(ret);
> + xe_validation_guard(&ctx, &xe_bo_device(bo)->val, &exec,
> + (struct xe_val_flags) {.interruptible = true}, ret) {
> + ret = drm_exec_lock_obj(&exec, &bo->ttm.base);
> + drm_exec_retry_on_contention(&exec);
> + if (ret)
> + break;
>
> - if (xe_bo_madv_is_dontneed(bo)) {
> - ret = -EBUSY;
> - goto out_unlock;
> - }
> + /*
> + * Reject exporting purgeable BOs. DONTNEED BOs can be purged
> + * at any time, making the exported dma-buf unusable. Purged BOs
> + * have no backing store and are permanently invalid.
> + */
> + if (xe_bo_madv_is_dontneed(bo)) {
> + ret = -EBUSY;
> + break;
> + }
>
> - if (xe_bo_is_purged(bo)) {
> - ret = -EINVAL;
> - goto out_unlock;
> - }
> + if (xe_bo_is_purged(bo)) {
> + ret = -EINVAL;
> + break;
> + }
>
> - xe_bo_willneed_get_locked(bo);
> - xe_bo_unlock(bo);
> + /*
> + * Make sure the BO is backed in its preferred placement so that
> + * the exporter, rather than the importer, is charged for the
> + * memory allocation.
> + */
> + ret = xe_bo_validate(bo, NULL, false, &exec);
> + drm_exec_retry_on_contention(&exec);
> + xe_validation_retry_on_oom(&ctx, &ret);
> + if (ret)
> + break;
>
> - ret = ttm_bo_setup_export(&bo->ttm, &ctx);
> + xe_bo_willneed_get_locked(bo);
> + }
> if (ret)
> - goto out_put;
> + return ERR_PTR(ret);
>
> buf = drm_gem_prime_export(obj, flags);
> if (IS_ERR(buf)) {
> - ret = PTR_ERR(buf);
> - goto out_put;
> + xe_bo_lock(bo, false);
> + xe_bo_willneed_put_locked(bo);
> + xe_bo_unlock(bo);
> + return buf;
> }
>
> buf->ops = &xe_dmabuf_ops;
> return buf;
> -
> -out_put:
> - xe_bo_lock(bo, false);
> - xe_bo_willneed_put_locked(bo);
> -out_unlock:
> - xe_bo_unlock(bo);
> - return ERR_PTR(ret);
> }
>
> static struct drm_gem_object *
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] drm/xe/dma-buf: Validate BOs on export to charge the exporter
2026-11-11 12:18 ` Matthew Auld
@ 2026-11-11 12:22 ` Thomas Hellström
0 siblings, 0 replies; 3+ messages in thread
From: Thomas Hellström @ 2026-11-11 12:22 UTC (permalink / raw)
To: Matthew Auld, intel-xe; +Cc: Matthew Brost, Dave Airlie, Rodrigo Vivi, stable
Hi, Matt
Thanks for reviewing!
On Wed, 2026-11-11 at 12:18 +0000, Matthew Auld wrote:
> On 11/11/2026 10:09, Thomas Hellström wrote:
> > xe_gem_prime_export() calls ttm_bo_setup_export() to make sure the
> > exporter is charged for the memory backing an exported BO. However,
> > that only populates a TTM_PL_SYSTEM tt. A BO that has been evicted,
> > or
> > that has deferred backing, will instead get its memory allocated
> > when
> > the importer maps it. For p2p imports this happens through
> > xe_bo_validate() in xe_dma_buf_map(), so the importer, not the
> > exporter, is charged for the allocation.
> >
> > Call xe_bo_validate() at export time instead, so that the BO is
> > moved
> > into its preferred placement and the exporter is charged.
>
> Is it maybe also worth mentioning somewhere the unreclaimable pages
> angle? I could be mistaken here, but I think that was maybe also the
> issue I saw in a workload. For example:
>
> 1. Create say 100G VRAM buffer. Use DEFERRED. This leaves lingering
> tt
> structure.
>
> 2. Validate it on native side. This is now resident in VRAM. But
> still
> has lingering tt.
>
> 3. Export it. This now populates 100G worth of TT pages due to
> lingering
> tt. But we now have a populated VRAM buffer which is also populated
> with
> 100G TT pages. That 100G TT pages is not reclaimable and is hidden
> from
> shrinker since this still just looks like normal VRAM buffer.
Since we have another patch to fix that, which I hope will go in before
this one, I think mentioning it here will be unnecessary.
>
> >
> > Do the purgeable checks and the validation in the same
> > xe_validation_guard() transaction, to avoid locking the BO twice.
> >
> > The commit message and code was drafted using LLM.
> >
> > Link:
> > https://patchwork.freedesktop.org/patch/747480/?series=172524&rev=1
> > Fixes: 91494dee1091 ("xe: populate buffers before exporting them.")
> > Cc: Matthew Brost <matthew.brost@intel.com>
> > Cc: Matthew Auld <matthew.auld@intel.com>
> > Cc: Dave Airlie <airlied@redhat.com>
> > Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
> > Cc: intel-xe@lists.freedesktop.org
> > Cc: <stable@vger.kernel.org> # v6.18+
> > Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>
> Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Thanks!
Thomas
>
> > ---
> > drivers/gpu/drm/xe/xe_dma_buf.c | 76 +++++++++++++++++-----------
> > -----
> > 1 file changed, 39 insertions(+), 37 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c
> > b/drivers/gpu/drm/xe/xe_dma_buf.c
> > index 5bf13dc4b61d..90c9b90de552 100644
> > --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> > +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> > @@ -263,60 +263,62 @@ struct drm_gem_object
> > *xe_dma_buf_to_gem(struct drm_device *dev,
> > struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj,
> > int flags)
> > {
> > struct xe_bo *bo = gem_to_xe_bo(obj);
> > + struct xe_validation_ctx ctx;
> > + struct drm_exec exec;
> > struct dma_buf *buf;
> > - struct ttm_operation_ctx ctx = {
> > - .interruptible = true,
> > - .no_wait_gpu = true,
> > - /* We opt to avoid OOM on system pages allocations
> > */
> > - .gfp_retry_mayfail = true,
> > - .allow_res_evict = false,
> > - };
> > - int ret;
> > + int ret = 0;
> >
> > if (bo->vm)
> > return ERR_PTR(-EPERM);
> >
> > - /*
> > - * Reject exporting purgeable BOs. DONTNEED BOs can be
> > purged
> > - * at any time, making the exported dma-buf unusable.
> > Purged BOs
> > - * have no backing store and are permanently invalid.
> > - */
> > - ret = xe_bo_lock(bo, true);
> > - if (ret)
> > - return ERR_PTR(ret);
> > + xe_validation_guard(&ctx, &xe_bo_device(bo)->val, &exec,
> > + (struct xe_val_flags) {.interruptible
> > = true}, ret) {
> > + ret = drm_exec_lock_obj(&exec, &bo->ttm.base);
> > + drm_exec_retry_on_contention(&exec);
> > + if (ret)
> > + break;
> >
> > - if (xe_bo_madv_is_dontneed(bo)) {
> > - ret = -EBUSY;
> > - goto out_unlock;
> > - }
> > + /*
> > + * Reject exporting purgeable BOs. DONTNEED BOs
> > can be purged
> > + * at any time, making the exported dma-buf
> > unusable. Purged BOs
> > + * have no backing store and are permanently
> > invalid.
> > + */
> > + if (xe_bo_madv_is_dontneed(bo)) {
> > + ret = -EBUSY;
> > + break;
> > + }
> >
> > - if (xe_bo_is_purged(bo)) {
> > - ret = -EINVAL;
> > - goto out_unlock;
> > - }
> > + if (xe_bo_is_purged(bo)) {
> > + ret = -EINVAL;
> > + break;
> > + }
> >
> > - xe_bo_willneed_get_locked(bo);
> > - xe_bo_unlock(bo);
> > + /*
> > + * Make sure the BO is backed in its preferred
> > placement so that
> > + * the exporter, rather than the importer, is
> > charged for the
> > + * memory allocation.
> > + */
> > + ret = xe_bo_validate(bo, NULL, false, &exec);
> > + drm_exec_retry_on_contention(&exec);
> > + xe_validation_retry_on_oom(&ctx, &ret);
> > + if (ret)
> > + break;
> >
> > - ret = ttm_bo_setup_export(&bo->ttm, &ctx);
> > + xe_bo_willneed_get_locked(bo);
> > + }
> > if (ret)
> > - goto out_put;
> > + return ERR_PTR(ret);
> >
> > buf = drm_gem_prime_export(obj, flags);
> > if (IS_ERR(buf)) {
> > - ret = PTR_ERR(buf);
> > - goto out_put;
> > + xe_bo_lock(bo, false);
> > + xe_bo_willneed_put_locked(bo);
> > + xe_bo_unlock(bo);
> > + return buf;
> > }
> >
> > buf->ops = &xe_dmabuf_ops;
> > return buf;
> > -
> > -out_put:
> > - xe_bo_lock(bo, false);
> > - xe_bo_willneed_put_locked(bo);
> > -out_unlock:
> > - xe_bo_unlock(bo);
> > - return ERR_PTR(ret);
> > }
> >
> > static struct drm_gem_object *
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-11 12:22 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 10:09 [PATCH] drm/xe/dma-buf: Validate BOs on export to charge the exporter Thomas Hellström
2026-11-11 12:18 ` Matthew Auld
2026-11-11 12:22 ` Thomas Hellström
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).