Page-fault work may still be queued when PCI error recovery starts or
the device becomes permanently wedged. Servicing these faults can migrate
memory or update page tables after device I/O has been blocked.

Check the device state before and after fault servicing to cover a reset
racing with the worker. Drop the active fault and its chained faults
without sending a hardware response, and invalidate the cached fault
state.

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]>
---
 drivers/gpu/drm/xe/xe_pagefault.c | 40 +++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_pagefault.c 
b/drivers/gpu/drm/xe/xe_pagefault.c
index 2e415995f067..f486165f2323 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -592,6 +592,38 @@ static void xe_pagefault_save_to_vm(struct xe_device *xe, 
struct xe_pagefault *p
        xe_vm_put(vm);
 }
 
+static bool
+xe_pagefault_drop_if_blocked(struct xe_pagefault_queue *pf_queue,
+                            struct xe_pagefault_work *pf_work,
+                            struct xe_pagefault *pf,
+                            u64 *cache_start)
+{
+       struct xe_pagefault *next;
+
+       if (!xe_device_io_blocked(pf_work->xe))
+               return false;
+
+       /*
+        * cache_start is private to this worker invocation. pf_work->cache is
+        * shared with fault producers and must be invalidated under the queue
+        * lock.
+        */
+       xe_pagefault_cache_start_invalidate(*cache_start);
+
+       guard(spinlock_irq)(&pf_queue->lock);
+
+       xe_pagefault_cache_invalidate(pf_queue, pf_work);
+
+       while (pf) {
+               next = pf->consumer.next;
+               pf->consumer.next = NULL;
+               pf->consumer.alloc_state = XE_PAGEFAULT_ALLOC_STATE_FREE;
+               pf = next;
+       }
+
+       return true;
+}
+
 static void xe_pagefault_queue_work(struct work_struct *w)
 {
        struct xe_pagefault_work *pf_work =
@@ -615,6 +647,10 @@ static void xe_pagefault_queue_work(struct work_struct *w)
                int err = 0;
                bool invalidated = false;
 
+               if (xe_pagefault_drop_if_blocked(pf_queue, pf_work, pf,
+                                                &cache_start))
+                       continue;
+
                /* Last fault same address, ack immediately */
                if (xe_pagefault_match(pf, cache_start, cache_end, cache_asid)) 
{
                        xe_gt_stats_incr(gt, 
XE_GT_STATS_ID_LAST_PAGEFAULT_COUNT, 1);
@@ -623,6 +659,10 @@ static void xe_pagefault_queue_work(struct work_struct *w)
 
                err = xe_pagefault_service(pf);
 
+               if (xe_pagefault_drop_if_blocked(pf_queue, pf_work, pf,
+                                                &cache_start))
+                       continue;
+
                if (err) {
                        if (!(pf->consumer.access_type & 
XE_PAGEFAULT_ACCESS_PREFETCH)) {
                                xe_pagefault_save_to_vm(gt_to_xe(gt), pf);
-- 
2.43.0

Reply via email to