* [RFC PATCH 1/1] drm/xe: keep VM-bound WC BOs resident during reclaim
[not found] <20260728061443.33616-1-neil.zhong@ugreen.com>
@ 2026-07-28 6:14 ` Neil Zhong
2026-07-28 6:31 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Neil Zhong @ 2026-07-28 6:14 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, rodrigo.vivi, airlied, simona,
dri-devel, linux-kernel
On x86, restoring a backed-up write-combined (WC) buffer object can be
expensive. The restore path allocates WB pages and converts them with
set_pages_array_wc(), which performs synchronous cache and TLB flushes.
Xe currently allows its shrinker to back up the pages of a WC BO while
the BO still has GPUVA mappings created by VM_BIND. A later validation
restores the pages while holding the BO's dma-resv. The cache-attribute
conversion then serializes EXEC, VM_BIND and dma-buf users on that
reservation object.
This was observed as intermittent HDR 4K60 playback stalls on two
Panther Lake systems with 8 GiB of memory. In a pre-change reproducer,
the maximum ioctl latencies were 549 ms for XE_EXEC, 291 ms for
XE_VM_BIND and 343 ms for DMA-BUF IMPORT. ttm_tt_restore reached 80.6 ms.
Keep a non-purgeable WC BO resident while it has at least one GPUVA
mapping. Purgeable BOs are still discarded, and after the last
VM_UNBIND the BO becomes reclaimable again. Add an A/B module parameter
which can restore the old behavior.
With the change, a 21-minute capture had no XE_EXEC, XE_VM_BIND or
DMA-BUF ioctl over the 16.7 ms frame interval. Their respective maxima
were 348 us, 136 us and 20 us. The ttm_tt_restore maximum was 8.33 ms,
and the set_pages_array_wc call rate fell from 14.64/s to 0.179/s.
The change intentionally trades reclaimable memory for latency while a
WC BO remains mapped. It does not take an additional BO reference or
change teardown: VM destruction and process exit remove the GPUVA
mappings and drop their existing references. During testing,
MemAvailable remained near 3 GiB and the dma-buf working set released
five 24 MiB surfaces while playback continued.
Signed-off-by: Neil Zhong <neil.zhong@ugreen.com>
---
drivers/gpu/drm/xe/xe_defaults.h | 1 +
drivers/gpu/drm/xe/xe_module.c | 6 ++++++
drivers/gpu/drm/xe/xe_module.h | 2 +-
drivers/gpu/drm/xe/xe_shrinker.c | 28 ++++++++++++++++++++++++++++
4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h
index c8ae1d5f..645e289f 100644
--- a/drivers/gpu/drm/xe/xe_defaults.h
+++ b/drivers/gpu/drm/xe/xe_defaults.h
@@ -22,5 +22,6 @@
#define XE_DEFAULT_WEDGED_MODE XE_WEDGED_MODE_UPON_CRITICAL_ERROR
#define XE_DEFAULT_WEDGED_MODE_STR "upon-critical-error"
#define XE_DEFAULT_SVM_NOTIFIER_SIZE 512
+#define XE_DEFAULT_ALLOW_BOUND_WC_SHRINK false
#endif
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index 848d6526..67bcaf54 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -22,6 +22,7 @@
#include "xe_sched_job.h"
struct xe_modparam xe_modparam = {
+ .allow_bound_wc_shrink = XE_DEFAULT_ALLOW_BOUND_WC_SHRINK,
.probe_display = XE_DEFAULT_PROBE_DISPLAY,
.guc_log_level = XE_DEFAULT_GUC_LOG_LEVEL,
.force_probe = XE_DEFAULT_FORCE_PROBE,
@@ -33,6 +34,11 @@ struct xe_modparam xe_modparam = {
/* the rest are 0 by default */
};
+module_param_named(allow_bound_wc_shrink, xe_modparam.allow_bound_wc_shrink,
+ bool, 0600);
+MODULE_PARM_DESC(allow_bound_wc_shrink,
+ "Permit reclaim of VM-bound write-combined BOs");
+
module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600);
MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 "
"[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]");
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index a0eb7db0..bf2c49ce 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -12,6 +12,7 @@ struct work_struct;
/* Module modprobe variables */
struct xe_modparam {
+ bool allow_bound_wc_shrink;
bool probe_display;
int force_vram_bar_size;
int guc_log_level;
@@ -32,4 +33,3 @@ bool xe_destroy_wq_queue(struct work_struct *work);
void xe_destroy_wq_flush(void);
#endif
-
diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c
index 83374cd5..caa39f39 100644
--- a/drivers/gpu/drm/xe/xe_shrinker.c
+++ b/drivers/gpu/drm/xe/xe_shrinker.c
@@ -11,6 +11,7 @@
#include <drm/ttm/ttm_tt.h>
#include "xe_bo.h"
+#include "xe_module.h"
#include "xe_pm.h"
#include "xe_shrinker.h"
@@ -54,6 +55,30 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
write_unlock(&shrinker->lock);
}
+static bool xe_shrinker_skip_bound_wc(struct ttm_buffer_object *ttm_bo,
+ const struct xe_bo_shrink_flags flags)
+{
+ struct xe_bo *bo;
+
+ if (flags.purge || xe_modparam.allow_bound_wc_shrink ||
+ !xe_bo_is_xe_bo(ttm_bo))
+ return false;
+
+ bo = ttm_to_xe_bo(ttm_bo);
+
+ /*
+ * Restoring a backed-up WC BO changes freshly allocated WB pages to WC.
+ * On x86 that runs CPA cache/TLB flushes synchronously while validation
+ * holds this BO's dma-resv. A VM-bound BO is also likely to be reused by
+ * a following EXEC, so reclaiming it can turn moderate memory pressure
+ * into a multi-client reservation-lock stall. Keep that working set
+ * resident; purgeable objects and objects after VM_UNBIND remain
+ * reclaimable.
+ */
+ return ttm_bo->ttm->caching == ttm_write_combined &&
+ xe_bo_is_vm_bound(bo);
+}
+
static s64 __xe_shrinker_walk(struct xe_device *xe,
struct ttm_operation_ctx *ctx,
const struct xe_bo_shrink_flags flags,
@@ -75,6 +100,9 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
continue;
ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
+ if (xe_shrinker_skip_bound_wc(ttm_bo, flags))
+ continue;
+
if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
continue;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [RFC PATCH 1/1] drm/xe: keep VM-bound WC BOs resident during reclaim
2026-07-28 6:14 ` [RFC PATCH 1/1] drm/xe: keep VM-bound WC BOs resident during reclaim Neil Zhong
@ 2026-07-28 6:31 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-28 6:31 UTC (permalink / raw)
To: Neil Zhong; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unconditional dereference of `ttm_bo->ttm` in `xe_shrinker_skip_bound_wc` can cause a kernel panic due to a NULL pointer dereference.
--
--- Patch [1]: [RFC PATCH 1/1] drm/xe: keep VM-bound WC BOs resident during reclaim ---
commit 1cd578b99da153c47f4b384eb842462aba8d14d3
Author: Neil Zhong <neil.zhong@ugreen.com>
drm/xe: keep VM-bound WC BOs resident during reclaim
This commit adds logic to prevent the shrinker from reclaiming VM-bound
write-combined (WC) buffer objects to avoid expensive restore operations.
It introduces a module parameter to control this behavior and trades
reclaimable memory for lower latency.
> diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c
> index 83374cd576608..caa39f39cd155 100644
> --- a/drivers/gpu/drm/xe/xe_shrinker.c
> +++ b/drivers/gpu/drm/xe/xe_shrinker.c
[ ... ]
> @@ -54,6 +55,30 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
> write_unlock(&shrinker->lock);
> }
>
> +static bool xe_shrinker_skip_bound_wc(struct ttm_buffer_object *ttm_bo,
> + const struct xe_bo_shrink_flags flags)
> +{
> + struct xe_bo *bo;
> +
> + if (flags.purge || xe_modparam.allow_bound_wc_shrink ||
> + !xe_bo_is_xe_bo(ttm_bo))
> + return false;
> +
> + bo = ttm_to_xe_bo(ttm_bo);
> +
> + /*
> + * Restoring a backed-up WC BO changes freshly allocated WB pages to WC.
> + * On x86 that runs CPA cache/TLB flushes synchronously while validation
> + * holds this BO's dma-resv. A VM-bound BO is also likely to be reused by
> + * a following EXEC, so reclaiming it can turn moderate memory pressure
> + * into a multi-client reservation-lock stall. Keep that working set
> + * resident; purgeable objects and objects after VM_UNBIND remain
> + * reclaimable.
> + */
> + return ttm_bo->ttm->caching == ttm_write_combined &&
> + xe_bo_is_vm_bound(bo);
> +}
> +
> static s64 __xe_shrinker_walk(struct xe_device *xe,
> struct ttm_operation_ctx *ctx,
> const struct xe_bo_shrink_flags flags,
> @@ -75,6 +100,9 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
> continue;
>
> ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
> + if (xe_shrinker_skip_bound_wc(ttm_bo, flags))
> + continue;
> +
> if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
> continue;
[Severity: High]
Does this code risk a NULL pointer dereference when accessing ttm_bo->ttm
inside xe_shrinker_skip_bound_wc()?
A buffer object can be on the LRU list with a NULL ttm pointer under
certain conditions, such as during early initialization or if pipeline
gutting removes it.
While ttm_bo_shrink_suitable() safely handles this by checking if
ttm_bo->ttm is populated, the new xe_shrinker_skip_bound_wc() check is
invoked prior to ttm_bo_shrink_suitable(). This leads to an unconditional
dereference of ttm_bo->ttm in xe_shrinker_skip_bound_wc() before the safety
check occurs.
Could the check be moved after ttm_bo_shrink_suitable(), or should
xe_shrinker_skip_bound_wc() explicitly verify ttm_bo->ttm before
dereferencing it?
--
Sashiko AI review · https://sashiko.dev/#/patchset/4BF27CE161436C73+20260728061443.33616-2-neil.zhong@ugreen.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-28 6:31 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260728061443.33616-1-neil.zhong@ugreen.com>
2026-07-28 6:14 ` [RFC PATCH 1/1] drm/xe: keep VM-bound WC BOs resident during reclaim Neil Zhong
2026-07-28 6:31 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.