[PATCH v4 6/8] drm/xe/vm: split userptr bits into separate file

Matthew Brost matthew.brost at intel.com
Mon Jun 9 17:05:15 UTC 2025


On Mon, May 12, 2025 at 04:06:44PM +0100, Matthew Auld wrote:
> This will simplify compiling out the bits that depend on DRM_GPUSVM in a
> later patch. Without this we end up littering the code with ifdef
> checks, plus it becomes hard to be sure that something won't blow at
> runtime due to something not being initialised, even though it passed
> the build. Should be no functional change here.
> 
> Signed-off-by: Matthew Auld <matthew.auld at intel.com>
> Cc: Thomas Hellström <thomas.hellstrom at linux.intel.com>
> Cc: Matthew Brost <matthew.brost at intel.com>
> ---
>  drivers/gpu/drm/xe/Makefile      |   1 +
>  drivers/gpu/drm/xe/xe_pt.c       |   1 +
>  drivers/gpu/drm/xe/xe_userptr.c  | 303 +++++++++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_userptr.h  |  97 ++++++++++

We typically use *_types.h but I didn't do this for xe_svm.h either so
maybe a little unfair to nit pick.

Either way:
Reviewed-by: Matthew Brost <matthew.brost at intel.com>

>  drivers/gpu/drm/xe/xe_vm.c       | 280 +---------------------------
>  drivers/gpu/drm/xe/xe_vm.h       |  18 --
>  drivers/gpu/drm/xe/xe_vm_types.h |  60 +-----
>  7 files changed, 410 insertions(+), 350 deletions(-)
>  create mode 100644 drivers/gpu/drm/xe/xe_userptr.c
>  create mode 100644 drivers/gpu/drm/xe/xe_userptr.h
> 
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index e4bf484d4121..10b42118e761 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -116,6 +116,7 @@ xe-y += xe_bb.o \
>  	xe_tuning.o \
>  	xe_uc.o \
>  	xe_uc_fw.o \
> +	xe_userptr.o \
>  	xe_vm.o \
>  	xe_vram.o \
>  	xe_vram_freq.o \
> diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
> index 5cccfd9cc3e9..720c25bf48f2 100644
> --- a/drivers/gpu/drm/xe/xe_pt.c
> +++ b/drivers/gpu/drm/xe/xe_pt.c
> @@ -23,6 +23,7 @@
>  #include "xe_svm.h"
>  #include "xe_trace.h"
>  #include "xe_ttm_stolen_mgr.h"
> +#include "xe_userptr.h"
>  #include "xe_vm.h"
>  
>  struct xe_pt_dir {
> diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
> new file mode 100644
> index 000000000000..f573842a3d4b
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_userptr.c
> @@ -0,0 +1,303 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#include "xe_userptr.h"
> +
> +#include <linux/mm.h>
> +
> +#include "xe_hmm.h"
> +#include "xe_trace_bo.h"
> +
> +/**
> + * xe_vma_userptr_check_repin() - Advisory check for repin needed
> + * @uvma: The userptr vma
> + *
> + * Check if the userptr vma has been invalidated since last successful
> + * repin. The check is advisory only and can the function can be called
> + * without the vm->svm.gpusvm.notifier_lock held. There is no guarantee that the
> + * vma userptr will remain valid after a lockless check, so typically
> + * the call needs to be followed by a proper check under the notifier_lock.
> + *
> + * Return: 0 if userptr vma is valid, -EAGAIN otherwise; repin recommended.
> + */
> +int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma)
> +{
> +	return mmu_interval_check_retry(&uvma->userptr.notifier,
> +					uvma->userptr.notifier_seq) ?
> +		-EAGAIN : 0;
> +}
> +
> +int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma)
> +{
> +	struct xe_vma *vma = &uvma->vma;
> +	struct xe_vm *vm = xe_vma_vm(vma);
> +	struct xe_device *xe = vm->xe;
> +
> +	lockdep_assert_held(&vm->lock);
> +	xe_assert(xe, xe_vma_is_userptr(vma));
> +
> +	return xe_hmm_userptr_populate_range(uvma, false);
> +}
> +
> +static void __vma_userptr_invalidate(struct xe_vm *vm, struct xe_userptr_vma *uvma)
> +{
> +	struct xe_userptr *userptr = &uvma->userptr;
> +	struct xe_vma *vma = &uvma->vma;
> +	struct dma_resv_iter cursor;
> +	struct dma_fence *fence;
> +	long err;
> +
> +	/*
> +	 * Tell exec and rebind worker they need to repin and rebind this
> +	 * userptr.
> +	 */
> +	if (!xe_vm_in_fault_mode(vm) &&
> +	    !(vma->gpuva.flags & XE_VMA_DESTROYED)) {
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_move_tail(&userptr->invalidate_link,
> +			       &vm->userptr.invalidated);
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +	}
> +
> +	/*
> +	 * Preempt fences turn into schedule disables, pipeline these.
> +	 * Note that even in fault mode, we need to wait for binds and
> +	 * unbinds to complete, and those are attached as BOOKMARK fences
> +	 * to the vm.
> +	 */
> +	dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
> +			    DMA_RESV_USAGE_BOOKKEEP);
> +	dma_resv_for_each_fence_unlocked(&cursor, fence)
> +		dma_fence_enable_sw_signaling(fence);
> +	dma_resv_iter_end(&cursor);
> +
> +	err = dma_resv_wait_timeout(xe_vm_resv(vm),
> +				    DMA_RESV_USAGE_BOOKKEEP,
> +				    false, MAX_SCHEDULE_TIMEOUT);
> +	XE_WARN_ON(err <= 0);
> +
> +	if (xe_vm_in_fault_mode(vm) && userptr->initial_bind) {
> +		err = xe_vm_invalidate_vma(vma);
> +		XE_WARN_ON(err);
> +	}
> +
> +	xe_hmm_userptr_unmap(uvma);
> +}
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> +/**
> + * xe_vma_userptr_force_invalidate() - force invalidate a userptr
> + * @uvma: The userptr vma to invalidate
> + *
> + * Perform a forced userptr invalidation for testing purposes.
> + */
> +void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
> +{
> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +
> +	/* Protect against concurrent userptr pinning */
> +	lockdep_assert_held(&vm->lock);
> +	/* Protect against concurrent notifiers */
> +	lockdep_assert_held(&vm->svm.gpusvm.notifier_lock);
> +	/*
> +	 * Protect against concurrent instances of this function and
> +	 * the critical exec sections
> +	 */
> +	xe_vm_assert_held(vm);
> +
> +	if (!mmu_interval_read_retry(&uvma->userptr.notifier,
> +				     uvma->userptr.notifier_seq))
> +		uvma->userptr.notifier_seq -= 2;
> +	__vma_userptr_invalidate(vm, uvma);
> +}
> +#endif
> +
> +static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni,
> +				   const struct mmu_notifier_range *range,
> +				   unsigned long cur_seq)
> +{
> +	struct xe_userptr_vma *uvma = container_of(mni, typeof(*uvma), userptr.notifier);
> +	struct xe_vma *vma = &uvma->vma;
> +	struct xe_vm *vm = xe_vma_vm(vma);
> +
> +	xe_assert(vm->xe, xe_vma_is_userptr(vma));
> +	trace_xe_vma_userptr_invalidate(vma);
> +
> +	if (!mmu_notifier_range_blockable(range))
> +		return false;
> +
> +	vm_dbg(&xe_vma_vm(vma)->xe->drm,
> +	       "NOTIFIER: addr=0x%016llx, range=0x%016llx",
> +		xe_vma_start(vma), xe_vma_size(vma));
> +
> +	down_write(&vm->svm.gpusvm.notifier_lock);
> +	mmu_interval_set_seq(mni, cur_seq);
> +
> +	__vma_userptr_invalidate(vm, uvma);
> +	up_write(&vm->svm.gpusvm.notifier_lock);
> +	trace_xe_vma_userptr_invalidate_complete(vma);
> +
> +	return true;
> +}
> +
> +static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = {
> +	.invalidate = vma_userptr_invalidate,
> +};
> +
> +/**
> + * __xe_vm_userptr_needs_repin() - Check whether the VM does have userptrs
> + * that need repinning.
> + * @vm: The VM.
> + *
> + * This function checks for whether the VM has userptrs that need repinning,
> + * and provides a release-type barrier on the svm.gpusvm.notifier_lock after
> + * checking.
> + *
> + * Return: 0 if there are no userptrs needing repinning, -EAGAIN if there are.
> + */
> +int __xe_vm_userptr_needs_repin(struct xe_vm *vm)
> +{
> +	lockdep_assert_held_read(&vm->svm.gpusvm.notifier_lock);
> +
> +	return (list_empty(&vm->userptr.repin_list) &&
> +		list_empty(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
> +}
> +
> +int xe_vm_userptr_pin(struct xe_vm *vm)
> +{
> +	struct xe_userptr_vma *uvma, *next;
> +	int err = 0;
> +
> +	xe_assert(vm->xe, !xe_vm_in_fault_mode(vm));
> +	lockdep_assert_held_write(&vm->lock);
> +
> +	/* Collect invalidated userptrs */
> +	spin_lock(&vm->userptr.invalidated_lock);
> +	xe_assert(vm->xe, list_empty(&vm->userptr.repin_list));
> +	list_for_each_entry_safe(uvma, next, &vm->userptr.invalidated,
> +				 userptr.invalidate_link) {
> +		list_del_init(&uvma->userptr.invalidate_link);
> +		list_add_tail(&uvma->userptr.repin_link,
> +			      &vm->userptr.repin_list);
> +	}
> +	spin_unlock(&vm->userptr.invalidated_lock);
> +
> +	/* Pin and move to bind list */
> +	list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list,
> +				 userptr.repin_link) {
> +		err = xe_vma_userptr_pin_pages(uvma);
> +		if (err == -EFAULT) {
> +			list_del_init(&uvma->userptr.repin_link);
> +			/*
> +			 * We might have already done the pin once already, but
> +			 * then had to retry before the re-bind happened, due
> +			 * some other condition in the caller, but in the
> +			 * meantime the userptr got dinged by the notifier such
> +			 * that we need to revalidate here, but this time we hit
> +			 * the EFAULT. In such a case make sure we remove
> +			 * ourselves from the rebind list to avoid going down in
> +			 * flames.
> +			 */
> +			if (!list_empty(&uvma->vma.combined_links.rebind))
> +				list_del_init(&uvma->vma.combined_links.rebind);
> +
> +			/* Wait for pending binds */
> +			xe_vm_lock(vm, false);
> +			dma_resv_wait_timeout(xe_vm_resv(vm),
> +					      DMA_RESV_USAGE_BOOKKEEP,
> +					      false, MAX_SCHEDULE_TIMEOUT);
> +
> +			err = xe_vm_invalidate_vma(&uvma->vma);
> +			xe_vm_unlock(vm);
> +			if (err)
> +				break;
> +		} else {
> +			if (err)
> +				break;
> +
> +			list_del_init(&uvma->userptr.repin_link);
> +			list_move_tail(&uvma->vma.combined_links.rebind,
> +				       &vm->rebind_list);
> +		}
> +	}
> +
> +	if (err) {
> +		down_write(&vm->svm.gpusvm.notifier_lock);
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list,
> +					 userptr.repin_link) {
> +			list_del_init(&uvma->userptr.repin_link);
> +			list_move_tail(&uvma->userptr.invalidate_link,
> +				       &vm->userptr.invalidated);
> +		}
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +		up_write(&vm->svm.gpusvm.notifier_lock);
> +	}
> +	return err;
> +}
> +
> +/**
> + * xe_vm_userptr_check_repin() - Check whether the VM might have userptrs
> + * that need repinning.
> + * @vm: The VM.
> + *
> + * This function does an advisory check for whether the VM has userptrs that
> + * need repinning.
> + *
> + * Return: 0 if there are no indications of userptrs needing repinning,
> + * -EAGAIN if there are.
> + */
> +int xe_vm_userptr_check_repin(struct xe_vm *vm)
> +{
> +	return (list_empty_careful(&vm->userptr.repin_list) &&
> +		list_empty_careful(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
> +}
> +
> +int xe_userptr_setup(struct xe_userptr_vma *uvma, unsigned long start,
> +		     unsigned long range)
> +{
> +	struct xe_userptr *userptr = &uvma->userptr;
> +	int err;
> +
> +	INIT_LIST_HEAD(&userptr->invalidate_link);
> +	INIT_LIST_HEAD(&userptr->repin_link);
> +	mutex_init(&userptr->unmap_mutex);
> +
> +	err = mmu_interval_notifier_insert(&userptr->notifier, current->mm,
> +					   start, range,
> +					   &vma_userptr_notifier_ops);
> +	if (err)
> +		return err;
> +
> +	userptr->notifier_seq = LONG_MAX;
> +
> +	return 0;
> +}
> +
> +void xe_userptr_remove(struct xe_userptr_vma *uvma)
> +{
> +	struct xe_userptr *userptr = &uvma->userptr;
> +
> +	if (userptr->sg)
> +		xe_hmm_userptr_free_sg(uvma);
> +
> +	/*
> +	 * Since userptr pages are not pinned, we can't remove
> +	 * the notifier until we're sure the GPU is not accessing
> +	 * them anymore
> +	 */
> +	mmu_interval_notifier_remove(&userptr->notifier);
> +	mutex_destroy(&userptr->unmap_mutex);
> +}
> +
> +void xe_userptr_destroy(struct xe_userptr_vma *uvma)
> +{
> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +
> +	spin_lock(&vm->userptr.invalidated_lock);
> +	xe_assert(vm->xe, list_empty(&uvma->userptr.repin_link));
> +	list_del(&uvma->userptr.invalidate_link);
> +	spin_unlock(&vm->userptr.invalidated_lock);
> +}
> diff --git a/drivers/gpu/drm/xe/xe_userptr.h b/drivers/gpu/drm/xe/xe_userptr.h
> new file mode 100644
> index 000000000000..83d17b58ed16
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_userptr.h
> @@ -0,0 +1,97 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#ifndef _XE_USERPTR_H_
> +#define _XE_USERPTR_H_
> +
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
> +#include <linux/scatterlist.h>
> +#include <linux/spinlock.h>
> +
> +struct xe_vm;
> +struct xe_vma;
> +struct xe_userptr_vma;
> +
> +/** struct xe_userptr_vm - User pointer VM level state */
> +struct xe_userptr_vm {
> +	/**
> +	 * @userptr.repin_list: list of VMAs which are user pointers,
> +	 * and needs repinning. Protected by @lock.
> +	 */
> +	struct list_head repin_list;
> +	/**
> +	 * @notifier_lock: protects notifier in write mode and
> +	 * submission in read mode.
> +	 */
> +	struct rw_semaphore notifier_lock;
> +	/**
> +	 * @userptr.invalidated_lock: Protects the
> +	 * @userptr.invalidated list.
> +	 */
> +	spinlock_t invalidated_lock;
> +	/**
> +	 * @userptr.invalidated: List of invalidated userptrs, not yet
> +	 * picked
> +	 * up for revalidation. Protected from access with the
> +	 * @invalidated_lock. Removing items from the list
> +	 * additionally requires @lock in write mode, and adding
> +	 * items to the list requires either the @userptr.notifer_lock in
> +	 * write mode, OR @lock in write mode.
> +	 */
> +	struct list_head invalidated;
> +};
> +
> +/** struct xe_userptr - User pointer */
> +struct xe_userptr {
> +	/** @invalidate_link: Link for the vm::userptr.invalidated list */
> +	struct list_head invalidate_link;
> +	/** @userptr: link into VM repin list if userptr. */
> +	struct list_head repin_link;
> +	/**
> +	 * @notifier: MMU notifier for user pointer (invalidation call back)
> +	 */
> +	struct mmu_interval_notifier notifier;
> +	/** @sgt: storage for a scatter gather table */
> +	struct sg_table sgt;
> +	/** @sg: allocated scatter gather table */
> +	struct sg_table *sg;
> +	/** @notifier_seq: notifier sequence number */
> +	unsigned long notifier_seq;
> +	/** @unmap_mutex: Mutex protecting dma-unmapping */
> +	struct mutex unmap_mutex;
> +	/**
> +	 * @initial_bind: user pointer has been bound at least once.
> +	 * write: vm->userptr.notifier_lock in read mode and vm->resv held.
> +	 * read: vm->userptr.notifier_lock in write mode or vm->resv held.
> +	 */
> +	bool initial_bind;
> +	/** @mapped: Whether the @sgt sg-table is dma-mapped. Protected by @unmap_mutex. */
> +	bool mapped;
> +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> +	u32 divisor;
> +#endif
> +};
> +
> +void xe_userptr_remove(struct xe_userptr_vma *uvma);
> +int xe_userptr_setup(struct xe_userptr_vma *uvma, unsigned long start,
> +		     unsigned long range);
> +void xe_userptr_destroy(struct xe_userptr_vma *uvma);
> +
> +int xe_vm_userptr_pin(struct xe_vm *vm);
> +int __xe_vm_userptr_needs_repin(struct xe_vm *vm);
> +int xe_vm_userptr_check_repin(struct xe_vm *vm);
> +int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma);
> +int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma);
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> +void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma);
> +#else
> +static inline void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
> +{
> +}
> +#endif
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index 79323c78130f..e5bf4ddc9d86 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -39,6 +39,7 @@
>  #include "xe_svm.h"
>  #include "xe_sync.h"
>  #include "xe_trace_bo.h"
> +#include "xe_userptr.h"
>  #include "xe_wa.h"
>  #include "xe_hmm.h"
>  
> @@ -47,37 +48,6 @@ static struct drm_gem_object *xe_vm_obj(struct xe_vm *vm)
>  	return vm->gpuvm.r_obj;
>  }
>  
> -/**
> - * xe_vma_userptr_check_repin() - Advisory check for repin needed
> - * @uvma: The userptr vma
> - *
> - * Check if the userptr vma has been invalidated since last successful
> - * repin. The check is advisory only and can the function can be called
> - * without the vm->userptr.notifier_lock held. There is no guarantee that the
> - * vma userptr will remain valid after a lockless check, so typically
> - * the call needs to be followed by a proper check under the notifier_lock.
> - *
> - * Return: 0 if userptr vma is valid, -EAGAIN otherwise; repin recommended.
> - */
> -int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma)
> -{
> -	return mmu_interval_check_retry(&uvma->userptr.notifier,
> -					uvma->userptr.notifier_seq) ?
> -		-EAGAIN : 0;
> -}
> -
> -int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma)
> -{
> -	struct xe_vma *vma = &uvma->vma;
> -	struct xe_vm *vm = xe_vma_vm(vma);
> -	struct xe_device *xe = vm->xe;
> -
> -	lockdep_assert_held(&vm->lock);
> -	xe_assert(xe, xe_vma_is_userptr(vma));
> -
> -	return xe_hmm_userptr_populate_range(uvma, false);
> -}
> -
>  static bool preempt_fences_waiting(struct xe_vm *vm)
>  {
>  	struct xe_exec_queue *q;
> @@ -299,25 +269,6 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
>  	up_write(&vm->lock);
>  }
>  
> -/**
> - * __xe_vm_userptr_needs_repin() - Check whether the VM does have userptrs
> - * that need repinning.
> - * @vm: The VM.
> - *
> - * This function checks for whether the VM has userptrs that need repinning,
> - * and provides a release-type barrier on the userptr.notifier_lock after
> - * checking.
> - *
> - * Return: 0 if there are no userptrs needing repinning, -EAGAIN if there are.
> - */
> -int __xe_vm_userptr_needs_repin(struct xe_vm *vm)
> -{
> -	lockdep_assert_held_read(&vm->userptr.notifier_lock);
> -
> -	return (list_empty(&vm->userptr.repin_list) &&
> -		list_empty(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
> -}
> -
>  #define XE_VM_REBIND_RETRY_TIMEOUT_MS 1000
>  
>  /**
> @@ -583,201 +534,6 @@ static void preempt_rebind_work_func(struct work_struct *w)
>  	trace_xe_vm_rebind_worker_exit(vm);
>  }
>  
> -static void __vma_userptr_invalidate(struct xe_vm *vm, struct xe_userptr_vma *uvma)
> -{
> -	struct xe_userptr *userptr = &uvma->userptr;
> -	struct xe_vma *vma = &uvma->vma;
> -	struct dma_resv_iter cursor;
> -	struct dma_fence *fence;
> -	long err;
> -
> -	/*
> -	 * Tell exec and rebind worker they need to repin and rebind this
> -	 * userptr.
> -	 */
> -	if (!xe_vm_in_fault_mode(vm) &&
> -	    !(vma->gpuva.flags & XE_VMA_DESTROYED)) {
> -		spin_lock(&vm->userptr.invalidated_lock);
> -		list_move_tail(&userptr->invalidate_link,
> -			       &vm->userptr.invalidated);
> -		spin_unlock(&vm->userptr.invalidated_lock);
> -	}
> -
> -	/*
> -	 * Preempt fences turn into schedule disables, pipeline these.
> -	 * Note that even in fault mode, we need to wait for binds and
> -	 * unbinds to complete, and those are attached as BOOKMARK fences
> -	 * to the vm.
> -	 */
> -	dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
> -			    DMA_RESV_USAGE_BOOKKEEP);
> -	dma_resv_for_each_fence_unlocked(&cursor, fence)
> -		dma_fence_enable_sw_signaling(fence);
> -	dma_resv_iter_end(&cursor);
> -
> -	err = dma_resv_wait_timeout(xe_vm_resv(vm),
> -				    DMA_RESV_USAGE_BOOKKEEP,
> -				    false, MAX_SCHEDULE_TIMEOUT);
> -	XE_WARN_ON(err <= 0);
> -
> -	if (xe_vm_in_fault_mode(vm) && userptr->initial_bind) {
> -		err = xe_vm_invalidate_vma(vma);
> -		XE_WARN_ON(err);
> -	}
> -
> -	xe_hmm_userptr_unmap(uvma);
> -}
> -
> -static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni,
> -				   const struct mmu_notifier_range *range,
> -				   unsigned long cur_seq)
> -{
> -	struct xe_userptr_vma *uvma = container_of(mni, typeof(*uvma), userptr.notifier);
> -	struct xe_vma *vma = &uvma->vma;
> -	struct xe_vm *vm = xe_vma_vm(vma);
> -
> -	xe_assert(vm->xe, xe_vma_is_userptr(vma));
> -	trace_xe_vma_userptr_invalidate(vma);
> -
> -	if (!mmu_notifier_range_blockable(range))
> -		return false;
> -
> -	vm_dbg(&xe_vma_vm(vma)->xe->drm,
> -	       "NOTIFIER: addr=0x%016llx, range=0x%016llx",
> -		xe_vma_start(vma), xe_vma_size(vma));
> -
> -	down_write(&vm->userptr.notifier_lock);
> -	mmu_interval_set_seq(mni, cur_seq);
> -
> -	__vma_userptr_invalidate(vm, uvma);
> -	up_write(&vm->userptr.notifier_lock);
> -	trace_xe_vma_userptr_invalidate_complete(vma);
> -
> -	return true;
> -}
> -
> -static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = {
> -	.invalidate = vma_userptr_invalidate,
> -};
> -
> -#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> -/**
> - * xe_vma_userptr_force_invalidate() - force invalidate a userptr
> - * @uvma: The userptr vma to invalidate
> - *
> - * Perform a forced userptr invalidation for testing purposes.
> - */
> -void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
> -{
> -	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> -
> -	/* Protect against concurrent userptr pinning */
> -	lockdep_assert_held(&vm->lock);
> -	/* Protect against concurrent notifiers */
> -	lockdep_assert_held(&vm->userptr.notifier_lock);
> -	/*
> -	 * Protect against concurrent instances of this function and
> -	 * the critical exec sections
> -	 */
> -	xe_vm_assert_held(vm);
> -
> -	if (!mmu_interval_read_retry(&uvma->userptr.notifier,
> -				     uvma->userptr.notifier_seq))
> -		uvma->userptr.notifier_seq -= 2;
> -	__vma_userptr_invalidate(vm, uvma);
> -}
> -#endif
> -
> -int xe_vm_userptr_pin(struct xe_vm *vm)
> -{
> -	struct xe_userptr_vma *uvma, *next;
> -	int err = 0;
> -
> -	xe_assert(vm->xe, !xe_vm_in_fault_mode(vm));
> -	lockdep_assert_held_write(&vm->lock);
> -
> -	/* Collect invalidated userptrs */
> -	spin_lock(&vm->userptr.invalidated_lock);
> -	xe_assert(vm->xe, list_empty(&vm->userptr.repin_list));
> -	list_for_each_entry_safe(uvma, next, &vm->userptr.invalidated,
> -				 userptr.invalidate_link) {
> -		list_del_init(&uvma->userptr.invalidate_link);
> -		list_add_tail(&uvma->userptr.repin_link,
> -			      &vm->userptr.repin_list);
> -	}
> -	spin_unlock(&vm->userptr.invalidated_lock);
> -
> -	/* Pin and move to bind list */
> -	list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list,
> -				 userptr.repin_link) {
> -		err = xe_vma_userptr_pin_pages(uvma);
> -		if (err == -EFAULT) {
> -			list_del_init(&uvma->userptr.repin_link);
> -			/*
> -			 * We might have already done the pin once already, but
> -			 * then had to retry before the re-bind happened, due
> -			 * some other condition in the caller, but in the
> -			 * meantime the userptr got dinged by the notifier such
> -			 * that we need to revalidate here, but this time we hit
> -			 * the EFAULT. In such a case make sure we remove
> -			 * ourselves from the rebind list to avoid going down in
> -			 * flames.
> -			 */
> -			if (!list_empty(&uvma->vma.combined_links.rebind))
> -				list_del_init(&uvma->vma.combined_links.rebind);
> -
> -			/* Wait for pending binds */
> -			xe_vm_lock(vm, false);
> -			dma_resv_wait_timeout(xe_vm_resv(vm),
> -					      DMA_RESV_USAGE_BOOKKEEP,
> -					      false, MAX_SCHEDULE_TIMEOUT);
> -
> -			err = xe_vm_invalidate_vma(&uvma->vma);
> -			xe_vm_unlock(vm);
> -			if (err)
> -				break;
> -		} else {
> -			if (err)
> -				break;
> -
> -			list_del_init(&uvma->userptr.repin_link);
> -			list_move_tail(&uvma->vma.combined_links.rebind,
> -				       &vm->rebind_list);
> -		}
> -	}
> -
> -	if (err) {
> -		down_write(&vm->userptr.notifier_lock);
> -		spin_lock(&vm->userptr.invalidated_lock);
> -		list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list,
> -					 userptr.repin_link) {
> -			list_del_init(&uvma->userptr.repin_link);
> -			list_move_tail(&uvma->userptr.invalidate_link,
> -				       &vm->userptr.invalidated);
> -		}
> -		spin_unlock(&vm->userptr.invalidated_lock);
> -		up_write(&vm->userptr.notifier_lock);
> -	}
> -	return err;
> -}
> -
> -/**
> - * xe_vm_userptr_check_repin() - Check whether the VM might have userptrs
> - * that need repinning.
> - * @vm: The VM.
> - *
> - * This function does an advisory check for whether the VM has userptrs that
> - * need repinning.
> - *
> - * Return: 0 if there are no indications of userptrs needing repinning,
> - * -EAGAIN if there are.
> - */
> -int xe_vm_userptr_check_repin(struct xe_vm *vm)
> -{
> -	return (list_empty_careful(&vm->userptr.repin_list) &&
> -		list_empty_careful(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
> -}
> -
>  static int xe_vma_ops_alloc(struct xe_vma_ops *vops, bool array_of_binds)
>  {
>  	int i;
> @@ -1215,25 +971,15 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
>  		drm_gpuvm_bo_put(vm_bo);
>  	} else /* userptr or null */ {
>  		if (!is_null && !is_cpu_addr_mirror) {
> -			struct xe_userptr *userptr = &to_userptr_vma(vma)->userptr;
> -			u64 size = end - start + 1;
> +			struct xe_userptr_vma *uvma = to_userptr_vma(vma);
>  			int err;
>  
> -			INIT_LIST_HEAD(&userptr->invalidate_link);
> -			INIT_LIST_HEAD(&userptr->repin_link);
> -			vma->gpuva.gem.offset = bo_offset_or_userptr;
> -			mutex_init(&userptr->unmap_mutex);
> -
> -			err = mmu_interval_notifier_insert(&userptr->notifier,
> -							   current->mm,
> -							   xe_vma_userptr(vma), size,
> -							   &vma_userptr_notifier_ops);
> +			err = xe_userptr_setup(uvma, xe_vma_userptr(vma),
> +					       end - start + 1);
>  			if (err) {
>  				xe_vma_free(vma);
>  				return ERR_PTR(err);
>  			}
> -
> -			userptr->notifier_seq = LONG_MAX;
>  		}
>  
>  		xe_vm_get(vm);
> @@ -1253,18 +999,8 @@ static void xe_vma_destroy_late(struct xe_vma *vma)
>  
>  	if (xe_vma_is_userptr(vma)) {
>  		struct xe_userptr_vma *uvma = to_userptr_vma(vma);
> -		struct xe_userptr *userptr = &uvma->userptr;
>  
> -		if (userptr->sg)
> -			xe_hmm_userptr_free_sg(uvma);
> -
> -		/*
> -		 * Since userptr pages are not pinned, we can't remove
> -		 * the notifier until we're sure the GPU is not accessing
> -		 * them anymore
> -		 */
> -		mmu_interval_notifier_remove(&userptr->notifier);
> -		mutex_destroy(&userptr->unmap_mutex);
> +		xe_userptr_remove(uvma);
>  		xe_vm_put(vm);
>  	} else if (xe_vma_is_null(vma) || xe_vma_is_cpu_addr_mirror(vma)) {
>  		xe_vm_put(vm);
> @@ -1301,11 +1037,7 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence)
>  
>  	if (xe_vma_is_userptr(vma)) {
>  		xe_assert(vm->xe, vma->gpuva.flags & XE_VMA_DESTROYED);
> -
> -		spin_lock(&vm->userptr.invalidated_lock);
> -		xe_assert(vm->xe, list_empty(&to_userptr_vma(vma)->userptr.repin_link));
> -		list_del(&to_userptr_vma(vma)->userptr.invalidate_link);
> -		spin_unlock(&vm->userptr.invalidated_lock);
> +		xe_userptr_destroy(to_userptr_vma(vma));
>  	} else if (!xe_vma_is_null(vma) && !xe_vma_is_cpu_addr_mirror(vma)) {
>  		xe_bo_assert_held(xe_vma_bo(vma));
>  
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index 0ef811fc2bde..c59a94e2ffb9 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -210,12 +210,6 @@ static inline bool xe_vm_in_preempt_fence_mode(struct xe_vm *vm)
>  int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q);
>  void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q);
>  
> -int xe_vm_userptr_pin(struct xe_vm *vm);
> -
> -int __xe_vm_userptr_needs_repin(struct xe_vm *vm);
> -
> -int xe_vm_userptr_check_repin(struct xe_vm *vm);
> -
>  int xe_vm_rebind(struct xe_vm *vm, bool rebind_worker);
>  struct dma_fence *xe_vma_rebind(struct xe_vm *vm, struct xe_vma *vma,
>  				u8 tile_mask);
> @@ -253,10 +247,6 @@ static inline void xe_vm_reactivate_rebind(struct xe_vm *vm)
>  	}
>  }
>  
> -int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma);
> -
> -int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma);
> -
>  bool xe_vm_validate_should_retry(struct drm_exec *exec, int err, ktime_t *end);
>  
>  int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma);
> @@ -300,12 +290,4 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>  void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>  void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>  void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> -
> -#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> -void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma);
> -#else
> -static inline void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
> -{
> -}
> -#endif
>  #endif
> diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
> index 1662604c4486..65e889f2537d 100644
> --- a/drivers/gpu/drm/xe/xe_vm_types.h
> +++ b/drivers/gpu/drm/xe/xe_vm_types.h
> @@ -17,6 +17,7 @@
>  #include "xe_device_types.h"
>  #include "xe_pt_types.h"
>  #include "xe_range_fence.h"
> +#include "xe_userptr.h"
>  
>  struct xe_bo;
>  struct xe_svm_range;
> @@ -46,37 +47,6 @@ struct xe_vm_pgtable_update_op;
>  #define XE_VMA_DUMPABLE		(DRM_GPUVA_USERBITS << 8)
>  #define XE_VMA_SYSTEM_ALLOCATOR	(DRM_GPUVA_USERBITS << 9)
>  
> -/** struct xe_userptr - User pointer */
> -struct xe_userptr {
> -	/** @invalidate_link: Link for the vm::userptr.invalidated list */
> -	struct list_head invalidate_link;
> -	/** @userptr: link into VM repin list if userptr. */
> -	struct list_head repin_link;
> -	/**
> -	 * @notifier: MMU notifier for user pointer (invalidation call back)
> -	 */
> -	struct mmu_interval_notifier notifier;
> -	/** @sgt: storage for a scatter gather table */
> -	struct sg_table sgt;
> -	/** @sg: allocated scatter gather table */
> -	struct sg_table *sg;
> -	/** @notifier_seq: notifier sequence number */
> -	unsigned long notifier_seq;
> -	/** @unmap_mutex: Mutex protecting dma-unmapping */
> -	struct mutex unmap_mutex;
> -	/**
> -	 * @initial_bind: user pointer has been bound at least once.
> -	 * write: vm->userptr.notifier_lock in read mode and vm->resv held.
> -	 * read: vm->userptr.notifier_lock in write mode or vm->resv held.
> -	 */
> -	bool initial_bind;
> -	/** @mapped: Whether the @sgt sg-table is dma-mapped. Protected by @unmap_mutex. */
> -	bool mapped;
> -#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
> -	u32 divisor;
> -#endif
> -};
> -
>  struct xe_vma {
>  	/** @gpuva: Base GPUVA object */
>  	struct drm_gpuva gpuva;
> @@ -237,33 +207,7 @@ struct xe_vm {
>  	const struct xe_pt_ops *pt_ops;
>  
>  	/** @userptr: user pointer state */
> -	struct {
> -		/**
> -		 * @userptr.repin_list: list of VMAs which are user pointers,
> -		 * and needs repinning. Protected by @lock.
> -		 */
> -		struct list_head repin_list;
> -		/**
> -		 * @notifier_lock: protects notifier in write mode and
> -		 * submission in read mode.
> -		 */
> -		struct rw_semaphore notifier_lock;
> -		/**
> -		 * @userptr.invalidated_lock: Protects the
> -		 * @userptr.invalidated list.
> -		 */
> -		spinlock_t invalidated_lock;
> -		/**
> -		 * @userptr.invalidated: List of invalidated userptrs, not yet
> -		 * picked
> -		 * up for revalidation. Protected from access with the
> -		 * @invalidated_lock. Removing items from the list
> -		 * additionally requires @lock in write mode, and adding
> -		 * items to the list requires either the @userptr.notifer_lock in
> -		 * write mode, OR @lock in write mode.
> -		 */
> -		struct list_head invalidated;
> -	} userptr;
> +	struct xe_userptr_vm userptr;
>  
>  	/** @preempt: preempt state */
>  	struct {
> -- 
> 2.49.0
> 


More information about the Intel-xe mailing list