On Mon, Oct 26, 2026 at 05:50:45PM +0530, Arvind Yadav wrote:
> Fault service holds vm->lock for read while using a VMA. SVM garbage
> collection also runs under the read lock and can reset AUTORESET
> attributes. The reset can split or merge VMAs and immediately free a
> VMA that another fault worker is still using. The GC mutex serializes

I'll have to think about this one for a bit but quick thought:

Can we use some ref counting (e.g., add a VMA ref count) rather than
changing the locking?

Matt

> collectors but does not protect VMA readers.
> 
> Keep backing collection under read access when no attribute change is
> needed. Defer ranges requiring a reset and return -EAGAIN so the caller
> can release its read-side state and collect with write access.
> 
> In fault service, discard the old VMA pointer before releasing the read
> lock. After acquiring write access, recheck VM state and collect the
> queued ranges. Downgrade to read and repeat VMA lookup, permission checks
> and advice-dependent setup.
> 
> Drain GC under the existing write lock before madvise, bind and attribute
> query. Require write access for VMA changes while retaining parallel
> fault service when no reset is needed.
> 
> [ 2163.042338] 
> ==================================================================
> [ 2163.042353] BUG: KASAN: slab-use-after-free in 
> __xe_svm_handle_pagefault+0x297/0x1460 [xe]
> [ 2163.042815] Read of size 8 at addr ffff88813f879b70 by task 
> kworker/u65:3/3115
> 
> [ 2163.042834] CPU: 0 UID: 0 PID: 3115 Comm: kworker/u65:3 Kdump: loaded 
> Tainted: G S      W  O        7.3.0-lgci-xe-svm-22oct #16 PREEMPT(full)
> [ 2163.042841] Tainted: [S]=CPU_OUT_OF_SPEC, [W]=WARN, [O]=OOT_MODULE
> [ 2163.042842] Hardware name: ASUS System Product Name/PRIME Z790-P WIFI, 
> BIOS 1002 03/31/2023
> [ 2163.042845] Workqueue: xe_page_fault_work_queue xe_pagefault_queue_work 
> [xe]
> [ 2163.043326] Call Trace:
> [ 2163.043327]  <TASK>
> [ 2163.043330]  dump_stack_lvl+0x91/0xf0
> [ 2163.043336]  print_report+0xd1/0x610
> [ 2163.043340]  ? __xe_svm_handle_pagefault+0x297/0x1460 [xe]
> [ 2163.043825]  ? __virt_addr_valid+0x1b2/0x350
> [ 2163.043831]  ? kasan_complete_mode_report_info+0x6a/0x210
> [ 2163.043836]  ? __xe_svm_handle_pagefault+0x297/0x1460 [xe]
> [ 2163.044340]  kasan_report+0xa3/0xe0
> [ 2163.044344]  ? __xe_svm_handle_pagefault+0x297/0x1460 [xe]
> [ 2163.044881]  __asan_load8+0x8b/0xe0
> [ 2163.044885]  __xe_svm_handle_pagefault+0x297/0x1460 [xe]
> [ 2163.045456]  ? lock_acquire+0x1b0/0x320
> [ 2163.045461]  ? xe_vm_find_overlapping_vma+0x88/0x310 [xe]
> [ 2163.046072]  ? __pfx___xe_svm_handle_pagefault+0x10/0x10 [xe]
> [ 2163.046679]  ? rcu_is_watching+0x21/0x60
> [ 2163.046684]  ? __mutex_lock+0x1172/0x18b0
> [ 2163.046690]  ? find_held_lock+0x31/0x90
> [ 2163.046696]  ? xe_vm_find_overlapping_vma+0xa1/0x310 [xe]
> [ 2163.047273]  ? __kasan_check_write+0x14/0x30
> [ 2163.047278]  ? __mutex_unlock_slowpath+0x10f/0x660
> [ 2163.047285]  ? __pfx___mutex_unlock_slowpath+0x10/0x10
> [ 2163.047295]  ? mutex_unlock+0x12/0x20
> [ 2163.047299]  ? xe_vm_find_overlapping_vma+0xa1/0x310 [xe]
> [ 2163.047844]  xe_svm_handle_pagefault+0x5c/0x140 [xe]
> [ 2163.048355]  xe_pagefault_queue_work+0x19ad/0x2450 [xe]
> [ 2163.048843]  process_one_work+0x5ae/0xdd0
> [ 2163.048847]  ? __lock_acquire+0x58a/0x2820
> [ 2163.048850]  ? __schedule+0x22a6/0x3670
> [ 2163.048858]  ? __pfx_process_one_work+0x10/0x10
> [ 2163.048866]  ? __pfx_xe_pagefault_queue_work+0x10/0x10 [xe]
> [ 2163.049345]  ? assign_work+0xe4/0x1d0
> [ 2163.049350]  worker_thread+0x3b4/0x6a0
> [ 2163.049357]  ? __pfx_worker_thread+0x10/0x10
> [ 2163.049361]  kthread+0x204/0x250
> [ 2163.049364]  ? kthread+0xf1/0x250
> [ 2163.049369]  ? __pfx_kthread+0x10/0x10
> [ 2163.049372]  ret_from_fork+0x465/0x6a0
> [ 2163.049377]  ? __pfx_ret_from_fork+0x10/0x10
> [ 2163.049381]  ? __switch_to+0x26f/0x740
> [ 2163.049386]  ? __pfx_kthread+0x10/0x10
> [ 2163.049390]  ret_from_fork_asm+0x1a/0x30
> [ 2163.049399]  </TASK>
> 
> [ 2163.049654] Allocated by task 522570:
> [ 2163.049662]  kasan_save_stack+0x26/0x60
> [ 2163.049668]  kasan_save_track+0x14/0x40
> [ 2163.049672]  kasan_save_alloc_info+0x37/0x60
> [ 2163.049677]  __kasan_kmalloc+0xc3/0xd0
> [ 2163.049681]  __kmalloc_cache_noprof+0x2b4/0x6c0
> [ 2163.049686]  xe_vma_create+0xe3/0xe60 [xe]
> [ 2163.050170]  new_vma+0x416/0x540 [xe]
> [ 2163.050654]  vm_bind_ioctl_ops_parse+0xc3e/0x1110 [xe]
> [ 2163.051139]  xe_vm_alloc_vma+0x243/0xd60 [xe]
> [ 2163.051623]  xe_vm_alloc_madvise_vma+0x92/0xd0 [xe]
> [ 2163.052107]  xe_vm_madvise_ioctl+0x783/0x14f0 [xe]
> [ 2163.052591]  drm_ioctl_kernel+0x143/0x200
> [ 2163.052597]  drm_ioctl+0x419/0x790
> [ 2163.052600]  xe_drm_ioctl+0x9e/0xf0 [xe]
> [ 2163.053074]  __x64_sys_ioctl+0xd5/0x130
> [ 2163.053079]  x64_sys_call+0x2b5/0x27e0
> [ 2163.053084]  do_syscall_64+0xef/0x6c0
> [ 2163.053088]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> 
> [ 2163.053098] Freed by task 17929:
> [ 2163.053105]  kasan_save_stack+0x26/0x60
> [ 2163.053110]  kasan_save_track+0x14/0x40
> [ 2163.053114]  kasan_save_free_info+0x3b/0x60
> [ 2163.053118]  __kasan_slab_free+0x6f/0xa0
> [ 2163.053123]  kfree+0x2bb/0x600
> [ 2163.053127]  xe_vma_free+0x5a/0x70 [xe]
> [ 2163.053610]  xe_vma_destroy_late+0xae/0xf0 [xe]
> [ 2163.054093]  xe_vma_destroy+0xda/0xf20 [xe]
> [ 2163.054576]  xe_vm_alloc_vma+0x304/0xd60 [xe]
> [ 2163.055059]  xe_vm_alloc_cpu_addr_mirror_vma+0x8f/0xd0 [xe]
> [ 2163.055543]  xe_svm_garbage_collector+0x388/0x5d0 [xe]
> [ 2163.056028]  xe_svm_garbage_collector_work_func+0x2a/0x40 [xe]
> [ 2163.056512]  process_one_work+0x5ae/0xdd0
> [ 2163.056517]  worker_thread+0x3b4/0x6a0
> [ 2163.056521]  kthread+0x204/0x250
> [ 2163.056525]  ret_from_fork+0x465/0x6a0
> [ 2163.056530]  ret_from_fork_asm+0x1a/0x30
> 
> Fixes: 941bcc1a4bbf ("drm/xe: Fine grained page fault locking")
> Cc: Matthew Brost <[email protected]>
> Cc: Thomas Hellström <[email protected]>
> Cc: Himal Prasad Ghimiray <[email protected]>
> Assisted-by: LLM
> Signed-off-by: Arvind Yadav <[email protected]>
> ---
>  drivers/gpu/drm/xe/xe_pagefault.c  |  37 +++++-
>  drivers/gpu/drm/xe/xe_svm.c        | 179 ++++++++++++++++++++++-------
>  drivers/gpu/drm/xe/xe_svm.h        |   8 ++
>  drivers/gpu/drm/xe/xe_vm.c         |  54 +++++----
>  drivers/gpu/drm/xe/xe_vm_madvise.c |   5 +
>  5 files changed, 212 insertions(+), 71 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c 
> b/drivers/gpu/drm/xe/xe_pagefault.c
> index 5247321c805e..9e1377df9d01 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
> @@ -270,8 +270,9 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>       struct xe_device *xe = gt_to_xe(gt);
>       struct xe_vm *vm;
>       struct xe_vma *vma = NULL;
> +     enum xe_pagefault_error error;
>       int err;
> -     bool atomic;
> +     bool atomic, svm_relookup = false;
>       u32 asid = FIELD_GET(XE_PAGEFAULT_ASID_MASK, pf->consumer.id);
>  
>       /* Producer flagged this fault to be nacked */
> @@ -297,10 +298,13 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>               goto unlock_vm;
>       }
>  
> +lookup_vma:
>       vma = xe_vm_find_vma_by_addr(vm, xe_pagefault_addr(pf));
>       if (!vma) {
>               err = -EINVAL;
> -             xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_NOT_FOUND);
> +             xe_pagefault_set_error(pf, svm_relookup ?
> +                                    XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND :
> +                                    XE_PAGEFAULT_ERROR_VMA_NOT_FOUND);
>               goto unlock_vm;
>       }
>  
> @@ -313,11 +317,36 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  
>       atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
>  
> -     if (xe_vma_is_cpu_addr_mirror(vma))
> +     if (xe_vma_is_cpu_addr_mirror(vma)) {
>               err = xe_svm_handle_pagefault(vm, vma, pf, gt,
>                                             xe_pagefault_addr(pf), atomic);
> -     else
> +             if (err == -EAGAIN) {
> +                     /* GC needs to change VMAs. Drop all read-side state. */
> +                     vma = NULL;
> +                     up_read(&vm->lock);
> +                     down_write(&vm->lock);
> +
> +                     if (xe_vm_is_closed(vm)) {
> +                             err = -ENOENT;
> +                             error = XE_PAGEFAULT_ERROR_VM_CLOSED;
> +                     } else {
> +                             err = xe_svm_garbage_collect(vm);
> +                             error = 
> XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR;
> +                     }
> +
> +                     downgrade_write(&vm->lock);
> +                     if (err) {
> +                             xe_pagefault_set_error(pf, error);
> +                             goto unlock_vm;
> +                     }
> +
> +                     xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_NONE);
> +                     svm_relookup = true;
> +                     goto lookup_vma;
> +             }
> +     } else {
>               err = xe_pagefault_handle_vma(gt, vma, pf, atomic);
> +     }
>  
>  unlock_vm:
>       if (err)
> diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
> index bc6902b5bf57..d19cb0c4b154 100644
> --- a/drivers/gpu/drm/xe/xe_svm.c
> +++ b/drivers/gpu/drm/xe/xe_svm.c
> @@ -356,12 +356,36 @@ static void xe_vma_set_default_attributes(struct xe_vma 
> *vma)
>       xe_vma_mem_attr_copy(&vma->attr, &default_attr);
>  }
>  
> +/* vm->lock keeps the VMA and its attributes stable during this check. */
> +static bool xe_svm_range_needs_attr_reset(struct xe_vm *vm, u64 start)
> +{
> +     struct xe_vma *vma;
> +     u64 end;
> +
> +     lockdep_assert_held(&vm->lock);
> +
> +     vma = xe_vm_find_vma_by_addr(vm, start);
> +     if (!vma || !(vma->gpuva.flags & XE_VMA_MADV_AUTORESET))
> +             return false;
> +
> +     if (!xe_vma_has_default_mem_attrs(vma))
> +             return true;
> +
> +     start = xe_vma_start(vma);
> +     end = xe_vma_end(vma);
> +     xe_vm_find_cpu_addr_mirror_vma_range(vm, &start, &end);
> +
> +     return start != xe_vma_start(vma) || end != xe_vma_end(vma);
> +}
> +
>  static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 start, u64 
> end)
>  {
>       struct xe_vma *vma;
>       bool has_default_attr;
>       int err;
>  
> +     lockdep_assert_held_write(&vm->lock);
> +
>       vma = xe_vm_find_vma_by_addr(vm, start);
>       if (!vma)
>               return -EINVAL;
> @@ -397,70 +421,134 @@ static int xe_svm_range_set_default_attr(struct xe_vm 
> *vm, u64 start, u64 end)
>               return err;
>       }
>  
> -     /*
> -      * On call from xe_svm_handle_pagefault original VMA might be changed
> -      * signal this to lookup for VMA again.
> -      */
> -     return -EAGAIN;
> +     return 0;
>  }
>  
> -static int xe_svm_garbage_collector(struct xe_vm *vm)
> +/*
> + * With reset_attrs false, defer ranges requiring a VMA change and return
> + * -EAGAIN after collecting the remaining ready ranges in the batch. The
> + * caller must release all read-side state before collecting them with
> + * vm->lock held for write.
> + */
> +static int xe_svm_garbage_collector(struct xe_vm *vm, bool reset_attrs)
>  {
>       struct xe_svm_range *range;
> -     u64 range_start;
> -     u64 range_end;
> -     int err, ret = 0;
> +     LIST_HEAD(pending);
> +     LIST_HEAD(deferred);
> +     u64 range_start, range_end;
> +     bool needs_reset;
> +     int err = 0, ret = 0;
>  
>       lockdep_assert_held(&vm->lock);
> +     if (reset_attrs)
> +             lockdep_assert_held_write(&vm->lock);
>  
>       if (xe_vm_is_closed_or_banned(vm))
>               return -ENOENT;
>  
>       guard(mutex)(&vm->svm.garbage_collector.lock);
>  
> +     /* Process a finite batch, excluding events queued during this pass. */
> +     spin_lock(&vm->svm.garbage_collector.list_lock);
> +     if (list_empty(&vm->svm.garbage_collector.range_list)) {
> +             spin_unlock(&vm->svm.garbage_collector.list_lock);
> +             return 0;
> +     }
> +     list_splice_init(&vm->svm.garbage_collector.range_list, &pending);
> +     spin_unlock(&vm->svm.garbage_collector.list_lock);
> +
>       for (;;) {
>               spin_lock(&vm->svm.garbage_collector.list_lock);
> -             range = 
> list_first_entry_or_null(&vm->svm.garbage_collector.range_list,
> -                                              typeof(*range),
> +             range = list_first_entry_or_null(&pending, typeof(*range),
>                                                garbage_collector_link);
> -             if (!range)
> +             if (!range) {
> +                     spin_unlock(&vm->svm.garbage_collector.list_lock);
>                       break;
> +             }
>  
>               range_start = xe_svm_range_start(range);
>               range_end = xe_svm_range_end(range);
> -
>               list_del(&range->garbage_collector_link);
>               spin_unlock(&vm->svm.garbage_collector.list_lock);
>  
> +             /*
> +              * Keep the backing range alive until its attributes can be
> +              * reset. The GC mutex excludes other collectors and explicit
> +              * range removal requires vm->lock for write.
> +              */
> +             needs_reset = xe_svm_range_needs_attr_reset(vm, range_start);
> +             if (needs_reset && !reset_attrs) {
> +                     spin_lock(&vm->svm.garbage_collector.list_lock);
> +                     list_add_tail(&range->garbage_collector_link, 
> &deferred);
> +                     spin_unlock(&vm->svm.garbage_collector.list_lock);
> +                     ret = -EAGAIN;
> +                     continue;
> +             }
> +
>               err = __xe_svm_garbage_collector(vm, range);
>               if (err) {
>                       drm_warn(&vm->xe->drm,
>                                "Garbage collection failed: %pe\n",
>                                ERR_PTR(err));
> +                     /* Leave the failed range in the tree for VM teardown. 
> */
>                       xe_vm_kill(vm, true);
> -                     return err;
> +                     goto out;
>               }
>  
> -             err = xe_svm_range_set_default_attr(vm, range_start, range_end);
> -             if (err) {
> -                     if (err == -EAGAIN)
> -                             ret = -EAGAIN;
> -                     else
> -                             return err;
> +             if (needs_reset) {
> +                     err = xe_svm_range_set_default_attr(vm, range_start, 
> range_end);
> +                     if (err)
> +                             goto out;
>               }
>       }
> +
> +out:
> +     /* Restore ranges not yet processed, including deferred resets. */
> +     spin_lock(&vm->svm.garbage_collector.list_lock);
> +     list_splice_tail_init(&pending, &vm->svm.garbage_collector.range_list);
> +     list_splice_tail_init(&deferred, &vm->svm.garbage_collector.range_list);
>       spin_unlock(&vm->svm.garbage_collector.list_lock);
>  
> -     return ret;
> +     return err ?: ret;
> +}
> +
> +/**
> + * xe_svm_garbage_collect() - Collect SVM ranges and reset their attributes
> + * @vm: The VM, with vm->lock held for write.
> + *
> + * Process one batch of queued ranges, including attribute resets. Ranges
> + * queued during this pass are left for a later collection.
> + *
> + * Call before changing advice or bindings, or querying attributes, so the
> + * batch is reset before applying new advice or reporting attributes.
> + * No VMA pointer may be retained across this call.
> + *
> + * Return: 0 on success, negative error code on error.
> + */
> +int xe_svm_garbage_collect(struct xe_vm *vm)
> +{
> +     lockdep_assert_held_write(&vm->lock);
> +
> +     if (!xe_vm_in_fault_mode(vm))
> +             return 0;
> +
> +     return xe_svm_garbage_collector(vm, true);
>  }
>  
>  static void xe_svm_garbage_collector_work_func(struct work_struct *w)
>  {
>       struct xe_vm *vm = container_of(w, struct xe_vm,
>                                       svm.garbage_collector.work);
> +     int err = 0;
>  
> -     guard(rwsem_read)(&vm->lock);
> -     xe_svm_garbage_collector(vm);
> +     scoped_guard(rwsem_read, &vm->lock)
> +             err = xe_svm_garbage_collector(vm, false);
> +
> +     if (err != -EAGAIN)
> +             return;
> +
> +     guard(rwsem_write)(&vm->lock);
> +     xe_svm_garbage_collect(vm);
>  }
>  
>  #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> @@ -1302,13 +1390,15 @@ static int __xe_svm_handle_pagefault(struct xe_vm 
> *vm, struct xe_vma *vma,
>       if (range) {
>               mutex_unlock(&range->lock);
>               drm_gpusvm_range_put(&range->base);
> -     }
>  
> -     /* Always process UNMAPs first so view SVM ranges is current */
> -     err = xe_svm_garbage_collector(vm);
> -     if (err) {
> -             xe_pagefault_set_error(pf, 
> XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR);
> -             return err;
> +             /* The caller collected before the initial service attempt. */
> +             err = xe_svm_garbage_collector(vm, false);
> +             if (err) {
> +                     if (err != -EAGAIN)
> +                             xe_pagefault_set_error(pf,
> +                                                   
> XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR);
> +                     return err;
> +             }
>       }
>  
>       dpagemap = ctx.devmem_only ? xe_tile_local_pagemap(tile) :
> @@ -1468,14 +1558,28 @@ static int __xe_svm_handle_pagefault(struct xe_vm 
> *vm, struct xe_vma *vma,
>   * Create GPU bindings for a SVM page fault. Optionally migrate to device
>   * memory.
>   *
> - * Return: 0 on success, negative error code on error.
> + * The caller must hold vm->lock for read. On -EAGAIN, discard @vma before
> + * releasing that lock. Collect with write access, then repeat VMA lookup
> + * and permission checks before retrying.
> + *
> + * Return: 0 on success, -EAGAIN if the caller must collect with write
> + * access and repeat VMA lookup, or another negative error code on error.
>   */
>  int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
>                           struct xe_pagefault *pf, struct xe_gt *gt,
>                           u64 fault_addr, bool atomic)
>  {
>       int need_vram, ret;
> -retry:
> +
> +     /* Consume older UNMAPs before reading advice or building the context. 
> */
> +     ret = xe_svm_garbage_collector(vm, false);
> +     if (ret) {
> +             if (ret != -EAGAIN)
> +                     xe_pagefault_set_error(pf,
> +                                            
> XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR);
> +             return ret;
> +     }
> +
>       need_vram = xe_vma_need_vram_for_atomic(vm->xe, vma, atomic);
>       if (need_vram < 0) {
>               xe_pagefault_set_error(pf, 
> XE_PAGEFAULT_ERROR_SVM_NEEDS_VRAM_CHECK);
> @@ -1485,18 +1589,9 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct 
> xe_vma *vma,
>       ret =  __xe_svm_handle_pagefault(vm, vma, pf, gt, fault_addr,
>                                        need_vram ? true : false);
>       if (ret == -EAGAIN) {
> -             /*
> -              * Retry once on -EAGAIN to re-lookup the VMA, as the original 
> VMA
> -              * may have been split by xe_svm_range_set_default_attr.
> -              */
> -             vma = xe_vm_find_vma_by_addr(vm, fault_addr);
> -             if (!vma) {
> -                     xe_pagefault_set_error(pf, 
> XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND);
> -                     return -EINVAL;
> -             }
> -
> +             /* Retry from the caller after collecting with write access. */
>               xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_NONE);
> -             goto retry;
> +             return ret;
>       }
>       if (ret && xe_pagefault_get_error(pf) == XE_PAGEFAULT_ERROR_NONE)
>               xe_pagefault_set_error(pf, 
> XE_PAGEFAULT_ERROR_SVM_SERVICE_FAILED);
> diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
> index ffc1cc808102..523213a8cd26 100644
> --- a/drivers/gpu/drm/xe/xe_svm.h
> +++ b/drivers/gpu/drm/xe/xe_svm.h
> @@ -109,6 +109,8 @@ void xe_svm_fini(struct xe_vm *vm);
>  
>  void xe_svm_close(struct xe_vm *vm);
>  
> +int xe_svm_garbage_collect(struct xe_vm *vm);
> +
>  int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
>                           struct xe_pagefault *pf, struct xe_gt *gt,
>                           u64 fault_addr, bool atomic);
> @@ -316,6 +318,12 @@ void xe_svm_close(struct xe_vm *vm)
>  {
>  }
>  
> +static inline
> +int xe_svm_garbage_collect(struct xe_vm *vm)
> +{
> +     return 0;
> +}
> +
>  static inline
>  int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
>                           struct xe_pagefault *pf, struct xe_gt *gt,
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index d12365b11186..6afe6f1be1d4 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -1308,19 +1308,12 @@ static void vma_destroy_cb(struct dma_fence *fence,
>       xe_destroy_wq_queue(&vma->destroy_work);
>  }
>  
> -static void xe_vm_assert_write_mode_or_garbage_collector(struct xe_vm *vm)
> -{
> -     lockdep_assert(lockdep_is_held_type(&vm->lock, 0) ||
> -                    (lockdep_is_held_type(&vm->lock, 1) &&
> -                     lockdep_is_held_type(&vm->svm.garbage_collector.lock, 
> 0)));
> -}
> -
>  static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence)
>  {
>       struct xe_vm *vm = xe_vma_vm(vma);
>       struct xe_bo *bo = xe_vma_bo(vma);
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>       xe_assert(vm->xe, list_empty(&vma->combined_links.destroy));
>  
>       if (xe_vma_is_userptr(vma)) {
> @@ -1416,7 +1409,7 @@ static int xe_vm_insert_vma(struct xe_vm *vm, struct 
> xe_vma *vma)
>       int err;
>  
>       xe_assert(vm->xe, xe_vma_vm(vma) == vm);
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       mutex_lock(&vm->snap_mutex);
>       err = drm_gpuva_insert(&vm->gpuvm, &vma->gpuva);
> @@ -1429,7 +1422,7 @@ static int xe_vm_insert_vma(struct xe_vm *vm, struct 
> xe_vma *vma)
>  static void xe_vm_remove_vma(struct xe_vm *vm, struct xe_vma *vma)
>  {
>       xe_assert(vm->xe, xe_vma_vm(vma) == vm);
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       mutex_lock(&vm->snap_mutex);
>       drm_gpuva_remove(&vma->gpuva);
> @@ -2267,7 +2260,7 @@ static int xe_vm_query_vmas(struct xe_vm *vm, u64 
> start, u64 end)
>       struct drm_gpuva *gpuva;
>       u32 num_vmas = 0;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held(&vm->lock);
>       drm_gpuvm_for_each_va_range(gpuva, &vm->gpuvm, start, end)
>               num_vmas++;
>  
> @@ -2280,7 +2273,7 @@ static int get_mem_attrs(struct xe_vm *vm, u32 
> *num_vmas, u64 start,
>       struct drm_gpuva *gpuva;
>       int i = 0;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held(&vm->lock);
>  
>       drm_gpuvm_for_each_va_range(gpuva, &vm->gpuvm, start, end) {
>               struct xe_vma *vma = gpuva_to_vma(gpuva);
> @@ -2330,6 +2323,12 @@ int xe_vm_query_vmas_attrs_ioctl(struct drm_device 
> *dev, void *data, struct drm_
>       if (err)
>               goto put_vm;
>  
> +     if (!xe_vm_is_closed_or_banned(vm)) {
> +             err = xe_svm_garbage_collect(vm);
> +             if (err)
> +                     goto unlock_vm;
> +     }
> +
>       attrs_user = u64_to_user_ptr(args->vector_of_mem_attr);
>  
>       if (args->num_mem_ranges == 0 && !attrs_user) {
> @@ -2487,7 +2486,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct 
> xe_vma_ops *vops,
>       u64 range_end = addr + range;
>       int err;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       vm_dbg(&vm->xe->drm,
>              "op=%d, addr=0x%016llx, range=0x%016llx, 
> bo_offset_or_userptr=0x%016llx",
> @@ -2694,7 +2693,7 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct 
> drm_gpuva_op_map *op,
>       struct xe_vma *vma;
>       int err = 0;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       if (bo) {
>               err = 0;
> @@ -2795,7 +2794,7 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct 
> xe_vma_op *op)
>  
>       switch (op->base.op) {
>       case DRM_GPUVA_OP_MAP:
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               err |= xe_vm_insert_vma(vm, op->map.vma);
>               if (!err)
> @@ -2806,7 +2805,7 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct 
> xe_vma_op *op)
>               u8 tile_present =
>                       gpuva_to_vma(op->base.remap.unmap->va)->tile_present;
>  
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               prep_vma_destroy(vm, gpuva_to_vma(op->base.remap.unmap->va),
>                                true);
> @@ -2842,7 +2841,7 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct 
> xe_vma_op *op)
>               break;
>       }
>       case DRM_GPUVA_OP_UNMAP:
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               prep_vma_destroy(vm, gpuva_to_vma(op->base.unmap.va), true);
>               op->flags |= XE_VMA_OP_COMMITTED;
> @@ -2889,7 +2888,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, 
> struct drm_gpuva_ops *ops,
>       u8 id, tile_mask = 0;
>       int err = 0;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       for_each_tile(tile, vm->xe, id)
>               tile_mask |= 0x1 << id;
> @@ -3072,7 +3071,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct 
> xe_vma_op *op,
>  
>       switch (op->base.op) {
>       case DRM_GPUVA_OP_MAP:
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               if (op->map.vma) {
>                       prep_vma_destroy(vm, op->map.vma, post_commit);
> @@ -3083,7 +3082,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct 
> xe_vma_op *op,
>       {
>               struct xe_vma *vma = gpuva_to_vma(op->base.unmap.va);
>  
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               if (vma) {
>                       xe_svm_notifier_lock(vm);
> @@ -3098,7 +3097,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct 
> xe_vma_op *op,
>       {
>               struct xe_vma *vma = gpuva_to_vma(op->base.remap.unmap->va);
>  
> -             xe_vm_assert_write_mode_or_garbage_collector(vm);
> +             lockdep_assert_held_write(&vm->lock);
>  
>               if (op->remap.prev) {
>                       prep_vma_destroy(vm, op->remap.prev, prev_post_commit);
> @@ -4092,6 +4091,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void 
> *data, struct drm_file *file)
>               goto release_vm_lock;
>       }
>  
> +     /* Consume queued resets before replacing VMAs. */
> +     err = xe_svm_garbage_collect(vm);
> +     if (err)
> +             goto release_vm_lock;
> +
>       for (i = 0; i < args->num_binds; ++i) {
>               u64 range = bind_ops[i].range;
>               u64 addr = bind_ops[i].addr;
> @@ -4907,7 +4911,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
>       u16 default_pat;
>       int err;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       if (is_madvise)
>               ops = drm_gpuvm_madvise_ops_create(&vm->gpuvm, map_req);
> @@ -5041,7 +5045,7 @@ int xe_vm_alloc_madvise_vma(struct xe_vm *vm, uint64_t 
> start, uint64_t range)
>               .map.va.range = range,
>       };
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       vm_dbg(&vm->xe->drm, "MADVISE_OPS_CREATE: addr=0x%016llx, 
> size=0x%016llx", start, range);
>  
> @@ -5073,7 +5077,7 @@ void xe_vm_find_cpu_addr_mirror_vma_range(struct xe_vm 
> *vm, u64 *start, u64 *end
>  {
>       struct xe_vma *prev, *next;
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held(&vm->lock);
>  
>       if (*start >= SZ_4K) {
>               prev = xe_vm_find_vma_by_addr(vm, *start - SZ_4K);
> @@ -5105,7 +5109,7 @@ int xe_vm_alloc_cpu_addr_mirror_vma(struct xe_vm *vm, 
> uint64_t start, uint64_t r
>               .map.va.range = range,
>       };
>  
> -     xe_vm_assert_write_mode_or_garbage_collector(vm);
> +     lockdep_assert_held_write(&vm->lock);
>  
>       vm_dbg(&vm->xe->drm, "CPU_ADDR_MIRROR_VMA_OPS_CREATE: addr=0x%016llx, 
> size=0x%016llx",
>              start, range);
> diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c 
> b/drivers/gpu/drm/xe/xe_vm_madvise.c
> index 5c7aefc641ee..1af5e79bbfd3 100644
> --- a/drivers/gpu/drm/xe/xe_vm_madvise.c
> +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c
> @@ -638,6 +638,11 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void 
> *data, struct drm_file *fil
>               goto unlock_vm;
>       }
>  
> +     /* Consume queued resets before publishing new advice. */
> +     err = xe_svm_garbage_collect(vm);
> +     if (err)
> +             goto unlock_vm;
> +
>       err = xe_madvise_details_init(vm, args, &details);
>       if (err)
>               goto unlock_vm;
> -- 
> 2.43.0
> 

Reply via email to