The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Matthew Brost <matthew.brost@intel.com>
To: Nathan Bourgeois <iridescentrosesfall@gmail.com>
Cc: intel-xe@lists.freedesktop.org, dri-devel@lists.freedesktop.org,
	linux-kernel@vger.kernel.org,
	"Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
	"Rodrigo Vivi" <rodrigo.vivi@intel.com>,
	"Christian König" <christian.koenig@amd.com>,
	"Matthew Auld" <matthew.auld@intel.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>
Subject: Re: [PATCH] drm/xe: Fix unnecessary host-side population of ttm_tt on non-TT resources
Date: Thu, 20 Aug 2026 10:03:36 -0700	[thread overview]
Message-ID: <aoczaCuoaZfUkfm5@gsse-cloud1.jf.intel.com> (raw)
In-Reply-To: <20260820031901.1018324-1-iridescentrosesfall@gmail.com>

On Wed, Aug 19, 2026 at 11:19:01PM -0400, Nathan Bourgeois wrote:

Thanks the patch.

> When a buffer object (BO) has a created but unpopulated ttm_tt and is
> exported, the default behavior populates the ttm_tt, even if the manager
> does not require a TT. This unnecessary host-side population reduces
> host memory available to the user.
> 
> This occurs when a BO is created with XE_BO_FLAG_DEFER_BACKING and
> migration places the BO into a resource whose manager does not use the
> TT backing. The retained ttm_tt is not authoritative for storage, yet
> the default ttm_bo_setup_export() treats the existence of the ttm_tt as
> requiring population.
> 

Thanks for explaining this, I see the problem.

> The issue was reproduced with vLLM 0.27.1 loading
> 0xSer0/DeepSeek-V4-Flash-180B (d3c704b) on a system with 128 GB of RAM
> and six Intel Arc Pro B70 GPUs providing 192 GB of VRAM. Loading the
> weights (100.61 GB) triggered host-memory exhaustion and OOM events on
> the baseline kernel.
> 
> On Ubuntu 24.04 with 7.0-12-generic, the vLLM service cgroup grew by
> 115.84 GB on the instrumented baseline and by 15.34 GB with this change,
> a reduction of 100.50 GB. Diagnostic instrumentation counting cumulative
> TT pages across all six GPUs recorded 109.22 GB on baseline and 8.72 GB
> with this change. The reduction is 0.11 GB less than the model weight
> size.
> 
> The change creates an Xe-local export setup helper which skips
> ttm_bo_populate() when the BO has a current resource manager and if that
> manager has use_tt == false. If the manager does not use TT, then
> population is skipped, while an absent manager conservatively calls
> ttm_bo_populate(). Behavior for a manager which requires a TT backing
> remains unchanged. Keep this policy local to Xe so other TTM drivers
> retain their existing export behavior.
> 

Reading the commit which added ttm_bo_setup_export, to me this looks
like a bug in ttm_bo_setup_export() or in general TTM.

git format-patch -1 50243079865ae

'This only applies currently to TTM_PL_SYSTEM objects, because
GTT objects get populated on first validate, and VRAM doesn't
use TT.'

I'd probably move this fix into ttm_bo_setup_export() and post it for
discussion.

It may also be the case that the ttm_tt preallocated with
XE_BO_FLAG_DEFER_BACKING should be dropped once the BO moves to VRAM, as
that dangling ttm_tt could cause other issues. I'll need to take a
closer look at that angle.

> The regression test creates both the control case and the VRAM migration
> case. The control case creates the deferred BO in system RAM and leaves
> it there, which requires a TT and population when xe_gem_prime_export() is
> called. The VRAM migration case creates the deferred BO in system RAM
> and then migrates it to VRAM, which does not require a TT and thus, when
> xe_gem_prime_export() is called, no population occurs.
>

This part looks good as different patch from what I'm assuming will be a
TTM fix.

Matt

> Fixes: 91494dee1091 ("xe: populate buffers before exporting them.")
> Cc: stable@vger.kernel.org
> Assisted-by: Codex:gpt-5.6-sol
> Signed-off-by: Nathan Bourgeois <iridescentrosesfall@gmail.com>
> ---
> Newer Testing:
> - Built and booted b4f95affc66ef76342c1f6bf3849f6c8ade6b9d6 plus this patch on EPYC 7352 with six Intel Arc Pro B70.
> - KUnit xe_live_test: xe_dma_buf_kunit: pass:6 fail:0 skip:0 total:6 PASS
> - vLLM 0.27.1 loading DeepSeek-V4-Flash-0731 (full model compared to REAP) on six Intel Arc Pro B70 GPUs: No OOM error, 17.82 GB delta.
>  drivers/gpu/drm/xe/tests/xe_dma_buf.c | 105 ++++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_dma_buf.c       |  28 ++++++-
>  2 files changed, 132 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
> index 0be8440b3976..bff803ab7a57 100644
> --- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
> +++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
> @@ -260,12 +260,117 @@ static const struct dma_buf_test_params test_params[] = {
>  	{}
>  };
>  
> +static void xe_test_dmabuf_export_deferred(struct xe_device *xe, u32 bo_flags,
> +					   u32 mem_type, bool expect_populated)
> +{
> +	struct drm_exec *exec = XE_VALIDATION_OPT_OUT;
> +	struct kunit *test = kunit_get_current_test();
> +	struct ttm_resource_manager *man;
> +	struct dma_buf *dmabuf;
> +	struct xe_bo *bo;
> +	size_t size = PAGE_SIZE;
> +	int err;
> +
> +	/* No VRAM on device? */
> +	if (!ttm_manager_type(&xe->ttm, mem_type))
> +		return;
> +
> +	if (mem_type == XE_PL_VRAM0 &&
> +	    xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
> +		size = SZ_64K;
> +
> +	/*
> +	 * DEFER_BACKING places the BO in SYSTEM and creates a ttm_tt which
> +	 * is unpopulated. In the case of VRAM, migrating leaves the ttm_tt
> +	 * retained and unpopulated while VRAM becomes the real backing.
> +	 */
> +	bo = xe_bo_create_user(xe, NULL, size, DRM_XE_GEM_CPU_CACHING_WC,
> +			       bo_flags | XE_BO_FLAG_DEFER_BACKING, NULL);
> +	if (IS_ERR(bo)) {
> +		KUNIT_FAIL(test, "BO creation failed: %pe\n", bo);
> +		return;
> +	}
> +
> +	err = xe_bo_lock(bo, false);
> +	if (err) {
> +		KUNIT_FAIL(test, "BO lock failed: %d\n", err);
> +		goto out_put_bo;
> +	}
> +
> +	if (bo->ttm.resource->mem_type != mem_type)
> +		err = xe_bo_migrate(bo, mem_type, NULL, exec);
> +	if (err) {
> +		KUNIT_FAIL(test, "BO migration to %u failed: %d\n", mem_type,
> +			   err);
> +		goto out_unlock;
> +	}
> +
> +	man = ttm_manager_type(bo->ttm.bdev, bo->ttm.resource->mem_type);
> +	if (!man || !bo->ttm.ttm) {
> +		KUNIT_FAIL(test, "Expected a retained unpopulated TT\n");
> +		goto out_unlock;
> +	}
> +
> +	/* Precondition: ttm_tt starts unpopulated after migration */
> +	KUNIT_EXPECT_EQ(test, man->use_tt, expect_populated);
> +	KUNIT_EXPECT_FALSE(test, ttm_tt_is_populated(bo->ttm.ttm));
> +
> +	xe_bo_unlock(bo);
> +
> +	dmabuf = xe_gem_prime_export(&bo->ttm.base, 0);
> +	if (IS_ERR(dmabuf)) {
> +		KUNIT_FAIL(test, "dma-buf export failed: %pe\n", dmabuf);
> +		goto out_put_bo;
> +	}
> +
> +	err = xe_bo_lock(bo, false);
> +	if (err) {
> +		KUNIT_FAIL(test, "post-export BO lock failed: %d\n", err);
> +		goto out_put_dmabuf;
> +	}
> +
> +	/* Postcondition: if VRAM, ttm_tt remains unpopulated, if SYSTEM ttm_tt is populated */
> +	KUNIT_EXPECT_EQ(test, bo->ttm.resource->mem_type, mem_type);
> +	KUNIT_EXPECT_NOT_NULL(test, bo->ttm.ttm);
> +	if (bo->ttm.ttm)
> +		KUNIT_EXPECT_EQ(test, ttm_tt_is_populated(bo->ttm.ttm),
> +				expect_populated);
> +
> +	xe_bo_unlock(bo);
> +	dma_buf_put(dmabuf);
> +	drm_gem_object_put(&bo->ttm.base);
> +	return;
> +
> +out_unlock:
> +	xe_bo_unlock(bo);
> +	goto out_put_bo;
> +out_put_dmabuf:
> +	dma_buf_put(dmabuf);
> +out_put_bo:
> +	drm_gem_object_put(&bo->ttm.base);
> +}
> +
>  static int dma_buf_run_device(struct xe_device *xe)
>  {
>  	const struct dma_buf_test_params *params;
>  	struct kunit *test = kunit_get_current_test();
>  
>  	guard(xe_pm_runtime)(xe);
> +
> +	/*
> +	 * A retained TT must not be populated when VRAM is the backing
> +	 * resource.
> +	 */
> +	xe_test_dmabuf_export_deferred(xe, XE_BO_FLAG_VRAM0, XE_PL_VRAM0,
> +				       false);
> +
> +	/*
> +	 * Control case: deferred SYSTEM backing must still be populated
> +	 * before export.
> +	 */
> +	xe_test_dmabuf_export_deferred(xe, XE_BO_FLAG_SYSTEM, XE_PL_SYSTEM,
> +				       true);
> +
>  	for (params = test_params; params->mem_mask; ++params) {
>  		struct dma_buf_test_params p = *params;
>  
> diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
> index bf0728838ead..0c4e4e2e1a81 100644
> --- a/drivers/gpu/drm/xe/xe_dma_buf.c
> +++ b/drivers/gpu/drm/xe/xe_dma_buf.c
> @@ -219,6 +219,32 @@ static const struct dma_buf_ops xe_dmabuf_ops = {
>  	.vunmap = drm_gem_dmabuf_vunmap,
>  };
>  
> +static int xe_dma_bo_setup_export(struct ttm_buffer_object *tbo,
> +				  struct ttm_operation_ctx *ctx)
> +{
> +	struct ttm_resource_manager *man = NULL;
> +	int ret;
> +
> +	ret = ttm_bo_reserve(tbo, false, false, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (tbo->resource)
> +		man = ttm_manager_type(tbo->bdev, tbo->resource->mem_type);
> +
> +	/*
> +	 * Do not populate BO-sized system pages when backed by a non-TT resource.
> +	 * This is Xe-specific; the generic ttm_bo_setup_export() always populates.
> +	 */
> +	if (man && !man->use_tt)
> +		ret = 0;
> +	else
> +		ret = ttm_bo_populate(tbo, ctx);
> +
> +	ttm_bo_unreserve(tbo);
> +	return ret;
> +}
> +
>  struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
>  {
>  	struct xe_bo *bo = gem_to_xe_bo(obj);
> @@ -257,7 +283,7 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
>  	xe_bo_willneed_get_locked(bo);
>  	xe_bo_unlock(bo);
>  
> -	ret = ttm_bo_setup_export(&bo->ttm, &ctx);
> +	ret = xe_dma_bo_setup_export(&bo->ttm, &ctx);
>  	if (ret)
>  		goto out_put;
>  
> 
> base-commit: b4f95affc66ef76342c1f6bf3849f6c8ade6b9d6
> -- 
> 2.55.0
> 

  reply	other threads:[~2026-08-20 17:03 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20  3:19 [PATCH] drm/xe: Fix unnecessary host-side population of ttm_tt on non-TT resources Nathan Bourgeois
2026-08-20 17:03 ` Matthew Brost [this message]
     [not found]   ` <CAMpTW2cxRrG4oJ0HrRoKhHrVHxWO01-L+QDoWMLCAUaSfw75sA@mail.gmail.com>
2026-08-20 18:56     ` Nathan Bourgeois
2026-08-20 20:03       ` Thomas Hellström
2026-08-20 23:57         ` Matthew Brost
2026-08-21  1:08           ` Dave Airlie
2026-08-21  2:34             ` Nathan Bourgeois

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aoczaCuoaZfUkfm5@gsse-cloud1.jf.intel.com \
    --to=matthew.brost@intel.com \
    --cc=airlied@gmail.com \
    --cc=christian.koenig@amd.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=iridescentrosesfall@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matthew.auld@intel.com \
    --cc=rodrigo.vivi@intel.com \
    --cc=simona@ffwll.ch \
    --cc=thomas.hellstrom@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox