summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe/xe_pt.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-24 16:21:27 +0200
commit8f9aa2c90530ab92301a82231ae44f3722becd93 (patch)
treefb282e955b0a880b07131a135257fe3ec764e928 /drivers/gpu/drm/xe/xe_pt.c
parent93467b31bec6da512b51544e5e4584f2745e995e (diff)
parent155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 (diff)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/gpu/drm/xe/xe_pt.c')
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c72
1 files changed, 57 insertions, 15 deletions
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 8e5f4f0dea3f..c3a69103b9bb 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -432,6 +432,7 @@ xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent,
static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
struct xe_pt_stage_bind_walk *xe_walk)
{
+ struct xe_bo *bo = xe_vma_bo(xe_walk->vma);
u64 size, dma;
if (level > MAX_HUGEPTE_LEVEL)
@@ -445,8 +446,8 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
if (next - xe_walk->va_curs_start > xe_walk->curs->size)
return false;
- /* null VMA's do not have dma addresses */
- if (xe_vma_is_null(xe_walk->vma))
+ /* null VMA's and purged BO's do not have dma addresses */
+ if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo)))
return true;
/* if we are clearing page table, no dma addresses*/
@@ -467,6 +468,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
static bool
xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
{
+ struct xe_bo *bo = xe_vma_bo(xe_walk->vma);
struct xe_res_cursor curs = *xe_walk->curs;
if (!IS_ALIGNED(addr, SZ_64K))
@@ -475,8 +477,8 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
if (next > xe_walk->l0_end_addr)
return false;
- /* null VMA's do not have dma addresses */
- if (xe_vma_is_null(xe_walk->vma))
+ /* null VMA's and purged BO's do not have dma addresses */
+ if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo)))
return true;
xe_res_next(&curs, addr - xe_walk->va_curs_start);
@@ -707,7 +709,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
{
struct xe_device *xe = tile_to_xe(tile);
struct xe_bo *bo = xe_vma_bo(vma);
- struct xe_res_cursor curs;
+ struct xe_res_cursor curs = {};
struct xe_vm *vm = xe_vma_vm(vma);
struct xe_pt_stage_bind_walk xe_walk = {
.base = {
@@ -884,13 +886,21 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset,
{
struct xe_pt_zap_ptes_walk *xe_walk =
container_of(walk, typeof(*xe_walk), base);
- struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
+ struct xe_pt *xe_child;
pgoff_t end_offset;
- XE_WARN_ON(!*child);
XE_WARN_ON(!level);
/*
+ * Below would be unexpected behavior that needs to be root caused
+ * but better warn and bail than crash the driver.
+ */
+ if (XE_WARN_ON(!*child))
+ return 0;
+
+ xe_child = container_of(*child, typeof(*xe_child), base);
+
+ /*
* Note that we're called from an entry callback, and we're dealing
* with the child of that entry rather than the parent, so need to
* adjust level down.
@@ -1077,7 +1087,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma)
xe_pt_commit_prepare_locks_assert(vma);
if (xe_vma_is_userptr(vma))
- xe_svm_assert_held_read(vm);
+ xe_svm_assert_held_read_or_inject_write(vm);
}
static void xe_pt_commit(struct xe_vma *vma,
@@ -1398,6 +1408,38 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update)
}
#if IS_ENABLED(CONFIG_DRM_GPUSVM)
+/*
+ * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit()
+ * and the matching late release in xe_pt_update_ops_run(). Read mode by
+ * default; write mode when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on,
+ * because a userptr op in this critical section may invoke the injected
+ * xe_vma_userptr_force_invalidate() path that calls
+ * drm_gpusvm_unmap_pages() with ctx->in_notifier=true, which requires the
+ * lock held for write.
+ */
+static void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm)
+{
+#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
+ down_write(&vm->svm.gpusvm.notifier_lock);
+#else
+ xe_svm_notifier_lock(vm);
+#endif
+}
+
+static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm)
+{
+#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
+ up_write(&vm->svm.gpusvm.notifier_lock);
+#else
+ xe_svm_notifier_unlock(vm);
+#endif
+}
+#else
+static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { }
+static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { }
+#endif
+
+#if IS_ENABLED(CONFIG_DRM_GPUSVM)
#ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT
static bool xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
@@ -1428,7 +1470,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma,
struct xe_userptr_vma *uvma;
unsigned long notifier_seq;
- xe_svm_assert_held_read(vm);
+ xe_svm_assert_held_read_or_inject_write(vm);
if (!xe_vma_is_userptr(vma))
return 0;
@@ -1458,7 +1500,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op,
{
int err = 0;
- xe_svm_assert_held_read(vm);
+ xe_svm_assert_held_read_or_inject_write(vm);
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
@@ -1530,12 +1572,12 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update)
if (err)
return err;
- xe_svm_notifier_lock(vm);
+ xe_pt_svm_userptr_notifier_lock(vm);
list_for_each_entry(op, &vops->list, link) {
err = op_check_svm_userptr(vm, op, pt_update_ops);
if (err) {
- xe_svm_notifier_unlock(vm);
+ xe_pt_svm_userptr_notifier_unlock(vm);
break;
}
}
@@ -2371,7 +2413,7 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
vma->tile_invalidated & ~BIT(tile->id));
vma->tile_staged &= ~BIT(tile->id);
if (xe_vma_is_userptr(vma)) {
- xe_svm_assert_held_read(vm);
+ xe_svm_assert_held_read_or_inject_write(vm);
to_userptr_vma(vma)->userptr.initial_bind = true;
}
@@ -2407,7 +2449,7 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
if (!vma->tile_present) {
list_del_init(&vma->combined_links.rebind);
if (xe_vma_is_userptr(vma)) {
- xe_svm_assert_held_read(vm);
+ xe_svm_assert_held_read_or_inject_write(vm);
spin_lock(&vm->userptr.invalidated_lock);
list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link);
@@ -2683,7 +2725,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
}
if (pt_update_ops->needs_svm_lock)
- xe_svm_notifier_unlock(vm);
+ xe_pt_svm_userptr_notifier_unlock(vm);
/*
* The last fence is only used for zero bind queue idling; migrate