From: Matthew Brost <matthew.brost@intel.com>
To: intel-xe@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Cc: "Carlos Santa" <carlos.santa@intel.com>,
"Ryan Neph" <ryanneph@google.com>,
"Christian Koenig" <christian.koenig@amd.com>,
"Huang Rui" <ray.huang@amd.com>,
"Matthew Auld" <matthew.auld@intel.com>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
linux-kernel@vger.kernel.org,
"Thomas Hellström" <thomas.hellstrom@linux.intel.com>
Subject: [PATCH v2 05/33] drm/ttm: Support defragmentation moves
Date: Fri, 10 Jul 2026 14:54:14 -0700 [thread overview]
Message-ID: <20260710215442.2444235-6-matthew.brost@intel.com> (raw)
In-Reply-To: <20260710215442.2444235-1-matthew.brost@intel.com>
Add infrastructure to let a driver re-back an already-resident BO with
fresh pages (e.g. at a more beneficial order) by reusing the normal move
machinery.
Add ctx->defrag, which makes ttm_bo_validate() skip the "current
resource already compatible" short-circuit so a move is forced even when
placement is unchanged. In ttm_bo_handle_move_mem(), when defragging a
populated BO, stash the old tt, create and populate a fresh one
(allocating new backing), and hand the old tt to the driver move
callback via bo->defrag_old_tt for copying. On success the old tt is
unpopulated and freed; if populate or the move fails, the freshly
allocated backing is discharged and the BO is restored to its original
tt and placement.
The old tt is handed off through bo->defrag_old_tt (reservation-lock
protected) rather than the operation context. The pointer describes BO
state for the duration of the move, so keeping it on the buffer object
is better layering and lets ttm_bo_move_accel_cleanup() pick it up.
Because a defrag move commonly fails (beneficial-order pages are often
unavailable) and then leaves the BO untouched on its original backing,
defer ttm_bo_unmap_virtual() for the defrag case until the new backing
has been populated and the move is about to commit. Ordinary moves still
unmap up front. This avoids needlessly zapping and refaulting every
userspace mapping on the common failure path where nothing moved.
Teach ttm_bo_move_accel_cleanup()'s ghost path about defrag: instead of
tearing the move's source tt down synchronously, hand bo->defrag_old_tt
to the ghost object so it is kept populated until the copy fence signals
and then unbound and destroyed asynchronously, while the BO keeps its
new tt. This lets a driver pipeline defrag teardown behind the GPU copy
rather than blocking on it.
This is driver-opt-in and a no-op unless ctx->defrag is set.
Cc: Carlos Santa <carlos.santa@intel.com>
Cc: Ryan Neph <ryanneph@google.com>
Cc: Christian Koenig <christian.koenig@amd.com>
Cc: Huang Rui <ray.huang@amd.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Assisted-by: GitHub_Copilot:claude-opus-4.8
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
---
drivers/gpu/drm/ttm/ttm_bo.c | 83 ++++++++++++++++++++++++++++---
drivers/gpu/drm/ttm/ttm_bo_util.c | 16 +++++-
drivers/gpu/drm/ttm/ttm_pool.c | 5 +-
include/drm/ttm/ttm_bo.h | 17 +++++++
4 files changed, 111 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 3980f376e3ba..d05da4eb1d8a 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -125,36 +125,93 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
{
struct ttm_device *bdev = bo->bdev;
bool old_use_tt, new_use_tt;
+ bool defrag;
int ret;
old_use_tt = !bo->resource || ttm_manager_type(bdev, bo->resource->mem_type)->use_tt;
new_use_tt = ttm_manager_type(bdev, mem->mem_type)->use_tt;
- ttm_bo_unmap_virtual(bo);
+ /*
+ * Only the BO that is the actual defrag target (moved in place via
+ * ttm_bo_validate(), evict == false) is being defragmented. The same
+ * ctx->defrag is threaded into evictions triggered while allocating
+ * that target's new backing (ttm_bo_evict_alloc()), so guard against
+ * an unrelated victim BO (evict == true) being misclassified as a
+ * defrag move and needlessly reallocating its backing.
+ */
+ defrag = ctx->defrag && !evict && new_use_tt && bo->ttm &&
+ ttm_tt_is_populated(bo->ttm);
+
+ /*
+ * Tear down userspace mappings up front for ordinary moves. A defrag
+ * move is likely to leave the BO in place (beneficial-order allocation
+ * commonly fails), so defer the unmap until the new backing has been
+ * populated and the move is about to commit. This avoids a needless
+ * zap and refault of every userspace mapping on the common failure,
+ * where the BO keeps its original, still-mapped backing.
+ */
+ if (!defrag)
+ ttm_bo_unmap_virtual(bo);
/*
* Create and bind a ttm if required.
*/
if (new_use_tt) {
+ struct ttm_tt *defrag_old_tt = NULL;
+
+ /*
+ * For a defrag move, stash the current populated tt and force a
+ * fresh one to be created and populated (at beneficial order).
+ * The old tt is handed to the driver move callback to copy the
+ * contents from, and freed once the move completes.
+ */
+ if (defrag) {
+ defrag_old_tt = bo->ttm;
+ bo->ttm = NULL;
+ }
+
/* Zero init the new TTM structure if the old location should
* have used one as well.
*/
- ret = ttm_tt_create(bo, old_use_tt);
- if (ret)
+ ret = ttm_tt_create(bo, old_use_tt && !defrag_old_tt);
+ if (ret) {
+ if (defrag_old_tt)
+ bo->ttm = defrag_old_tt;
goto out_err;
+ }
- if (mem->mem_type != TTM_PL_SYSTEM) {
+ if (defrag_old_tt || mem->mem_type != TTM_PL_SYSTEM) {
ret = ttm_bo_populate(bo, ctx);
- if (ret)
+ if (ret) {
+ /*
+ * Discharge the freshly allocated backing and
+ * leave the BO on its original tt/placement.
+ */
+ if (defrag_old_tt) {
+ ttm_tt_destroy(bo->bdev, bo->ttm);
+ bo->ttm = defrag_old_tt;
+ }
goto out_err;
+ }
}
+
+ bo->defrag_old_tt = defrag_old_tt;
}
ret = dma_resv_reserve_fences(bo->base.resv, 1);
if (ret)
goto out_err;
+ /*
+ * A defrag move deferred its virtual unmap until the new backing was
+ * successfully populated; tear the userspace mappings down now, before
+ * the move swaps in the new pages, so the next fault re-establishes
+ * them against the new backing.
+ */
+ if (defrag)
+ ttm_bo_unmap_virtual(bo);
+
ret = bdev->funcs->move(bo, evict, ctx, mem, hop);
if (ret) {
if (ret == -EMULTIHOP)
@@ -162,12 +219,24 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
goto out_err;
}
+ if (bo->defrag_old_tt) {
+ ttm_tt_unpopulate(bo->bdev, bo->defrag_old_tt);
+ ttm_tt_destroy(bo->bdev, bo->defrag_old_tt);
+ bo->defrag_old_tt = NULL;
+ }
+
ctx->bytes_moved += bo->base.size;
return 0;
out_err:
- if (!old_use_tt)
+ if (bo->defrag_old_tt) {
+ /* Failed defrag move: restore the original backing. */
+ ttm_bo_tt_destroy(bo);
+ bo->ttm = bo->defrag_old_tt;
+ bo->defrag_old_tt = NULL;
+ } else if (!old_use_tt) {
ttm_bo_tt_destroy(bo);
+ }
return ret;
}
@@ -835,7 +904,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
force_space = false;
do {
/* Check whether we need to move buffer. */
- if (bo->resource &&
+ if (bo->resource && !ctx->defrag &&
ttm_resource_compatible(bo->resource, placement,
force_space))
return 0;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 3e3c201a0222..771313162c8d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -626,10 +626,22 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,
* bo to be unbound and destroyed.
*/
- if (dst_use_tt)
+ if (bo->defrag_old_tt) {
+ /*
+ * Defrag move: @bo already points at the freshly allocated,
+ * beneficial-order tt that the move copied into. Hand the old
+ * tt (the move's source) to the ghost so it is kept populated
+ * until @fence signals and then unbound and destroyed, instead
+ * of being torn down synchronously. @bo keeps its new tt.
+ */
+ ghost_obj->ttm = bo->defrag_old_tt;
+ ghost_obj->defrag_old_tt = NULL;
+ bo->defrag_old_tt = NULL;
+ } else if (dst_use_tt) {
ghost_obj->ttm = NULL;
- else
+ } else {
bo->ttm = NULL;
+ }
dma_resv_unlock(&ghost_obj->base._resv);
ttm_bo_put(ghost_obj);
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 6826c8ae6fc5..340855e9bcfe 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -860,8 +860,11 @@ static int ttm_pool_iter_lower_order(struct ttm_pool_alloc_iter *it)
* pages. Record it so the driver can later try to defragment the object
* back to beneficial order.
*/
- if (it->beneficial_order && it->order == it->beneficial_order)
+ if (it->beneficial_order && it->order == it->beneficial_order) {
it->tt->page_flags |= TTM_TT_FLAG_BENEFICIAL_ORDER_FAILED;
+ if (it->ctx->defrag)
+ return -ENOMEM;
+ }
it->order--;
it->page_caching = it->tt->caching;
diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h
index fd814b5cf535..f4f8a8c29f04 100644
--- a/include/drm/ttm/ttm_bo.h
+++ b/include/drm/ttm/ttm_bo.h
@@ -135,6 +135,16 @@ struct ttm_buffer_object {
* reservation lock.
*/
struct sg_table *sg;
+
+ /**
+ * @defrag_old_tt: Used internally during a defrag move to hand the BO's
+ * previous, populated struct ttm_tt to the driver's move callback so
+ * that it can copy the contents into the freshly allocated
+ * beneficial-order backing. Set by TTM, consumed by the driver; the old
+ * tt is freed (or restored on failure) by TTM once the move completes.
+ * Protected by the reservation lock.
+ */
+ struct ttm_tt *defrag_old_tt;
};
#define TTM_BO_MAP_IOMEM_MASK 0x80
@@ -194,6 +204,13 @@ struct ttm_operation_ctx {
* reclaim/compaction before falling back to a smaller order anyway.
*/
bool beneficial_reclaim_backoff;
+ /**
+ * @defrag: Defragmentation pass. Attempt a move even when the bo's
+ * current resource is already compatible with the requested placement,
+ * so that the backing store can be reallocated at the pool's beneficial
+ * order.
+ */
+ bool defrag;
/**
* @resv: Reservation object to be used together with
* @allow_res_evict.
--
2.34.1
next prev parent reply other threads:[~2026-07-10 21:54 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 21:54 [PATCH v2 00/33] drm/ttm, drm/xe: Minimize dma-resv hold times and defragment sub-optimally backed BOs Matthew Brost
2026-07-10 21:54 ` [PATCH v2 01/33] drm/ttm/pool: Allow backing off reclaim at the beneficial order Matthew Brost
2026-07-11 10:38 ` Christian König
2026-07-11 13:27 ` Matthew Brost
2026-07-10 21:54 ` [PATCH v2 02/33] drm/ttm/pool: Add ttm_pool_page_order_nodma() helper Matthew Brost
2026-07-11 10:39 ` Christian König
2026-07-11 12:40 ` Matthew Brost
2026-07-10 21:54 ` [PATCH v2 03/33] drm/ttm: Record sub-optimal page order allocations in ttm_tt Matthew Brost
2026-07-10 21:54 ` [PATCH v2 04/33] drm/ttm: Introduce ttm_pool_alloc_iter for __ttm_pool_alloc() Matthew Brost
2026-07-10 21:54 ` Matthew Brost [this message]
2026-07-10 21:54 ` [PATCH v2 06/33] drm/ttm: Add fault injection for beneficial-order allocation failures Matthew Brost
2026-07-10 21:54 ` [PATCH v2 07/33] drm/ttm: Harvest beneficial-order pages on defragmentation moves Matthew Brost
2026-07-10 21:54 ` [PATCH v2 08/33] drm/ttm: Bound page (re)allocation per defragmentation move Matthew Brost
2026-07-10 21:54 ` [PATCH v2 09/33] drm/ttm: Preallocate beneficial-order defrag pages outside the lock Matthew Brost
2026-07-10 21:54 ` [PATCH v2 10/33] drm/ttm: Add full out-of-lock preallocation for ttm_pool_alloc() Matthew Brost
2026-07-10 21:54 ` [PATCH v2 11/33] drm/gpusvm: Add a DMA-mapping accounting callback Matthew Brost
2026-07-10 21:54 ` [PATCH v2 12/33] drm/xe: Add debugfs stats for DMA-mapped pages per order Matthew Brost
2026-07-10 21:54 ` [PATCH v2 13/33] drm/xe: Flush L2 asynchronously in xe_bo_trigger_rebind() Matthew Brost
2026-07-10 21:54 ` [PATCH v2 14/33] drm/xe: Destroy page tables after unlinking all VMAs on VM close Matthew Brost
2026-07-10 21:54 ` [PATCH v2 15/33] drm/xe: Track BOs backed at a sub-optimal page order Matthew Brost
2026-07-10 21:54 ` [PATCH v2 16/33] drm/xe: Back off beneficial-order reclaim under defrag pressure Matthew Brost
2026-07-10 21:54 ` [PATCH v2 17/33] drm/xe: Add xe_migrate_copy_defrag() for on-GPU defrag copies Matthew Brost
2026-07-10 21:54 ` [PATCH v2 18/33] drm/xe: Handle defrag moves in xe_bo_move() Matthew Brost
2026-07-10 21:54 ` [PATCH v2 19/33] drm/xe: Skip self-copies for borrowed pages on defrag moves Matthew Brost
2026-07-10 21:54 ` [PATCH v2 20/33] drm/xe: Add a page defragmentation worker Matthew Brost
2026-07-10 21:54 ` [PATCH v2 21/33] drm/xe: Add defrag GT stats Matthew Brost
2026-07-10 21:54 ` [PATCH v2 22/33] drm/xe: Add Kconfig.profile options for BO defrag configuration Matthew Brost
2026-07-10 21:54 ` [PATCH v2 23/33] drm/xe: Defrag using out-of-lock page preallocation Matthew Brost
2026-07-10 21:54 ` [PATCH v2 24/33] drm/xe: Add defrag profiling tracepoints Matthew Brost
2026-07-10 21:54 ` [PATCH v2 25/33] drm/xe: Preallocate system BO backing outside the dma-resv lock Matthew Brost
2026-07-10 21:54 ` [PATCH v2 26/33] drm/xe: Add tracepoint for xe_gem_create_ioctl Matthew Brost
2026-07-10 21:54 ` [PATCH v2 27/33] drm/xe: Add IOVA-based xe_res_cursor variant Matthew Brost
2026-07-10 21:54 ` [PATCH v2 28/33] drm/xe: Use IOVA-based DMA mapping for eligible tt BOs Matthew Brost
2026-07-10 21:54 ` [PATCH v2 29/33] drm/xe: Add per-device dependency scheduler for IOVA defrag finalize Matthew Brost
2026-07-10 21:54 ` [PATCH v2 30/33] drm/xe: Add packed copy-step IOVA mapping for defrag Matthew Brost
2026-07-10 21:54 ` [PATCH v2 31/33] drm/xe: Blit src-natural to dst-packed for defrag-IOVA copies Matthew Brost
2026-07-10 21:54 ` [PATCH v2 32/33] drm/xe: Finalize defrag-IOVA moves with post-copy job Matthew Brost
2026-07-10 21:54 ` [PATCH v2 33/33] drm/amdgpu: Preallocate system BO pages outside the reservation lock Matthew Brost
2026-07-10 22:03 ` ✗ CI.checkpatch: warning for drm/ttm, drm/xe: Minimize dma-resv hold times and defragment sub-optimally backed BOs Patchwork
2026-07-10 22:05 ` ✓ CI.KUnit: success " Patchwork
2026-07-10 22:20 ` ✗ CI.checksparse: warning " Patchwork
2026-07-10 22:40 ` ✗ Xe.CI.BAT: failure " Patchwork
2026-07-11 10:33 ` [PATCH v2 00/33] " Christian König
2026-07-11 13:49 ` Matthew Brost
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=20260710215442.2444235-6-matthew.brost@intel.com \
--to=matthew.brost@intel.com \
--cc=airlied@gmail.com \
--cc=carlos.santa@intel.com \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=matthew.auld@intel.com \
--cc=mripard@kernel.org \
--cc=ray.huang@amd.com \
--cc=ryanneph@google.com \
--cc=simona@ffwll.ch \
--cc=thomas.hellstrom@linux.intel.com \
--cc=tzimmermann@suse.de \
/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