diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 197 | 
1 files changed, 143 insertions, 54 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 41fbc4fd0fac..d17b2452cb1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -25,6 +25,7 @@  #include <linux/pagemap.h>  #include <linux/sched/mm.h>  #include <linux/sched/task.h> +#include <linux/fdtable.h>  #include <drm/ttm/ttm_tt.h>  #include <drm/drm_exec.h> @@ -806,13 +807,22 @@ kfd_mem_dmaunmap_attachment(struct kgd_mem *mem,  static int kfd_mem_export_dmabuf(struct kgd_mem *mem)  {  	if (!mem->dmabuf) { -		struct dma_buf *ret = amdgpu_gem_prime_export( -			&mem->bo->tbo.base, +		struct amdgpu_device *bo_adev; +		struct dma_buf *dmabuf; +		int r, fd; + +		bo_adev = amdgpu_ttm_adev(mem->bo->tbo.bdev); +		r = drm_gem_prime_handle_to_fd(&bo_adev->ddev, bo_adev->kfd.client.file, +					       mem->gem_handle,  			mem->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE ? -				DRM_RDWR : 0); -		if (IS_ERR(ret)) -			return PTR_ERR(ret); -		mem->dmabuf = ret; +					       DRM_RDWR : 0, &fd); +		if (r) +			return r; +		dmabuf = dma_buf_get(fd); +		close_fd(fd); +		if (WARN_ON_ONCE(IS_ERR(dmabuf))) +			return PTR_ERR(dmabuf); +		mem->dmabuf = dmabuf;  	}  	return 0; @@ -1137,7 +1147,7 @@ static int reserve_bo_and_vm(struct kgd_mem *mem,  	ctx->n_vms = 1;  	ctx->sync = &mem->sync; -	drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT); +	drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);  	drm_exec_until_all_locked(&ctx->exec) {  		ret = amdgpu_vm_lock_pd(vm, &ctx->exec, 2);  		drm_exec_retry_on_contention(&ctx->exec); @@ -1176,7 +1186,7 @@ static int reserve_bo_and_cond_vms(struct kgd_mem *mem,  	int ret;  	ctx->sync = &mem->sync; -	drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT); +	drm_exec_init(&ctx->exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);  	drm_exec_until_all_locked(&ctx->exec) {  		ctx->n_vms = 0;  		list_for_each_entry(entry, &mem->attachments, list) { @@ -1384,7 +1394,6 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info,  				  amdgpu_amdkfd_restore_userptr_worker);  		*process_info = info; -		*ef = dma_fence_get(&info->eviction_fence->base);  	}  	vm->process_info = *process_info; @@ -1415,6 +1424,8 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info,  	list_add_tail(&vm->vm_list_node,  			&(vm->process_info->vm_list_head));  	vm->process_info->n_vms++; + +	*ef = dma_fence_get(&vm->process_info->eviction_fence->base);  	mutex_unlock(&vm->process_info->lock);  	return 0; @@ -1426,10 +1437,7 @@ validate_pd_fail:  reserve_pd_fail:  	vm->process_info = NULL;  	if (info) { -		/* Two fence references: one in info and one in *ef */  		dma_fence_put(&info->eviction_fence->base); -		dma_fence_put(*ef); -		*ef = NULL;  		*process_info = NULL;  		put_pid(info->pid);  create_evict_fence_fail: @@ -1623,7 +1631,8 @@ int amdgpu_amdkfd_criu_resume(void *p)  		goto out_unlock;  	}  	WRITE_ONCE(pinfo->block_mmu_notifications, false); -	schedule_delayed_work(&pinfo->restore_userptr_work, 0); +	queue_delayed_work(system_freezable_wq, +			   &pinfo->restore_userptr_work, 0);  out_unlock:  	mutex_unlock(&pinfo->lock); @@ -1779,6 +1788,9 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(  		pr_debug("Failed to allow vma node access. ret %d\n", ret);  		goto err_node_allow;  	} +	ret = drm_gem_handle_create(adev->kfd.client.file, gobj, &(*mem)->gem_handle); +	if (ret) +		goto err_gem_handle_create;  	bo = gem_to_amdgpu_bo(gobj);  	if (bo_type == ttm_bo_type_sg) {  		bo->tbo.sg = sg; @@ -1830,6 +1842,8 @@ allocate_init_user_pages_failed:  err_pin_bo:  err_validate_bo:  	remove_kgd_mem_from_kfd_bo_list(*mem, avm->process_info); +	drm_gem_handle_delete(adev->kfd.client.file, (*mem)->gem_handle); +err_gem_handle_create:  	drm_vma_node_revoke(&gobj->vma_node, drm_priv);  err_node_allow:  	/* Don't unreserve system mem limit twice */ @@ -1942,8 +1956,11 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu(  	/* Free the BO*/  	drm_vma_node_revoke(&mem->bo->tbo.base.vma_node, drm_priv); -	if (mem->dmabuf) +	drm_gem_handle_delete(adev->kfd.client.file, mem->gem_handle); +	if (mem->dmabuf) {  		dma_buf_put(mem->dmabuf); +		mem->dmabuf = NULL; +	}  	mutex_destroy(&mem->lock);  	/* If this releases the last reference, it will end up calling @@ -2295,34 +2312,26 @@ int amdgpu_amdkfd_gpuvm_get_vm_fault_info(struct amdgpu_device *adev,  	return 0;  } -int amdgpu_amdkfd_gpuvm_import_dmabuf(struct amdgpu_device *adev, -				      struct dma_buf *dma_buf, -				      uint64_t va, void *drm_priv, -				      struct kgd_mem **mem, uint64_t *size, -				      uint64_t *mmap_offset) +static int import_obj_create(struct amdgpu_device *adev, +			     struct dma_buf *dma_buf, +			     struct drm_gem_object *obj, +			     uint64_t va, void *drm_priv, +			     struct kgd_mem **mem, uint64_t *size, +			     uint64_t *mmap_offset)  {  	struct amdgpu_vm *avm = drm_priv_to_vm(drm_priv); -	struct drm_gem_object *obj;  	struct amdgpu_bo *bo;  	int ret; -	obj = amdgpu_gem_prime_import(adev_to_drm(adev), dma_buf); -	if (IS_ERR(obj)) -		return PTR_ERR(obj); -  	bo = gem_to_amdgpu_bo(obj);  	if (!(bo->preferred_domains & (AMDGPU_GEM_DOMAIN_VRAM | -				    AMDGPU_GEM_DOMAIN_GTT))) { +				    AMDGPU_GEM_DOMAIN_GTT)))  		/* Only VRAM and GTT BOs are supported */ -		ret = -EINVAL; -		goto err_put_obj; -	} +		return -EINVAL;  	*mem = kzalloc(sizeof(struct kgd_mem), GFP_KERNEL); -	if (!*mem) { -		ret = -ENOMEM; -		goto err_put_obj; -	} +	if (!*mem) +		return -ENOMEM;  	ret = drm_vma_node_allow(&obj->vma_node, drm_priv);  	if (ret) @@ -2372,8 +2381,41 @@ err_remove_mem:  	drm_vma_node_revoke(&obj->vma_node, drm_priv);  err_free_mem:  	kfree(*mem); +	return ret; +} + +int amdgpu_amdkfd_gpuvm_import_dmabuf_fd(struct amdgpu_device *adev, int fd, +					 uint64_t va, void *drm_priv, +					 struct kgd_mem **mem, uint64_t *size, +					 uint64_t *mmap_offset) +{ +	struct drm_gem_object *obj; +	uint32_t handle; +	int ret; + +	ret = drm_gem_prime_fd_to_handle(&adev->ddev, adev->kfd.client.file, fd, +					 &handle); +	if (ret) +		return ret; +	obj = drm_gem_object_lookup(adev->kfd.client.file, handle); +	if (!obj) { +		ret = -EINVAL; +		goto err_release_handle; +	} + +	ret = import_obj_create(adev, obj->dma_buf, obj, va, drm_priv, mem, size, +				mmap_offset); +	if (ret) +		goto err_put_obj; + +	(*mem)->gem_handle = handle; + +	return 0; +  err_put_obj:  	drm_gem_object_put(obj); +err_release_handle: +	drm_gem_handle_delete(adev->kfd.client.file, handle);  	return ret;  } @@ -2426,7 +2468,8 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni,  				       KFD_QUEUE_EVICTION_TRIGGER_USERPTR);  		if (r)  			pr_err("Failed to quiesce KFD\n"); -		schedule_delayed_work(&process_info->restore_userptr_work, +		queue_delayed_work(system_freezable_wq, +			&process_info->restore_userptr_work,  			msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));  	}  	mutex_unlock(&process_info->notifier_lock); @@ -2552,7 +2595,7 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info)  	amdgpu_sync_create(&sync); -	drm_exec_init(&exec, 0); +	drm_exec_init(&exec, 0, 0);  	/* Reserve all BOs and page tables for validation */  	drm_exec_until_all_locked(&exec) {  		/* Reserve all the page directories */ @@ -2749,7 +2792,8 @@ unlock_out:  	/* If validation failed, reschedule another attempt */  	if (evicted_bos) { -		schedule_delayed_work(&process_info->restore_userptr_work, +		queue_delayed_work(system_freezable_wq, +			&process_info->restore_userptr_work,  			msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));  		kfd_smi_event_queue_restore_rescheduled(mm); @@ -2758,6 +2802,23 @@ unlock_out:  	put_task_struct(usertask);  } +static void replace_eviction_fence(struct dma_fence **ef, +				   struct dma_fence *new_ef) +{ +	struct dma_fence *old_ef = rcu_replace_pointer(*ef, new_ef, true +		/* protected by process_info->lock */); + +	/* If we're replacing an unsignaled eviction fence, that fence will +	 * never be signaled, and if anyone is still waiting on that fence, +	 * they will hang forever. This should never happen. We should only +	 * replace the fence in restore_work that only gets scheduled after +	 * eviction work signaled the fence. +	 */ +	WARN_ONCE(!dma_fence_is_signaled(old_ef), +		  "Replacing unsignaled eviction fence"); +	dma_fence_put(old_ef); +} +  /** amdgpu_amdkfd_gpuvm_restore_process_bos - Restore all BOs for the given   *   KFD process identified by process_info   * @@ -2781,7 +2842,6 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  	struct amdkfd_process_info *process_info = info;  	struct amdgpu_vm *peer_vm;  	struct kgd_mem *mem; -	struct amdgpu_amdkfd_fence *new_fence;  	struct list_head duplicate_save;  	struct amdgpu_sync sync_obj;  	unsigned long failed_size = 0; @@ -2793,7 +2853,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  	mutex_lock(&process_info->lock); -	drm_exec_init(&exec, 0); +	drm_exec_init(&exec, 0, 0);  	drm_exec_until_all_locked(&exec) {  		list_for_each_entry(peer_vm, &process_info->vm_list_head,  				    vm_list_node) { @@ -2825,12 +2885,6 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  	if (ret)  		goto validate_map_fail; -	ret = process_sync_pds_resv(process_info, &sync_obj); -	if (ret) { -		pr_debug("Memory eviction: Failed to sync to PD BO moving fence. Try again\n"); -		goto validate_map_fail; -	} -  	/* Validate BOs and map them to GPUVM (update VM page tables). */  	list_for_each_entry(mem, &process_info->kfd_bo_list,  			    validate_list) { @@ -2881,6 +2935,19 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  	if (failed_size)  		pr_debug("0x%lx/0x%lx in system\n", failed_size, total_size); +	/* Update mappings not managed by KFD */ +	list_for_each_entry(peer_vm, &process_info->vm_list_head, +			vm_list_node) { +		struct amdgpu_device *adev = amdgpu_ttm_adev( +			peer_vm->root.bo->tbo.bdev); + +		ret = amdgpu_vm_handle_moved(adev, peer_vm, &exec.ticket); +		if (ret) { +			pr_debug("Memory eviction: handle moved failed. Try again\n"); +			goto validate_map_fail; +		} +	} +  	/* Update page directories */  	ret = process_update_pds(process_info, &sync_obj);  	if (ret) { @@ -2888,25 +2955,47 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)  		goto validate_map_fail;  	} +	/* Sync with fences on all the page tables. They implicitly depend on any +	 * move fences from amdgpu_vm_handle_moved above. +	 */ +	ret = process_sync_pds_resv(process_info, &sync_obj); +	if (ret) { +		pr_debug("Memory eviction: Failed to sync to PD BO moving fence. Try again\n"); +		goto validate_map_fail; +	} +  	/* Wait for validate and PT updates to finish */  	amdgpu_sync_wait(&sync_obj, false); -	/* Release old eviction fence and create new one, because fence only -	 * goes from unsignaled to signaled, fence cannot be reused. -	 * Use context and mm from the old fence. +	/* The old eviction fence may be unsignaled if restore happens +	 * after a GPU reset or suspend/resume. Keep the old fence in that +	 * case. Otherwise release the old eviction fence and create new +	 * one, because fence only goes from unsignaled to signaled once +	 * and cannot be reused. Use context and mm from the old fence. +	 * +	 * If an old eviction fence signals after this check, that's OK. +	 * Anyone signaling an eviction fence must stop the queues first +	 * and schedule another restore worker.  	 */ -	new_fence = amdgpu_amdkfd_fence_create( +	if (dma_fence_is_signaled(&process_info->eviction_fence->base)) { +		struct amdgpu_amdkfd_fence *new_fence = +			amdgpu_amdkfd_fence_create(  				process_info->eviction_fence->base.context,  				process_info->eviction_fence->mm,  				NULL); -	if (!new_fence) { -		pr_err("Failed to create eviction fence\n"); -		ret = -ENOMEM; -		goto validate_map_fail; + +		if (!new_fence) { +			pr_err("Failed to create eviction fence\n"); +			ret = -ENOMEM; +			goto validate_map_fail; +		} +		dma_fence_put(&process_info->eviction_fence->base); +		process_info->eviction_fence = new_fence; +		replace_eviction_fence(ef, dma_fence_get(&new_fence->base)); +	} else { +		WARN_ONCE(*ef != &process_info->eviction_fence->base, +			  "KFD eviction fence doesn't match KGD process_info");  	} -	dma_fence_put(&process_info->eviction_fence->base); -	process_info->eviction_fence = new_fence; -	*ef = dma_fence_get(&new_fence->base);  	/* Attach new eviction fence to all BOs except pinned ones */  	list_for_each_entry(mem, &process_info->kfd_bo_list, validate_list) {  | 
