[PATCH v3] drm/amdkfd: unregistered svm range not overlap with TTM range

Felix Kuehling felix.kuehling at amd.com
Wed Oct 13 22:33:16 UTC 2021


Am 2021-10-13 um 6:05 p.m. schrieb Philip Yang:
> When creating unregistered new svm range to recover retry fault, avoid
> new svm range to overlap with ranges or userptr ranges managed by TTM,
> otherwise svm migration will trigger TTM or userptr eviction, to evict
> user queues unexpectedly.
>
> Change helper amdgpu_ttm_tt_affect_userptr to return userptr which is
> inside the range. Add helper svm_range_check_vm_userptr to scan all
> userptr of the vm, and return overlap userptr bo start, last.
>
> Signed-off-by: Philip Yang <Philip.Yang at amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |  4 +-
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h |  2 +-
>  drivers/gpu/drm/amd/amdkfd/kfd_svm.c    | 93 +++++++++++++++++++++++--
>  3 files changed, 92 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> index bd5dda8066fa..d784f8d3a834 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> @@ -1220,7 +1220,7 @@ struct mm_struct *amdgpu_ttm_tt_get_usermm(struct ttm_tt *ttm)
>   *
>   */
>  bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start,
> -				  unsigned long end)
> +				  unsigned long end, unsigned long *userptr)
>  {
>  	struct amdgpu_ttm_tt *gtt = (void *)ttm;
>  	unsigned long size;
> @@ -1235,6 +1235,8 @@ bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start,
>  	if (gtt->userptr > end || gtt->userptr + size <= start)
>  		return false;
>  
> +	if (userptr)
> +		*userptr = gtt->userptr;
>  	return true;
>  }
>  
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
> index ba5c864b8de1..91a087f9dc7c 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
> @@ -182,7 +182,7 @@ int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
>  bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm);
>  struct mm_struct *amdgpu_ttm_tt_get_usermm(struct ttm_tt *ttm);
>  bool amdgpu_ttm_tt_affect_userptr(struct ttm_tt *ttm, unsigned long start,
> -				  unsigned long end);
> +				  unsigned long end, unsigned long *userptr);
>  bool amdgpu_ttm_tt_userptr_invalidated(struct ttm_tt *ttm,
>  				       int *last_invalidated);
>  bool amdgpu_ttm_tt_is_userptr(struct ttm_tt *ttm);
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
> index 49c92713c2ad..95d018fe2287 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
> @@ -50,7 +50,9 @@ static bool
>  svm_range_cpu_invalidate_pagetables(struct mmu_interval_notifier *mni,
>  				    const struct mmu_notifier_range *range,
>  				    unsigned long cur_seq);
> -
> +static int
> +svm_range_check_vm(struct kfd_process *p, uint64_t start, uint64_t last,
> +		   uint64_t *bo_s, uint64_t *bo_l);
>  static const struct mmu_interval_notifier_ops svm_range_mn_ops = {
>  	.invalidate = svm_range_cpu_invalidate_pagetables,
>  };
> @@ -2308,6 +2310,7 @@ svm_range_best_restore_location(struct svm_range *prange,
>  
>  	return -1;
>  }
> +
>  static int
>  svm_range_get_range_boundaries(struct kfd_process *p, int64_t addr,
>  				unsigned long *start, unsigned long *last)
> @@ -2355,8 +2358,59 @@ svm_range_get_range_boundaries(struct kfd_process *p, int64_t addr,
>  		  vma->vm_end >> PAGE_SHIFT, *last);
>  
>  	return 0;
> +}
> +
> +static int
> +svm_range_check_vm_userptr(struct kfd_process *p, uint64_t start, uint64_t last,
> +			   uint64_t *bo_s, uint64_t *bo_l)
> +{
> +	struct amdgpu_bo_va_mapping *mapping;
> +	struct interval_tree_node *node;
> +	struct amdgpu_bo *bo = NULL;
> +	unsigned long userptr;
> +	uint32_t i;
> +	int r;
>  
> +	for (i = 0; i < p->n_pdds; i++) {
> +		struct amdgpu_vm *vm;
> +
> +		if (!p->pdds[i]->drm_priv)
> +			continue;
> +
> +		vm = drm_priv_to_vm(p->pdds[i]->drm_priv);
> +		r = amdgpu_bo_reserve(vm->root.bo, false);
> +		if (r)
> +			return r;
> +
> +		/* Check userptr by searching entire vm->va interval tree */
> +		node = interval_tree_iter_first(&vm->va, 0, ~0ULL);
> +		while (node) {
> +			mapping = container_of((struct rb_node *)node,
> +					       struct amdgpu_bo_va_mapping, rb);
> +			bo = mapping->bo_va->base.bo;
> +
> +			if (!amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm,
> +							 start << PAGE_SHIFT,
> +							 last << PAGE_SHIFT,
> +							 &userptr)) {
> +				node = interval_tree_iter_next(node, 0, ~0ULL);
> +				continue;
> +			}
> +
> +			pr_debug("[0x%llx 0x%llx] already userptr mapped\n",
> +				 start, last);
> +			if (bo_s && bo_l) {
> +				*bo_s = userptr >> PAGE_SHIFT;
> +				*bo_l = *bo_s + bo->tbo.ttm->num_pages - 1;
> +			}
> +			amdgpu_bo_unreserve(vm->root.bo);
> +			return -EADDRINUSE;
> +		}
> +		amdgpu_bo_unreserve(vm->root.bo);
> +	}
> +	return 0;
>  }
> +
>  static struct
>  svm_range *svm_range_create_unregistered_range(struct amdgpu_device *adev,
>  						struct kfd_process *p,
> @@ -2366,10 +2420,24 @@ svm_range *svm_range_create_unregistered_range(struct amdgpu_device *adev,
>  	struct svm_range *prange = NULL;
>  	unsigned long start, last;
>  	uint32_t gpuid, gpuidx;
> +	uint64_t bo_s = 0;
> +	uint64_t bo_l = 0;
>  
>  	if (svm_range_get_range_boundaries(p, addr, &start, &last))
>  		return NULL;
>  
> +	if (svm_range_check_vm(p, start, last, &bo_s, &bo_l) != -EADDRINUSE)
> +		svm_range_check_vm_userptr(p, start, last, &bo_s, &bo_l);
> +
> +	if (addr >= bo_s && addr <= bo_l)
> +		return NULL;
> +
> +	/* Create one page svm range if 2MB range overlaps with TTM range */
> +	if (addr < bo_s || addr > bo_l) {

If no overlapping BO is found, bo_l is 0 and addr > bo_l is true. So you
basically always go into this if-block. You should only use bo_s/l if
the return value from svm_range_check_vm or svm_range_check_vm_userptr
is -EADDRINUSE.

Regards,
  Felix


> +		start = addr;
> +		last = addr;
> +	}
> +
>  	prange = svm_range_new(&p->svms, start, last);
>  	if (!prange) {
>  		pr_debug("Failed to create prange in address [0x%llx]\n", addr);
> @@ -2672,6 +2740,8 @@ int svm_range_list_init(struct kfd_process *p)
>   * @p: current kfd_process
>   * @start: range start address, in pages
>   * @last: range last address, in pages
> + * @bo_s: mapping start address in pages if address range already mapped
> + * @bo_l: mapping last address in pages if address range already mapped
>   *
>   * The purpose is to avoid virtual address ranges already allocated by
>   * kfd_ioctl_alloc_memory_of_gpu ioctl.
> @@ -2686,8 +2756,11 @@ int svm_range_list_init(struct kfd_process *p)
>   * a signal. Release all buffer reservations and return to user-space.
>   */
>  static int
> -svm_range_check_vm(struct kfd_process *p, uint64_t start, uint64_t last)
> +svm_range_check_vm(struct kfd_process *p, uint64_t start, uint64_t last,
> +		   uint64_t *bo_s, uint64_t *bo_l)
>  {
> +	struct amdgpu_bo_va_mapping *mapping;
> +	struct interval_tree_node *node;
>  	uint32_t i;
>  	int r;
>  
> @@ -2701,8 +2774,17 @@ svm_range_check_vm(struct kfd_process *p, uint64_t start, uint64_t last)
>  		r = amdgpu_bo_reserve(vm->root.bo, false);
>  		if (r)
>  			return r;
> -		if (interval_tree_iter_first(&vm->va, start, last)) {
> -			pr_debug("Range [0x%llx 0x%llx] already mapped\n", start, last);
> +
> +		node = interval_tree_iter_first(&vm->va, start, last);
> +		if (node) {
> +			pr_debug("range [0x%llx 0x%llx] already TTM mapped\n",
> +				 start, last);
> +			mapping = container_of((struct rb_node *)node,
> +					       struct amdgpu_bo_va_mapping, rb);
> +			if (bo_s && bo_l) {
> +				*bo_s = mapping->start;
> +				*bo_l = mapping->last;
> +			}
>  			amdgpu_bo_unreserve(vm->root.bo);
>  			return -EADDRINUSE;
>  		}
> @@ -2743,7 +2825,8 @@ svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size)
>  		start = min(end, vma->vm_end);
>  	} while (start < end);
>  
> -	return svm_range_check_vm(p, start_unchg, (end - 1) >> PAGE_SHIFT);
> +	return svm_range_check_vm(p, start_unchg, (end - 1) >> PAGE_SHIFT, NULL,
> +				  NULL);
>  }
>  
>  /**


More information about the amd-gfx mailing list