Message ID | 20210805104705.862416-5-daniel.vetter@ffwll.ch (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/sched dependency handling and implicit sync fixes | expand |
Am 05.08.21 um 12:46 schrieb Daniel Vetter: > Instead of just a callback we can just glue in the gem helpers that > panfrost, v3d and lima currently use. There's really not that many > ways to skin this cat. > > v2/3: Rebased. > > v4: Repaint this shed. The functions are now called _add_dependency() > and _add_implicit_dependency() > > Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> (v3) > Reviewed-by: Steven Price <steven.price@arm.com> (v1) > Acked-by: Melissa Wen <mwen@igalia.com> > Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Reviewed-by: Christian König <christian.koenig@amd.com> > Cc: David Airlie <airlied@linux.ie> > Cc: Daniel Vetter <daniel@ffwll.ch> > Cc: Sumit Semwal <sumit.semwal@linaro.org> > Cc: "Christian König" <christian.koenig@amd.com> > Cc: Andrey Grodzovsky <andrey.grodzovsky@amd.com> > Cc: Lee Jones <lee.jones@linaro.org> > Cc: Nirmoy Das <nirmoy.aiemd@gmail.com> > Cc: Boris Brezillon <boris.brezillon@collabora.com> > Cc: Luben Tuikov <luben.tuikov@amd.com> > Cc: Alex Deucher <alexander.deucher@amd.com> > Cc: linux-media@vger.kernel.org > Cc: linaro-mm-sig@lists.linaro.org > --- > drivers/gpu/drm/scheduler/sched_entity.c | 18 +++- > drivers/gpu/drm/scheduler/sched_main.c | 104 +++++++++++++++++++++++ > include/drm/gpu_scheduler.h | 33 ++++++- > 3 files changed, 149 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c > index 89e3f6eaf519..381fbf462ea7 100644 > --- a/drivers/gpu/drm/scheduler/sched_entity.c > +++ b/drivers/gpu/drm/scheduler/sched_entity.c > @@ -211,6 +211,19 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, > job->sched->ops->free_job(job); > } > > +static struct dma_fence * > +drm_sched_job_dependency(struct drm_sched_job *job, > + struct drm_sched_entity *entity) > +{ > + if (!xa_empty(&job->dependencies)) > + return xa_erase(&job->dependencies, job->last_dependency++); > + > + if (job->sched->ops->dependency) > + return job->sched->ops->dependency(job, entity); > + > + return NULL; > +} > + > /** > * drm_sched_entity_kill_jobs - Make sure all remaining jobs are killed > * > @@ -229,7 +242,7 @@ static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) > struct drm_sched_fence *s_fence = job->s_fence; > > /* Wait for all dependencies to avoid data corruptions */ > - while ((f = job->sched->ops->dependency(job, entity))) > + while ((f = drm_sched_job_dependency(job, entity))) > dma_fence_wait(f, false); > > drm_sched_fence_scheduled(s_fence); > @@ -419,7 +432,6 @@ static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity) > */ > struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) > { > - struct drm_gpu_scheduler *sched = entity->rq->sched; > struct drm_sched_job *sched_job; > > sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); > @@ -427,7 +439,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) > return NULL; > > while ((entity->dependency = > - sched->ops->dependency(sched_job, entity))) { > + drm_sched_job_dependency(sched_job, entity))) { > trace_drm_sched_job_wait_dep(sched_job, entity->dependency); > > if (drm_sched_entity_add_dependency_cb(entity)) > diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c > index 454cb6164bdc..f77456929139 100644 > --- a/drivers/gpu/drm/scheduler/sched_main.c > +++ b/drivers/gpu/drm/scheduler/sched_main.c > @@ -603,6 +603,8 @@ int drm_sched_job_init(struct drm_sched_job *job, > > INIT_LIST_HEAD(&job->list); > > + xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC); > + > return 0; > } > EXPORT_SYMBOL(drm_sched_job_init); > @@ -637,6 +639,99 @@ void drm_sched_job_arm(struct drm_sched_job *job) > } > 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 > @@ -652,6 +747,9 @@ EXPORT_SYMBOL(drm_sched_job_arm); > */ > void drm_sched_job_cleanup(struct drm_sched_job *job) > { > + 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); > @@ -661,6 +759,12 @@ void drm_sched_job_cleanup(struct drm_sched_job *job) > } > > 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); > > diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h > index 83afc3aa8e2f..a47946f904b6 100644 > --- a/include/drm/gpu_scheduler.h > +++ b/include/drm/gpu_scheduler.h > @@ -27,9 +27,12 @@ > #include <drm/spsc_queue.h> > #include <linux/dma-fence.h> > #include <linux/completion.h> > +#include <linux/xarray.h> > > #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) > > +struct drm_gem_object; > + > struct drm_gpu_scheduler; > struct drm_sched_rq; > > @@ -198,6 +201,17 @@ struct drm_sched_job { > enum drm_sched_priority s_priority; > struct drm_sched_entity *entity; > struct dma_fence_cb cb; > + /** > + * @dependencies: > + * > + * Contains the dependencies as struct dma_fence for this job, see > + * drm_sched_job_add_dependency() and > + * drm_sched_job_add_implicit_dependencies(). > + */ > + struct xarray dependencies; > + > + /** @last_dependency: tracks @dependencies as they signal */ > + unsigned long last_dependency; > }; > > static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, > @@ -220,9 +234,15 @@ enum drm_gpu_sched_stat { > */ > struct drm_sched_backend_ops { > /** > - * @dependency: Called when the scheduler is considering scheduling > - * this job next, to get another struct dma_fence for this job to > - * block on. Once it returns NULL, run_job() may be called. > + * @dependency: > + * > + * Called when the scheduler is considering scheduling this job next, to > + * get another struct dma_fence for this job to block on. Once it > + * returns NULL, run_job() may be called. > + * > + * If a driver exclusively uses drm_sched_job_add_dependency() and > + * drm_sched_job_add_implicit_dependencies() this can be ommitted and > + * left as NULL. > */ > struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, > struct drm_sched_entity *s_entity); > @@ -349,6 +369,13 @@ int drm_sched_job_init(struct drm_sched_job *job, > struct drm_sched_entity *entity, > void *owner); > void drm_sched_job_arm(struct drm_sched_job *job); > +int drm_sched_job_add_dependency(struct drm_sched_job *job, > + struct dma_fence *fence); > +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, > + struct drm_gem_object *obj, > + bool write); > + > + > void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, > struct drm_gpu_scheduler **sched_list, > unsigned int num_sched_list);
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 89e3f6eaf519..381fbf462ea7 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -211,6 +211,19 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, job->sched->ops->free_job(job); } +static struct dma_fence * +drm_sched_job_dependency(struct drm_sched_job *job, + struct drm_sched_entity *entity) +{ + if (!xa_empty(&job->dependencies)) + return xa_erase(&job->dependencies, job->last_dependency++); + + if (job->sched->ops->dependency) + return job->sched->ops->dependency(job, entity); + + return NULL; +} + /** * drm_sched_entity_kill_jobs - Make sure all remaining jobs are killed * @@ -229,7 +242,7 @@ static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) struct drm_sched_fence *s_fence = job->s_fence; /* Wait for all dependencies to avoid data corruptions */ - while ((f = job->sched->ops->dependency(job, entity))) + while ((f = drm_sched_job_dependency(job, entity))) dma_fence_wait(f, false); drm_sched_fence_scheduled(s_fence); @@ -419,7 +432,6 @@ static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity) */ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) { - struct drm_gpu_scheduler *sched = entity->rq->sched; struct drm_sched_job *sched_job; sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); @@ -427,7 +439,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) return NULL; while ((entity->dependency = - sched->ops->dependency(sched_job, entity))) { + drm_sched_job_dependency(sched_job, entity))) { trace_drm_sched_job_wait_dep(sched_job, entity->dependency); if (drm_sched_entity_add_dependency_cb(entity)) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 454cb6164bdc..f77456929139 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -603,6 +603,8 @@ int drm_sched_job_init(struct drm_sched_job *job, INIT_LIST_HEAD(&job->list); + xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC); + return 0; } EXPORT_SYMBOL(drm_sched_job_init); @@ -637,6 +639,99 @@ void drm_sched_job_arm(struct drm_sched_job *job) } 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 @@ -652,6 +747,9 @@ EXPORT_SYMBOL(drm_sched_job_arm); */ void drm_sched_job_cleanup(struct drm_sched_job *job) { + 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); @@ -661,6 +759,12 @@ void drm_sched_job_cleanup(struct drm_sched_job *job) } 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); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 83afc3aa8e2f..a47946f904b6 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -27,9 +27,12 @@ #include <drm/spsc_queue.h> #include <linux/dma-fence.h> #include <linux/completion.h> +#include <linux/xarray.h> #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) +struct drm_gem_object; + struct drm_gpu_scheduler; struct drm_sched_rq; @@ -198,6 +201,17 @@ struct drm_sched_job { enum drm_sched_priority s_priority; struct drm_sched_entity *entity; struct dma_fence_cb cb; + /** + * @dependencies: + * + * Contains the dependencies as struct dma_fence for this job, see + * drm_sched_job_add_dependency() and + * drm_sched_job_add_implicit_dependencies(). + */ + struct xarray dependencies; + + /** @last_dependency: tracks @dependencies as they signal */ + unsigned long last_dependency; }; static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, @@ -220,9 +234,15 @@ enum drm_gpu_sched_stat { */ struct drm_sched_backend_ops { /** - * @dependency: Called when the scheduler is considering scheduling - * this job next, to get another struct dma_fence for this job to - * block on. Once it returns NULL, run_job() may be called. + * @dependency: + * + * Called when the scheduler is considering scheduling this job next, to + * get another struct dma_fence for this job to block on. Once it + * returns NULL, run_job() may be called. + * + * If a driver exclusively uses drm_sched_job_add_dependency() and + * drm_sched_job_add_implicit_dependencies() this can be ommitted and + * left as NULL. */ struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, struct drm_sched_entity *s_entity); @@ -349,6 +369,13 @@ int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, void *owner); void drm_sched_job_arm(struct drm_sched_job *job); +int drm_sched_job_add_dependency(struct drm_sched_job *job, + struct dma_fence *fence); +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, + struct drm_gem_object *obj, + bool write); + + void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, struct drm_gpu_scheduler **sched_list, unsigned int num_sched_list);