SVM fault handling and VM rebind work may still run when PCI error
recovery starts or the device becomes permanently wedged. This work can
validate BOs, update page tables or submit migration work after device
I/O has been blocked.

Stop SVM fault handling, pagemap population, device-memory copies and
preempt rebind work when device I/O is blocked.

v2:
 - Protect VM and SVM hardware access with the common SRCU gate (Thomas)
 - Hold the gate across page-fault, migration and rebind work.
 - Remove the racy device state checks (Matt)

Cc: Matthew Brost <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Himal Prasad Ghimiray <[email protected]>
Cc: Rodrigo Vivi <[email protected]>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Arvind Yadav <[email protected]>

 drm/xe: Stop VM work when device I/O is blocked
---
 drivers/gpu/drm/xe/xe_svm.c | 29 +++++++++++++++++++++++++----
 drivers/gpu/drm/xe/xe_vm.c  | 11 +++++++++--
 2 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index f39e647512ad..4788216546ff 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -11,6 +11,7 @@
 #include <drm/drm_pagemap_util.h>
 
 #include "xe_bo.h"
+#include "xe_device.h"
 #include "xe_exec_queue_types.h"
 #include "xe_gt_stats.h"
 #include "xe_log.h"
@@ -242,6 +243,7 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
        ktime_t start = xe_gt_stats_ktime_get();
        u64 adj_start = mmu_range->start, adj_end = mmu_range->end;
        u8 tile_mask = 0, id;
+       int io_idx;
        long err;
 
        xe_svm_assert_in_notifier(vm);
@@ -267,6 +269,9 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
        if (xe_vm_is_closed(vm))
                goto range_notifier_event_end;
 
+       if (xe_device_io_get(xe, &io_idx))
+               goto range_notifier_event_end;
+
        /*
         * XXX: Less than ideal to always wait on VM's resv slots if an
         * invalidation is not required. Could walk range list twice to figure
@@ -283,7 +288,7 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
                                                               &adj_start,
                                                               &adj_end);
        if (!tile_mask)
-               goto range_notifier_event_end;
+               goto out_io;
 
        xe_device_wmb(xe);
 
@@ -292,6 +297,9 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
        if (!WARN_ON_ONCE(err))
                xe_tlb_inval_batch_wait(&batch);
 
+out_io:
+       xe_device_io_put(io_idx);
+
 range_notifier_event_end:
        r = first;
        drm_gpusvm_for_each_range(r, notifier, adj_start, adj_end)
@@ -599,6 +607,8 @@ static int xe_svm_copy(struct page **pages,
 #define XE_VRAM_ADDR_INVALID   ~0x0ull
        u64 vram_addr = XE_VRAM_ADDR_INVALID;
        int err = 0, pos = 0;
+       bool io_held = false;
+       int io_idx;
        bool sram = dir == XE_SVM_COPY_TO_SRAM;
        ktime_t start = xe_gt_stats_ktime_get();
 
@@ -629,6 +639,12 @@ static int xe_svm_copy(struct page **pages,
                        vr = xe_page_to_vr(spage);
                        gt = xe_migrate_exec_queue(vr->migrate)->gt;
                        xe = vr->xe;
+
+                       err = xe_device_io_get(xe, &io_idx);
+                       if (err)
+                               goto err_out;
+
+                       io_held = true;
                }
                XE_WARN_ON(spage && xe_page_to_vr(spage) != vr);
 
@@ -749,6 +765,9 @@ static int xe_svm_copy(struct page **pages,
        if (pre_migrate_fence)
                dma_fence_wait(pre_migrate_fence, false);
 
+       if (io_held)
+               xe_device_io_put(io_idx);
+
        /*
         * XXX: We can't derive the GT here (or anywhere in this functions, but
         * compute always uses the primary GT so accumulate stats on the likely
@@ -1123,8 +1142,9 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap 
*dpagemap,
        struct xe_bo *bo;
        int err = 0, idx;
 
-       if (!drm_dev_enter(&xe->drm, &idx))
-               return -ENODEV;
+       err = xe_device_io_get(xe, &idx);
+       if (err)
+               return err;
 
        xe_pm_runtime_get(xe);
 
@@ -1166,7 +1186,8 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap 
*dpagemap,
                xe_bo_put(bo);
        }
        xe_pm_runtime_put(xe);
-       drm_dev_exit(idx);
+
+       xe_device_io_put(idx);
 
        return err;
 }
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 17dc4debe7c1..565d2ce91829 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -483,6 +483,7 @@ static void preempt_rebind_work_func(struct work_struct *w)
        unsigned int fence_count = 0;
        LIST_HEAD(preempt_fences);
        int err = 0;
+       int io_idx;
        long wait;
        int __maybe_unused tries = 0;
 
@@ -516,6 +517,12 @@ static void preempt_rebind_work_func(struct work_struct *w)
        if (err)
                goto out_unlock_outer;
 
+       if (xe_device_io_get(vm->xe, &io_idx)) {
+               xe_validation_ctx_fini(&ctx);
+               err = 0;
+               goto out_unlock_outer;
+       }
+
        drm_exec_until_all_locked(&exec) {
                bool done = false;
 
@@ -523,8 +530,7 @@ static void preempt_rebind_work_func(struct work_struct *w)
                drm_exec_retry_on_contention(&exec);
                xe_validation_retry_on_oom(&ctx, &err);
                if (err || done) {
-                       xe_validation_ctx_fini(&ctx);
-                       goto out_unlock_outer;
+                       goto out_unlock;
                }
        }
 
@@ -572,6 +578,7 @@ static void preempt_rebind_work_func(struct work_struct *w)
 
 out_unlock:
        xe_validation_ctx_fini(&ctx);
+       xe_device_io_put(io_idx);
 out_unlock_outer:
        if (err == -EAGAIN) {
                trace_xe_vm_rebind_worker_retry(vm);
-- 
2.43.0

Reply via email to