[PATCH v6 8/8] drm/xe: Favor evicting WB-cached BOs within each priority band

Matthew Brost <[email protected]> Thu, 30 Jul 2026 19:24:39 -0700
Newsgroups org.freedesktop.lists.intel-xe
Message-ID <[email protected]>
Split each of the four logical XE_BO_PRIORITY_* bands into two adjacent
TTM LRU priorities, using the now-doubled TTM_MAX_BO_PRIORITY (8). Within
a band, buffer objects whose system-memory backing uses write-back (WB)
CPU caching are placed at the lower (evicted-first) of the two actual
priorities, while everything else - VRAM residents, write-combined/
uncached system memory, and kernel BOs - lands at the higher one.

Evicting a WB-cached BO is comparatively cheap since its contents are
already coherent with the CPU cache, so preferring these BOs for
eviction over otherwise equal-priority peers reduces the odds of having
to evict a more expensive placement first. This distinction has no
meaning for VRAM-only BOs, which always land in the higher of the two
priorities in their band.

Add xe_bo_ttm_priority() to encode a logical band and a BO into the
actual, stored bo::ttm.priority as (band << 1) | !favor_evict, and
XE_BO_PRIORITY_BAND() to recover the logical band from a previously
encoded priority. Update all existing readers/writers of bo->ttm.priority
(xe_bo_init_locked(), xe_bo_update_ttm_priority(), xe_vma_destroy(),
xe_vma_update_bo_priority(), xe_vm_update_bo_priority(), and
__xe_shrinker_walk()'s purge early-exit) to go through these helpers so
they keep operating on logical bands.

Assisted-by: GitHub_Copilot:claude-sonnet-5
Signed-off-by: Matthew Brost <[email protected]>
---
 drivers/gpu/drm/xe/xe_bo.c       |  4 ++--
 drivers/gpu/drm/xe/xe_bo.h       | 34 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_bo_types.h | 14 ++++++++++++-
 drivers/gpu/drm/xe/xe_shrinker.c |  3 ++-
 drivers/gpu/drm/xe/xe_vm.c       | 17 ++++++++++------
 5 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 2669c487b778..b3d77ee44746 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -2413,9 +2413,9 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
 	bo->cpu_caching = cpu_caching;
 	bo->ttm.base.funcs = &xe_gem_object_funcs;
 	if (type != ttm_bo_type_device || !vm)
-		bo->ttm.priority = XE_BO_PRIORITY_HIGHEST;
+		bo->ttm.priority = xe_bo_ttm_priority(XE_BO_PRIORITY_HIGHEST, bo);
 	else
-		bo->ttm.priority = xe_vm_bo_priority(vm);
+		bo->ttm.priority = xe_bo_ttm_priority(xe_vm_bo_priority(vm), bo);
 	INIT_LIST_HEAD(&bo->pinned_link);
 #ifdef CONFIG_PROC_FS
 	INIT_LIST_HEAD(&bo->client_link);
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 6506001730ab..ab15d0909832 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -8,6 +8,8 @@
 
 #include <drm/ttm/ttm_tt.h>
 
+#include <uapi/drm/xe_drm.h>
+
 #include "xe_bo_types.h"
 #include "xe_ggtt.h"
 #include "xe_macros.h"
@@ -164,6 +166,38 @@ static inline struct xe_bo *gem_to_xe_bo(const struct drm_gem_object *obj)
 
 #define xe_bo_device(bo) ttm_to_xe_device((bo)->ttm.bdev)
 
+/**
+ * xe_bo_ttm_priority() - Encode a BO's actual TTM LRU priority
+ * @band: The logical priority band to use, one of the XE_BO_PRIORITY_*
+ * defines.
+ * @bo: The buffer object @band is to be applied to.
+ *
+ * Each logical priority band is split into two adjacent, actual TTM LRU
+ * priorities: a lower one, favored for eviction, for @bo's whose system
+ * memory backing store uses write-back (WB) CPU caching, and a higher one
+ * for everything else (VRAM residents, write-combined/uncached system
+ * memory, and kernel BOs with no cpu_caching set at all). Evicting a
+ * WB-cached BO is comparatively cheap, since its contents are already
+ * coherent with the CPU cache, so preferring them for eviction over
+ * otherwise equal-priority peers reduces the odds of having to evict a
+ * more expensive placement first. This distinction is meaningless for
+ * VRAM-only BOs, which naturally always fall into the higher of the two
+ * priorities within their band.
+ *
+ * Return: The actual TTM LRU priority to use for @bo, suitable for storing
+ * directly into bo::ttm.priority. Use XE_BO_PRIORITY_BAND() to recover the
+ * logical band from a previously encoded priority.
+ */
+static inline unsigned int xe_bo_ttm_priority(unsigned int band, const struct xe_bo *bo)
+{
+	bool favor_evict = bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB;
+
+	BUILD_BUG_ON(XE_BO_PRIORITY_NUM_BANDS * 2 != TTM_MAX_BO_PRIORITY);
+	xe_assert(xe_bo_device(bo), band < XE_BO_PRIORITY_NUM_BANDS);
+
+	return (band << 1) | !favor_evict;
+}
+
 static inline struct xe_bo *xe_bo_get(struct xe_bo *bo)
 {
 	if (bo)
diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h
index 7b02552652d3..dfbce9e5b8e8 100644
--- a/drivers/gpu/drm/xe/xe_bo_types.h
+++ b/drivers/gpu/drm/xe/xe_bo_types.h
@@ -23,11 +23,23 @@ struct xe_vm;
 
 #define XE_BO_MAX_PLACEMENTS	3
 
-/* TODO: To be selected with VM_MADVISE */
+/*
+ * TODO: To be selected with VM_MADVISE
+ *
+ * These are logical TTM LRU priority *bands*; each is further split into
+ * two actual TTM LRU priorities by xe_bo_ttm_priority(), to favor evicting
+ * write-back (WB) cached system-memory BOs ahead of their otherwise
+ * equal-priority peers within the same band, see xe_bo_ttm_priority().
+ * Use XE_BO_PRIORITY_BAND() to recover the logical band from a BO's actual,
+ * encoded bo->ttm.priority.
+ */
 #define	XE_BO_PRIORITY_LOW	0
 #define	XE_BO_PRIORITY_NORMAL	1
 #define	XE_BO_PRIORITY_HIGH	2
 #define	XE_BO_PRIORITY_HIGHEST	3
+#define	XE_BO_PRIORITY_NUM_BANDS	4
+
+#define XE_BO_PRIORITY_BAND(ttm_priority) ((ttm_priority) >> 1)
 
 /**
  * struct xe_bo - Xe buffer object
diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c
index 81895b587b56..682594baa17a 100644
--- a/drivers/gpu/drm/xe/xe_shrinker.c
+++ b/drivers/gpu/drm/xe/xe_shrinker.c
@@ -84,7 +84,8 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
 			 * rather than walking (and trylocking) BOs that can
 			 * never satisfy this pass.
 			 */
-			if (flags.purge && ttm_bo->priority > XE_BO_PRIORITY_LOW)
+			if (flags.purge &&
+			    XE_BO_PRIORITY_BAND(ttm_bo->priority) > XE_BO_PRIORITY_LOW)
 				break;
 
 			if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 1be367b1c671..93d19bd0a8e5 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1235,7 +1235,8 @@ static void vma_destroy_cb(struct dma_fence *fence,
 /**
  * xe_bo_update_ttm_priority() - Change a BO's TTM LRU priority
  * @bo: The buffer object
- * @priority: The new TTM LRU priority, one of the XE_BO_PRIORITY_* levels
+ * @priority: The new TTM LRU priority band, one of the XE_BO_PRIORITY_*
+ * levels
  *
  * Private, user BOs of non-fault-mode VMs have bo->ttm.bulk_move set to
  * their VM's LRU bulk-move range (see xe_bo_init_locked()), which tracks a
@@ -1248,6 +1249,9 @@ static void vma_destroy_cb(struct dma_fence *fence,
  * its bulk-move range moves it with a plain, non-bulk LRU update instead,
  * and reattaching afterwards re-inserts it into the new priority's bucket.
  *
+ * @priority is a logical priority band; see xe_bo_ttm_priority() for how it
+ * is translated into @bo's actual, stored bo::ttm.priority.
+ *
  * Context: Caller must hold @bo's dma-resv lock.
  */
 void xe_bo_update_ttm_priority(struct xe_bo *bo, int priority)
@@ -1256,12 +1260,12 @@ void xe_bo_update_ttm_priority(struct xe_bo *bo, int priority)
 
 	xe_bo_assert_held(bo);
 
-	if (bo->ttm.priority == priority)
+	if (XE_BO_PRIORITY_BAND(bo->ttm.priority) == priority)
 		return;
 
 	if (bulk)
 		ttm_bo_set_bulk_move(&bo->ttm, NULL);
-	bo->ttm.priority = priority;
+	bo->ttm.priority = xe_bo_ttm_priority(priority, bo);
 	ttm_bo_move_to_lru_tail_unlocked(&bo->ttm);
 	if (bulk)
 		ttm_bo_set_bulk_move(&bo->ttm, bulk);
@@ -1300,7 +1304,7 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence)
 		 */
 		if (!drm_gem_is_imported(&bo->ttm.base) &&
 		    !bo->purgeable.vma_count &&
-		    bo->ttm.priority != XE_BO_PRIORITY_LOW)
+		    XE_BO_PRIORITY_BAND(bo->ttm.priority) != XE_BO_PRIORITY_LOW)
 			xe_bo_update_ttm_priority(bo, XE_BO_PRIORITY_LOW);
 	}
 
@@ -3622,7 +3626,7 @@ void xe_vma_update_bo_priority(struct xe_vma *vma)
 	xe_assert(vm->xe, !bo->vm || bo->vm == vm);
 
 	priority = bo->vm ? xe_vm_bo_priority(vm) : XE_BO_PRIORITY_HIGHEST;
-	if (bo->ttm.priority == priority)
+	if (XE_BO_PRIORITY_BAND(bo->ttm.priority) == priority)
 		return;
 
 	xe_bo_assert_held(bo);
@@ -5132,7 +5136,8 @@ static void xe_vm_update_bo_priority(struct xe_vm *vm, int priority)
 		struct xe_vma *vma = gpuva_to_vma(gpuva);
 		struct xe_bo *bo = xe_vma_bo(vma);
 
-		if (!bo || bo->vm != vm || bo->ttm.priority == priority)
+		if (!bo || bo->vm != vm ||
+		    XE_BO_PRIORITY_BAND(bo->ttm.priority) == priority)
 			continue;
 
 		xe_bo_update_ttm_priority(bo, priority);
-- 
2.34.1