diff options
Diffstat (limited to 'drivers/gpu/drm/scheduler/sched_main.c')
| -rw-r--r-- | drivers/gpu/drm/scheduler/sched_main.c | 199 | 
1 files changed, 171 insertions, 28 deletions
| diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 67382621b429..042c16b5d54a 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -48,9 +48,11 @@  #include <linux/wait.h>  #include <linux/sched.h>  #include <linux/completion.h> +#include <linux/dma-resv.h>  #include <uapi/linux/sched/types.h>  #include <drm/drm_print.h> +#include <drm/drm_gem.h>  #include <drm/gpu_scheduler.h>  #include <drm/spsc_queue.h> @@ -564,7 +566,6 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext);  /**   * drm_sched_job_init - init a scheduler job - *   * @job: scheduler job to init   * @entity: scheduler entity to use   * @owner: job owner for debugging @@ -572,43 +573,193 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext);   * Refer to drm_sched_entity_push_job() documentation   * for locking considerations.   * + * Drivers must make sure drm_sched_job_cleanup() if this function returns + * successfully, even when @job is aborted before drm_sched_job_arm() is called. + * + * WARNING: amdgpu abuses &drm_sched.ready to signal when the hardware + * has died, which can mean that there's no valid runqueue for a @entity. + * This function returns -ENOENT in this case (which probably should be -EIO as + * a more meanigful return value). + *   * Returns 0 for success, negative error code otherwise.   */  int drm_sched_job_init(struct drm_sched_job *job,  		       struct drm_sched_entity *entity,  		       void *owner)  { -	struct drm_gpu_scheduler *sched; -  	drm_sched_entity_select_rq(entity);  	if (!entity->rq)  		return -ENOENT; -	sched = entity->rq->sched; - -	job->sched = sched;  	job->entity = entity; -	job->s_priority = entity->rq - sched->sched_rq; -	job->s_fence = drm_sched_fence_create(entity, owner); +	job->s_fence = drm_sched_fence_alloc(entity, owner);  	if (!job->s_fence)  		return -ENOMEM; -	job->id = atomic64_inc_return(&sched->job_id_count);  	INIT_LIST_HEAD(&job->list); +	xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC); +  	return 0;  }  EXPORT_SYMBOL(drm_sched_job_init);  /** - * drm_sched_job_cleanup - clean up scheduler job resources + * drm_sched_job_arm - arm a scheduler job for execution + * @job: scheduler job to arm + * + * This arms a scheduler job for execution. Specifically it initializes the + * &drm_sched_job.s_fence of @job, so that it can be attached to struct dma_resv + * or other places that need to track the completion of this job. + * + * Refer to drm_sched_entity_push_job() documentation for locking + * considerations. + * + * This can only be called if drm_sched_job_init() succeeded. + */ +void drm_sched_job_arm(struct drm_sched_job *job) +{ +	struct drm_gpu_scheduler *sched; +	struct drm_sched_entity *entity = job->entity; + +	BUG_ON(!entity); + +	sched = entity->rq->sched; + +	job->sched = sched; +	job->s_priority = entity->rq - sched->sched_rq; +	job->id = atomic64_inc_return(&sched->job_id_count); + +	drm_sched_fence_init(job->s_fence, job->entity); +} +EXPORT_SYMBOL(drm_sched_job_arm); + +/** + * drm_sched_job_add_dependency - adds the fence as a job dependency + * @job: scheduler job to add the dependencies to + * @fence: the dma_fence to add to the list of dependencies. + * + * Note that @fence is consumed in both the success and error cases. + * + * Returns: + * 0 on success, or an error on failing to expand the array. + */ +int drm_sched_job_add_dependency(struct drm_sched_job *job, +				 struct dma_fence *fence) +{ +	struct dma_fence *entry; +	unsigned long index; +	u32 id = 0; +	int ret; + +	if (!fence) +		return 0; + +	/* Deduplicate if we already depend on a fence from the same context. +	 * This lets the size of the array of deps scale with the number of +	 * engines involved, rather than the number of BOs. +	 */ +	xa_for_each(&job->dependencies, index, entry) { +		if (entry->context != fence->context) +			continue; + +		if (dma_fence_is_later(fence, entry)) { +			dma_fence_put(entry); +			xa_store(&job->dependencies, index, fence, GFP_KERNEL); +		} else { +			dma_fence_put(fence); +		} +		return 0; +	} + +	ret = xa_alloc(&job->dependencies, &id, fence, xa_limit_32b, GFP_KERNEL); +	if (ret != 0) +		dma_fence_put(fence); + +	return ret; +} +EXPORT_SYMBOL(drm_sched_job_add_dependency); + +/** + * drm_sched_job_add_implicit_dependencies - adds implicit dependencies as job + *   dependencies + * @job: scheduler job to add the dependencies to + * @obj: the gem object to add new dependencies from. + * @write: whether the job might write the object (so we need to depend on + * shared fences in the reservation object).   * + * This should be called after drm_gem_lock_reservations() on your array of + * GEM objects used in the job but before updating the reservations with your + * own fences. + * + * Returns: + * 0 on success, or an error on failing to expand the array. + */ +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, +					    struct drm_gem_object *obj, +					    bool write) +{ +	int ret; +	struct dma_fence **fences; +	unsigned int i, fence_count; + +	if (!write) { +		struct dma_fence *fence = dma_resv_get_excl_unlocked(obj->resv); + +		return drm_sched_job_add_dependency(job, fence); +	} + +	ret = dma_resv_get_fences(obj->resv, NULL, &fence_count, &fences); +	if (ret || !fence_count) +		return ret; + +	for (i = 0; i < fence_count; i++) { +		ret = drm_sched_job_add_dependency(job, fences[i]); +		if (ret) +			break; +	} + +	for (; i < fence_count; i++) +		dma_fence_put(fences[i]); +	kfree(fences); +	return ret; +} +EXPORT_SYMBOL(drm_sched_job_add_implicit_dependencies); + + +/** + * drm_sched_job_cleanup - clean up scheduler job resources   * @job: scheduler job to clean up + * + * Cleans up the resources allocated with drm_sched_job_init(). + * + * Drivers should call this from their error unwind code if @job is aborted + * before drm_sched_job_arm() is called. + * + * After that point of no return @job is committed to be executed by the + * scheduler, and this function should be called from the + * &drm_sched_backend_ops.free_job callback.   */  void drm_sched_job_cleanup(struct drm_sched_job *job)  { -	dma_fence_put(&job->s_fence->finished); +	struct dma_fence *fence; +	unsigned long index; + +	if (kref_read(&job->s_fence->finished.refcount)) { +		/* drm_sched_job_arm() has been called */ +		dma_fence_put(&job->s_fence->finished); +	} else { +		/* aborted job before committing to run it */ +		drm_sched_fence_free(job->s_fence); +	} +  	job->s_fence = NULL; + +	xa_for_each(&job->dependencies, index, fence) { +		dma_fence_put(fence); +	} +	xa_destroy(&job->dependencies); +  }  EXPORT_SYMBOL(drm_sched_job_cleanup); @@ -676,15 +827,6 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)  {  	struct drm_sched_job *job, *next; -	/* -	 * Don't destroy jobs while the timeout worker is running  OR thread -	 * is being parked and hence assumed to not touch pending_list -	 */ -	if ((sched->timeout != MAX_SCHEDULE_TIMEOUT && -	    !cancel_delayed_work(&sched->work_tdr)) || -	    kthread_should_park()) -		return NULL; -  	spin_lock(&sched->job_list_lock);  	job = list_first_entry_or_null(&sched->pending_list, @@ -693,17 +835,21 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)  	if (job && dma_fence_is_signaled(&job->s_fence->finished)) {  		/* remove job from pending_list */  		list_del_init(&job->list); + +		/* cancel this job's TO timer */ +		cancel_delayed_work(&sched->work_tdr);  		/* make the scheduled timestamp more accurate */  		next = list_first_entry_or_null(&sched->pending_list,  						typeof(*next), list); -		if (next) + +		if (next) {  			next->s_fence->scheduled.timestamp =  				job->s_fence->finished.timestamp; - +			/* start TO timer for next job */ +			drm_sched_start_timeout(sched); +		}  	} else {  		job = NULL; -		/* queue timeout for next job */ -		drm_sched_start_timeout(sched);  	}  	spin_unlock(&sched->job_list_lock); @@ -791,11 +937,8 @@ static int drm_sched_main(void *param)  					  (entity = drm_sched_select_entity(sched))) ||  					 kthread_should_stop()); -		if (cleanup_job) { +		if (cleanup_job)  			sched->ops->free_job(cleanup_job); -			/* queue timeout for next job */ -			drm_sched_start_timeout(sched); -		}  		if (!entity)  			continue; |