diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 412 | 
1 files changed, 283 insertions, 129 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 16884a0b677b..a1c78f90eadf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -40,6 +40,12 @@   */  static DEFINE_IDA(amdgpu_pasid_ida); +/* Helper to free pasid from a fence callback */ +struct amdgpu_pasid_cb { +	struct dma_fence_cb cb; +	unsigned int pasid; +}; +  /**   * amdgpu_pasid_alloc - Allocate a PASID   * @bits: Maximum width of the PASID in bits, must be at least 1 @@ -63,6 +69,9 @@ int amdgpu_pasid_alloc(unsigned int bits)  			break;  	} +	if (pasid >= 0) +		trace_amdgpu_pasid_allocated(pasid); +  	return pasid;  } @@ -72,9 +81,86 @@ int amdgpu_pasid_alloc(unsigned int bits)   */  void amdgpu_pasid_free(unsigned int pasid)  { +	trace_amdgpu_pasid_freed(pasid);  	ida_simple_remove(&amdgpu_pasid_ida, pasid);  } +static void amdgpu_pasid_free_cb(struct dma_fence *fence, +				 struct dma_fence_cb *_cb) +{ +	struct amdgpu_pasid_cb *cb = +		container_of(_cb, struct amdgpu_pasid_cb, cb); + +	amdgpu_pasid_free(cb->pasid); +	dma_fence_put(fence); +	kfree(cb); +} + +/** + * amdgpu_pasid_free_delayed - free pasid when fences signal + * + * @resv: reservation object with the fences to wait for + * @pasid: pasid to free + * + * Free the pasid only after all the fences in resv are signaled. + */ +void amdgpu_pasid_free_delayed(struct reservation_object *resv, +			       unsigned int pasid) +{ +	struct dma_fence *fence, **fences; +	struct amdgpu_pasid_cb *cb; +	unsigned count; +	int r; + +	r = reservation_object_get_fences_rcu(resv, NULL, &count, &fences); +	if (r) +		goto fallback; + +	if (count == 0) { +		amdgpu_pasid_free(pasid); +		return; +	} + +	if (count == 1) { +		fence = fences[0]; +		kfree(fences); +	} else { +		uint64_t context = dma_fence_context_alloc(1); +		struct dma_fence_array *array; + +		array = dma_fence_array_create(count, fences, context, +					       1, false); +		if (!array) { +			kfree(fences); +			goto fallback; +		} +		fence = &array->base; +	} + +	cb = kmalloc(sizeof(*cb), GFP_KERNEL); +	if (!cb) { +		/* Last resort when we are OOM */ +		dma_fence_wait(fence, false); +		dma_fence_put(fence); +		amdgpu_pasid_free(pasid); +	} else { +		cb->pasid = pasid; +		if (dma_fence_add_callback(fence, &cb->cb, +					   amdgpu_pasid_free_cb)) +			amdgpu_pasid_free_cb(fence, &cb->cb); +	} + +	return; + +fallback: +	/* Not enough memory for the delayed delete, as last resort +	 * block for all the fences to complete. +	 */ +	reservation_object_wait_timeout_rcu(resv, true, false, +					    MAX_SCHEDULE_TIMEOUT); +	amdgpu_pasid_free(pasid); +} +  /*   * VMID manager   * @@ -96,164 +182,185 @@ bool amdgpu_vmid_had_gpu_reset(struct amdgpu_device *adev,  		atomic_read(&adev->gpu_reset_counter);  } -/* idr_mgr->lock must be held */ -static int amdgpu_vmid_grab_reserved_locked(struct amdgpu_vm *vm, -					    struct amdgpu_ring *ring, -					    struct amdgpu_sync *sync, -					    struct dma_fence *fence, -					    struct amdgpu_job *job) -{ -	struct amdgpu_device *adev = ring->adev; -	unsigned vmhub = ring->funcs->vmhub; -	uint64_t fence_context = adev->fence_context + ring->idx; -	struct amdgpu_vmid *id = vm->reserved_vmid[vmhub]; -	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; -	struct dma_fence *updates = sync->last_vm_update; -	int r = 0; -	struct dma_fence *flushed, *tmp; -	bool needs_flush = vm->use_cpu_for_update; - -	flushed  = id->flushed_updates; -	if ((amdgpu_vmid_had_gpu_reset(adev, id)) || -	    (atomic64_read(&id->owner) != vm->entity.fence_context) || -	    (job->vm_pd_addr != id->pd_gpu_addr) || -	    (updates && (!flushed || updates->context != flushed->context || -			dma_fence_is_later(updates, flushed))) || -	    (!id->last_flush || (id->last_flush->context != fence_context && -				 !dma_fence_is_signaled(id->last_flush)))) { -		needs_flush = true; -		/* to prevent one context starved by another context */ -		id->pd_gpu_addr = 0; -		tmp = amdgpu_sync_peek_fence(&id->active, ring); -		if (tmp) { -			r = amdgpu_sync_fence(adev, sync, tmp, false); -			return r; -		} -	} - -	/* Good we can use this VMID. Remember this submission as -	* user of the VMID. -	*/ -	r = amdgpu_sync_fence(ring->adev, &id->active, fence, false); -	if (r) -		goto out; - -	if (updates && (!flushed || updates->context != flushed->context || -			dma_fence_is_later(updates, flushed))) { -		dma_fence_put(id->flushed_updates); -		id->flushed_updates = dma_fence_get(updates); -	} -	id->pd_gpu_addr = job->vm_pd_addr; -	atomic64_set(&id->owner, vm->entity.fence_context); -	job->vm_needs_flush = needs_flush; -	if (needs_flush) { -		dma_fence_put(id->last_flush); -		id->last_flush = NULL; -	} -	job->vmid = id - id_mgr->ids; -	trace_amdgpu_vm_grab_id(vm, ring, job); -out: -	return r; -} -  /** - * amdgpu_vm_grab_id - allocate the next free VMID + * amdgpu_vm_grab_idle - grab idle VMID   *   * @vm: vm to allocate id for   * @ring: ring we want to submit job to   * @sync: sync object where we add dependencies - * @fence: fence protecting ID from reuse + * @idle: resulting idle VMID   * - * Allocate an id for the vm, adding fences to the sync obj as necessary. + * Try to find an idle VMID, if none is idle add a fence to wait to the sync + * object. Returns -ENOMEM when we are out of memory.   */ -int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring, -		     struct amdgpu_sync *sync, struct dma_fence *fence, -		     struct amdgpu_job *job) +static int amdgpu_vmid_grab_idle(struct amdgpu_vm *vm, +				 struct amdgpu_ring *ring, +				 struct amdgpu_sync *sync, +				 struct amdgpu_vmid **idle)  {  	struct amdgpu_device *adev = ring->adev;  	unsigned vmhub = ring->funcs->vmhub;  	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; -	uint64_t fence_context = adev->fence_context + ring->idx; -	struct dma_fence *updates = sync->last_vm_update; -	struct amdgpu_vmid *id, *idle;  	struct dma_fence **fences;  	unsigned i; -	int r = 0; +	int r; + +	if (ring->vmid_wait && !dma_fence_is_signaled(ring->vmid_wait)) +		return amdgpu_sync_fence(adev, sync, ring->vmid_wait, false); -	mutex_lock(&id_mgr->lock); -	if (vm->reserved_vmid[vmhub]) { -		r = amdgpu_vmid_grab_reserved_locked(vm, ring, sync, fence, job); -		mutex_unlock(&id_mgr->lock); -		return r; -	}  	fences = kmalloc_array(sizeof(void *), id_mgr->num_ids, GFP_KERNEL); -	if (!fences) { -		mutex_unlock(&id_mgr->lock); +	if (!fences)  		return -ENOMEM; -	} +  	/* Check if we have an idle VMID */  	i = 0; -	list_for_each_entry(idle, &id_mgr->ids_lru, list) { -		fences[i] = amdgpu_sync_peek_fence(&idle->active, ring); +	list_for_each_entry((*idle), &id_mgr->ids_lru, list) { +		fences[i] = amdgpu_sync_peek_fence(&(*idle)->active, ring);  		if (!fences[i])  			break;  		++i;  	}  	/* If we can't find a idle VMID to use, wait till one becomes available */ -	if (&idle->list == &id_mgr->ids_lru) { +	if (&(*idle)->list == &id_mgr->ids_lru) {  		u64 fence_context = adev->vm_manager.fence_context + ring->idx;  		unsigned seqno = ++adev->vm_manager.seqno[ring->idx];  		struct dma_fence_array *array;  		unsigned j; +		*idle = NULL;  		for (j = 0; j < i; ++j)  			dma_fence_get(fences[j]);  		array = dma_fence_array_create(i, fences, fence_context, -					   seqno, true); +					       seqno, true);  		if (!array) {  			for (j = 0; j < i; ++j)  				dma_fence_put(fences[j]);  			kfree(fences); -			r = -ENOMEM; -			goto error; +			return -ENOMEM;  		} +		r = amdgpu_sync_fence(adev, sync, &array->base, false); +		dma_fence_put(ring->vmid_wait); +		ring->vmid_wait = &array->base; +		return r; +	} +	kfree(fences); -		r = amdgpu_sync_fence(ring->adev, sync, &array->base, false); -		dma_fence_put(&array->base); -		if (r) -			goto error; +	return 0; +} -		mutex_unlock(&id_mgr->lock); -		return 0; +/** + * amdgpu_vm_grab_reserved - try to assign reserved VMID + * + * @vm: vm to allocate id for + * @ring: ring we want to submit job to + * @sync: sync object where we add dependencies + * @fence: fence protecting ID from reuse + * @job: job who wants to use the VMID + * + * Try to assign a reserved VMID. + */ +static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm, +				     struct amdgpu_ring *ring, +				     struct amdgpu_sync *sync, +				     struct dma_fence *fence, +				     struct amdgpu_job *job, +				     struct amdgpu_vmid **id) +{ +	struct amdgpu_device *adev = ring->adev; +	unsigned vmhub = ring->funcs->vmhub; +	uint64_t fence_context = adev->fence_context + ring->idx; +	struct dma_fence *updates = sync->last_vm_update; +	bool needs_flush = vm->use_cpu_for_update; +	int r = 0; + +	*id = vm->reserved_vmid[vmhub]; +	if (updates && (*id)->flushed_updates && +	    updates->context == (*id)->flushed_updates->context && +	    !dma_fence_is_later(updates, (*id)->flushed_updates)) +	    updates = NULL; + +	if ((*id)->owner != vm->entity.fence_context || +	    job->vm_pd_addr != (*id)->pd_gpu_addr || +	    updates || !(*id)->last_flush || +	    ((*id)->last_flush->context != fence_context && +	     !dma_fence_is_signaled((*id)->last_flush))) { +		struct dma_fence *tmp; +		/* to prevent one context starved by another context */ +		(*id)->pd_gpu_addr = 0; +		tmp = amdgpu_sync_peek_fence(&(*id)->active, ring); +		if (tmp) { +			*id = NULL; +			r = amdgpu_sync_fence(adev, sync, tmp, false); +			return r; +		} +		needs_flush = true;  	} -	kfree(fences); + +	/* Good we can use this VMID. Remember this submission as +	* user of the VMID. +	*/ +	r = amdgpu_sync_fence(ring->adev, &(*id)->active, fence, false); +	if (r) +		return r; + +	if (updates) { +		dma_fence_put((*id)->flushed_updates); +		(*id)->flushed_updates = dma_fence_get(updates); +	} +	job->vm_needs_flush = needs_flush; +	return 0; +} + +/** + * amdgpu_vm_grab_used - try to reuse a VMID + * + * @vm: vm to allocate id for + * @ring: ring we want to submit job to + * @sync: sync object where we add dependencies + * @fence: fence protecting ID from reuse + * @job: job who wants to use the VMID + * @id: resulting VMID + * + * Try to reuse a VMID for this submission. + */ +static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm, +				 struct amdgpu_ring *ring, +				 struct amdgpu_sync *sync, +				 struct dma_fence *fence, +				 struct amdgpu_job *job, +				 struct amdgpu_vmid **id) +{ +	struct amdgpu_device *adev = ring->adev; +	unsigned vmhub = ring->funcs->vmhub; +	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; +	uint64_t fence_context = adev->fence_context + ring->idx; +	struct dma_fence *updates = sync->last_vm_update; +	int r;  	job->vm_needs_flush = vm->use_cpu_for_update; +  	/* Check if we can use a VMID already assigned to this VM */ -	list_for_each_entry_reverse(id, &id_mgr->ids_lru, list) { -		struct dma_fence *flushed; +	list_for_each_entry_reverse((*id), &id_mgr->ids_lru, list) {  		bool needs_flush = vm->use_cpu_for_update; +		struct dma_fence *flushed;  		/* Check all the prerequisites to using this VMID */ -		if (amdgpu_vmid_had_gpu_reset(adev, id)) -			continue; - -		if (atomic64_read(&id->owner) != vm->entity.fence_context) +		if ((*id)->owner != vm->entity.fence_context)  			continue; -		if (job->vm_pd_addr != id->pd_gpu_addr) +		if ((*id)->pd_gpu_addr != job->vm_pd_addr)  			continue; -		if (!id->last_flush || -		    (id->last_flush->context != fence_context && -		     !dma_fence_is_signaled(id->last_flush))) +		if (!(*id)->last_flush || +		    ((*id)->last_flush->context != fence_context && +		     !dma_fence_is_signaled((*id)->last_flush)))  			needs_flush = true; -		flushed  = id->flushed_updates; +		flushed  = (*id)->flushed_updates;  		if (updates && (!flushed || dma_fence_is_later(updates, flushed)))  			needs_flush = true; @@ -261,47 +368,91 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring,  		if (adev->asic_type < CHIP_VEGA10 && needs_flush)  			continue; -		/* Good we can use this VMID. Remember this submission as +		/* Good, we can use this VMID. Remember this submission as  		 * user of the VMID.  		 */ -		r = amdgpu_sync_fence(ring->adev, &id->active, fence, false); +		r = amdgpu_sync_fence(ring->adev, &(*id)->active, fence, false);  		if (r) -			goto error; +			return r;  		if (updates && (!flushed || dma_fence_is_later(updates, flushed))) { -			dma_fence_put(id->flushed_updates); -			id->flushed_updates = dma_fence_get(updates); +			dma_fence_put((*id)->flushed_updates); +			(*id)->flushed_updates = dma_fence_get(updates);  		} -		if (needs_flush) -			goto needs_flush; -		else -			goto no_flush_needed; +		job->vm_needs_flush |= needs_flush; +		return 0; +	} -	}; +	*id = NULL; +	return 0; +} -	/* Still no ID to use? Then use the idle one found earlier */ -	id = idle; +/** + * amdgpu_vm_grab_id - allocate the next free VMID + * + * @vm: vm to allocate id for + * @ring: ring we want to submit job to + * @sync: sync object where we add dependencies + * @fence: fence protecting ID from reuse + * @job: job who wants to use the VMID + * + * Allocate an id for the vm, adding fences to the sync obj as necessary. + */ +int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring, +		     struct amdgpu_sync *sync, struct dma_fence *fence, +		     struct amdgpu_job *job) +{ +	struct amdgpu_device *adev = ring->adev; +	unsigned vmhub = ring->funcs->vmhub; +	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; +	struct amdgpu_vmid *idle = NULL; +	struct amdgpu_vmid *id = NULL; +	int r = 0; -	/* Remember this submission as user of the VMID */ -	r = amdgpu_sync_fence(ring->adev, &id->active, fence, false); -	if (r) +	mutex_lock(&id_mgr->lock); +	r = amdgpu_vmid_grab_idle(vm, ring, sync, &idle); +	if (r || !idle)  		goto error; -	id->pd_gpu_addr = job->vm_pd_addr; -	dma_fence_put(id->flushed_updates); -	id->flushed_updates = dma_fence_get(updates); -	atomic64_set(&id->owner, vm->entity.fence_context); +	if (vm->reserved_vmid[vmhub]) { +		r = amdgpu_vmid_grab_reserved(vm, ring, sync, fence, job, &id); +		if (r || !id) +			goto error; +	} else { +		r = amdgpu_vmid_grab_used(vm, ring, sync, fence, job, &id); +		if (r) +			goto error; -needs_flush: -	job->vm_needs_flush = true; -	dma_fence_put(id->last_flush); -	id->last_flush = NULL; +		if (!id) { +			struct dma_fence *updates = sync->last_vm_update; -no_flush_needed: -	list_move_tail(&id->list, &id_mgr->ids_lru); +			/* Still no ID to use? Then use the idle one found earlier */ +			id = idle; +			/* Remember this submission as user of the VMID */ +			r = amdgpu_sync_fence(ring->adev, &id->active, +					      fence, false); +			if (r) +				goto error; + +			dma_fence_put(id->flushed_updates); +			id->flushed_updates = dma_fence_get(updates); +			job->vm_needs_flush = true; +		} + +		list_move_tail(&id->list, &id_mgr->ids_lru); +	} + +	id->pd_gpu_addr = job->vm_pd_addr; +	id->owner = vm->entity.fence_context; + +	if (job->vm_needs_flush) { +		dma_fence_put(id->last_flush); +		id->last_flush = NULL; +	}  	job->vmid = id - id_mgr->ids; +	job->pasid = vm->pasid;  	trace_amdgpu_vm_grab_id(vm, ring, job);  error: @@ -370,13 +521,15 @@ void amdgpu_vmid_reset(struct amdgpu_device *adev, unsigned vmhub,  	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];  	struct amdgpu_vmid *id = &id_mgr->ids[vmid]; -	atomic64_set(&id->owner, 0); +	mutex_lock(&id_mgr->lock); +	id->owner = 0;  	id->gds_base = 0;  	id->gds_size = 0;  	id->gws_base = 0;  	id->gws_size = 0;  	id->oa_base = 0;  	id->oa_size = 0; +	mutex_unlock(&id_mgr->lock);  }  /** @@ -454,6 +607,7 @@ void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev)  			amdgpu_sync_free(&id->active);  			dma_fence_put(id->flushed_updates);  			dma_fence_put(id->last_flush); +			dma_fence_put(id->pasid_mapping);  		}  	}  }  |