[PATCH v2 23/33] drm/xe: Defrag using out-of-lock page preallocation

From: Matthew Brost

Date: Fri Jul 10 2026 - 18:04:59 EST


Convert xe_bo_defrag_one() to a three-phase scheme that moves the
expensive beneficial-order allocations out of the BO dma-resv lock:

1. Under the lock, re-check eligibility, capture the tt cpu-caching and
read the exact sub-optimal page count (ttm_tt_suboptimal_pages()),
bounded by the run budget.
2. Drop the lock and preallocate that many beneficial-order chunks via
ttm_pool_prealloc_fill(); reclaim/compaction stalls and the cpu
cache mode change both happen here, outside the lock.
3. Re-take the lock, re-check, and validate consuming the prealloc bag.
Any shortfall harvests the old backing instead of reclaiming, so the
move never stalls under the lock; the BO re-queues for a later pass.

With both allocation and caching hoisted out, the BO-lock held time
drops to copy-bound (median ~0.27ms, p95 ~2ms); the remaining tail is
lock-wait behind active rendering, as intended.

Cc: Carlos Santa <carlos.santa@xxxxxxxxx>
Cc: Ryan Neph <ryanneph@xxxxxxxxxx>
Cc: Christian Koenig <christian.koenig@xxxxxxx>
Cc: Huang Rui <ray.huang@xxxxxxx>
Cc: Matthew Auld <matthew.auld@xxxxxxxxx>
Cc: Maarten Lankhorst <maarten.lankhorst@xxxxxxxxxxxxxxx>
Cc: Maxime Ripard <mripard@xxxxxxxxxx>
Cc: Thomas Zimmermann <tzimmermann@xxxxxxx>
Cc: David Airlie <airlied@xxxxxxxxx>
Cc: Simona Vetter <simona@xxxxxxxx>
Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: Thomas Hellström <thomas.hellstrom@xxxxxxxxxxxxxxx>
Assisted-by: GitHub_Copilot:claude-opus-4.8
Signed-off-by: Matthew Brost <matthew.brost@xxxxxxxxx>
---
drivers/gpu/drm/xe/xe_bo.c | 46 +++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 405316d0d116..2f014fc9f988 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1249,15 +1249,55 @@ static int xe_bo_defrag_one(struct xe_device *xe, struct xe_bo *bo,
.defrag_bytes_remaining = budget,
};
struct ttm_buffer_object *ttm_bo = &bo->ttm;
+ struct ttm_pool *pool = &ttm_bo->bdev->pool;
+ struct ttm_pool_prealloc pp = {};
struct ttm_placement placement;
struct ttm_place place;
+ enum ttm_caching tt_caching;
+ unsigned int order, want = 0;
int ret = 0;

*consumed = 0;

+ /*
+ * Phase 1: under the BO lock, re-check eligibility and estimate how
+ * many beneficial-order chunks the move will (re)allocate, bounded by
+ * the run's byte budget. Then drop the lock so the high-order, possibly
+ * reclaim/compaction stalling allocations happen unlocked.
+ */
+ xe_bo_lock(bo, false);
+ if (!xe_bo_needs_defrag(bo)) {
+ xe_bo_defrag_remove(bo);
+ xe_bo_unlock(bo);
+ goto out;
+ }
+ tt_caching = bo->ttm.ttm->caching;
+ order = ttm_pool_prealloc_order(pool);
+ if (order && ttm_bo->ttm) {
+ u32 suboptimal = ttm_tt_suboptimal_pages(ttm_bo->ttm);
+ u64 cap = min_t(u64, budget, xe_bo_size(bo)) >> PAGE_SHIFT;
+
+ /* Prealloc only the beneficial-order chunks the move replaces. */
+ want = DIV_ROUND_UP(min_t(u64, suboptimal, cap), 1UL << order);
+ }
+ xe_bo_unlock(bo);
+
+ /* Phase 2: preallocate outside the lock; */
+ if (want) {
+ ret = ttm_pool_prealloc_fill(pool, tt_caching, &pp, want);
+ if (ret || !pp.count) {
+ ret = ret ?: -ENOMEM;
+ xe_gt_stats_incr(xe_root_mmio_gt(xe),
+ XE_GT_STATS_ID_DEFRAG_FAILED_COUNT, 1);
+ goto out_err;
+ }
+ }
+
+ /*
+ * Phase 3: re-take the lock, re-check, and validate using the prealloc.
+ */
xe_bo_lock(bo, false);

- /* Re-check eligibility under the BO lock. */
if (!xe_bo_needs_defrag(bo)) {
xe_bo_defrag_remove(bo);
goto unlock;
@@ -1267,6 +1307,7 @@ static int xe_bo_defrag_one(struct xe_device *xe, struct xe_bo *bo,
place.flags |= TTM_PL_FLAG_TEMPORARY;
placement.num_placement = 1;
placement.placement = &place;
+ ctx.prealloc = &pp;

/*
* On success the move reallocates the backing at beneficial order and
@@ -1309,6 +1350,9 @@ static int xe_bo_defrag_one(struct xe_device *xe, struct xe_bo *bo,

unlock:
xe_bo_unlock(bo);
+out_err:
+ ttm_pool_prealloc_fini(pool, &pp);
+out:
return ret;
}

--
2.34.1