From a6a1f036c74e3d2a3a56b3140492c7c3ecb879f3 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Wed, 20 Jan 2021 15:09:59 -0500 Subject: drm/scheduler: Job timeout handler returns status (v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch does not change current behaviour. The driver's job timeout handler now returns status indicating back to the DRM layer whether the device (GPU) is no longer available, such as after it's been unplugged, or whether all is normal, i.e. current behaviour. All drivers which make use of the drm_sched_backend_ops' .timedout_job() callback have been accordingly renamed and return the would've-been default value of DRM_GPU_SCHED_STAT_NOMINAL to restart the task's timeout timer--this is the old behaviour, and is preserved by this patch. v2: Use enum as the status of a driver's job timeout callback method. v3: Return scheduler/device information, rather than task information. Cc: Alexander Deucher Cc: Andrey Grodzovsky Cc: Christian König Cc: Daniel Vetter Cc: Lucas Stach Cc: Russell King Cc: Christian Gmeiner Cc: Qiang Yu Cc: Rob Herring Cc: Tomeu Vizoso Cc: Steven Price Cc: Alyssa Rosenzweig Cc: Eric Anholt Reported-by: kernel test robot Signed-off-by: Luben Tuikov Acked-by: Alyssa Rosenzweig Acked-by: Christian König Acked-by: Steven Price Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/415095/ --- drivers/gpu/drm/scheduler/sched_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/gpu/drm/scheduler/sched_main.c') diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 92637b70c9bf..73fccc54268b 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -527,7 +527,7 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) EXPORT_SYMBOL(drm_sched_start); /** - * drm_sched_resubmit_jobs - helper to relunch job from pending ring list + * drm_sched_resubmit_jobs - helper to relaunch jobs from the pending list * * @sched: scheduler instance * @@ -561,8 +561,6 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) } else { s_job->s_fence->parent = fence; } - - } } EXPORT_SYMBOL(drm_sched_resubmit_jobs); -- cgit From f2f12eb9c32bc7a714276d8862efac2e7c41bcbe Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 2 Feb 2021 12:40:01 +0100 Subject: drm/scheduler: provide scheduler score externally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow multiple schedulers to share the load balancing score. This is useful when one engine has different hw rings. Signed-off-by: Christian König Reviewed-and-Tested-by: Leo Liu Link: https://patchwork.freedesktop.org/patch/msgid/20210204144405.2737-1-christian.koenig@amd.com --- drivers/gpu/drm/scheduler/sched_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/scheduler/sched_main.c') diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 73fccc54268b..d82a7ebf6099 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -91,7 +91,7 @@ void drm_sched_rq_add_entity(struct drm_sched_rq *rq, if (!list_empty(&entity->list)) return; spin_lock(&rq->lock); - atomic_inc(&rq->sched->score); + atomic_inc(rq->sched->score); list_add_tail(&entity->list, &rq->entities); spin_unlock(&rq->lock); } @@ -110,7 +110,7 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, if (list_empty(&entity->list)) return; spin_lock(&rq->lock); - atomic_dec(&rq->sched->score); + atomic_dec(rq->sched->score); list_del_init(&entity->list); if (rq->current_entity == entity) rq->current_entity = NULL; @@ -173,7 +173,7 @@ static void drm_sched_job_done(struct drm_sched_job *s_job) struct drm_gpu_scheduler *sched = s_fence->sched; atomic_dec(&sched->hw_rq_count); - atomic_dec(&sched->score); + atomic_dec(sched->score); trace_drm_sched_process_job(s_fence); @@ -732,7 +732,7 @@ drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, continue; } - num_score = atomic_read(&sched->score); + num_score = atomic_read(sched->score); if (num_score < min_score) { min_score = num_score; picked_sched = sched; @@ -842,16 +842,15 @@ static int drm_sched_main(void *param) * @hw_submission: number of hw submissions that can be in flight * @hang_limit: number of times to allow a job to hang before dropping it * @timeout: timeout value in jiffies for the scheduler + * @score: optional score atomic shared with other schedulers * @name: name used for debugging * * Return 0 on success, otherwise error code. */ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_backend_ops *ops, - unsigned hw_submission, - unsigned hang_limit, - long timeout, - const char *name) + unsigned hw_submission, unsigned hang_limit, long timeout, + atomic_t *score, const char *name) { int i, ret; sched->ops = ops; @@ -859,6 +858,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, sched->name = name; sched->timeout = timeout; sched->hang_limit = hang_limit; + sched->score = score ? score : &sched->_score; for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_COUNT; i++) drm_sched_rq_init(sched, &sched->sched_rq[i]); @@ -868,7 +868,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, spin_lock_init(&sched->job_list_lock); atomic_set(&sched->hw_rq_count, 0); INIT_DELAYED_WORK(&sched->work_tdr, drm_sched_job_timedout); - atomic_set(&sched->score, 0); + atomic_set(&sched->_score, 0); atomic64_set(&sched->job_id_count, 0); /* Each scheduler will run on a seperate kernel thread */ -- cgit From e6c6338f393b74ac0b303d567bb918b44ae7ad75 Mon Sep 17 00:00:00 2001 From: Jack Zhang Date: Mon, 8 Mar 2021 12:41:27 +0800 Subject: drm/amd/amdgpu implement tdr advanced mode [Why] Previous tdr design treats the first job in job_timeout as the bad job. But sometimes a later bad compute job can block a good gfx job and cause an unexpected gfx job timeout because gfx and compute ring share internal GC HW mutually. [How] This patch implements an advanced tdr mode.It involves an additinal synchronous pre-resubmit step(Step0 Resubmit) before normal resubmit step in order to find the real bad job. 1. At Step0 Resubmit stage, it synchronously submits and pends for the first job being signaled. If it gets timeout, we identify it as guilty and do hw reset. After that, we would do the normal resubmit step to resubmit left jobs. 2. For whole gpu reset(vram lost), do resubmit as the old way. v2: squash in build fix (Alex) Signed-off-by: Jack Zhang Reviewed-by: Andrey Grodzovsky Signed-off-by: Alex Deucher --- drivers/gpu/drm/scheduler/sched_main.c | 103 +++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 32 deletions(-) (limited to 'drivers/gpu/drm/scheduler/sched_main.c') diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index d82a7ebf6099..92d8de24d0a1 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -361,40 +361,16 @@ static void drm_sched_job_timedout(struct work_struct *work) */ void drm_sched_increase_karma(struct drm_sched_job *bad) { - int i; - struct drm_sched_entity *tmp; - struct drm_sched_entity *entity; - struct drm_gpu_scheduler *sched = bad->sched; - - /* don't increase @bad's karma if it's from KERNEL RQ, - * because sometimes GPU hang would cause kernel jobs (like VM updating jobs) - * corrupt but keep in mind that kernel jobs always considered good. - */ - if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) { - atomic_inc(&bad->karma); - for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; - i++) { - struct drm_sched_rq *rq = &sched->sched_rq[i]; - - spin_lock(&rq->lock); - list_for_each_entry_safe(entity, tmp, &rq->entities, list) { - if (bad->s_fence->scheduled.context == - entity->fence_context) { - if (atomic_read(&bad->karma) > - bad->sched->hang_limit) - if (entity->guilty) - atomic_set(entity->guilty, 1); - break; - } - } - spin_unlock(&rq->lock); - if (&entity->list != &rq->entities) - break; - } - } + drm_sched_increase_karma_ext(bad, 1); } EXPORT_SYMBOL(drm_sched_increase_karma); +void drm_sched_reset_karma(struct drm_sched_job *bad) +{ + drm_sched_increase_karma_ext(bad, 0); +} +EXPORT_SYMBOL(drm_sched_reset_karma); + /** * drm_sched_stop - stop the scheduler * @@ -533,15 +509,32 @@ EXPORT_SYMBOL(drm_sched_start); * */ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) +{ + drm_sched_resubmit_jobs_ext(sched, INT_MAX); +} +EXPORT_SYMBOL(drm_sched_resubmit_jobs); + +/** + * drm_sched_resubmit_jobs_ext - helper to relunch certain number of jobs from mirror ring list + * + * @sched: scheduler instance + * @max: job numbers to relaunch + * + */ +void drm_sched_resubmit_jobs_ext(struct drm_gpu_scheduler *sched, int max) { struct drm_sched_job *s_job, *tmp; uint64_t guilty_context; bool found_guilty = false; struct dma_fence *fence; + int i = 0; list_for_each_entry_safe(s_job, tmp, &sched->pending_list, list) { struct drm_sched_fence *s_fence = s_job->s_fence; + if (i >= max) + break; + if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) { found_guilty = true; guilty_context = s_job->s_fence->scheduled.context; @@ -552,6 +545,7 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) dma_fence_put(s_job->s_fence->parent); fence = sched->ops->run_job(s_job); + i++; if (IS_ERR_OR_NULL(fence)) { if (IS_ERR(fence)) @@ -563,7 +557,7 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) } } } -EXPORT_SYMBOL(drm_sched_resubmit_jobs); +EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext); /** * drm_sched_job_init - init a scheduler job @@ -903,3 +897,48 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) sched->ready = false; } EXPORT_SYMBOL(drm_sched_fini); + +/** + * drm_sched_increase_karma_ext - Update sched_entity guilty flag + * + * @bad: The job guilty of time out + * @type: type for increase/reset karma + * + */ +void drm_sched_increase_karma_ext(struct drm_sched_job *bad, int type) +{ + int i; + struct drm_sched_entity *tmp; + struct drm_sched_entity *entity; + struct drm_gpu_scheduler *sched = bad->sched; + + /* don't change @bad's karma if it's from KERNEL RQ, + * because sometimes GPU hang would cause kernel jobs (like VM updating jobs) + * corrupt but keep in mind that kernel jobs always considered good. + */ + if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) { + if (type == 0) + atomic_set(&bad->karma, 0); + else if (type == 1) + atomic_inc(&bad->karma); + + for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; + i++) { + struct drm_sched_rq *rq = &sched->sched_rq[i]; + + spin_lock(&rq->lock); + list_for_each_entry_safe(entity, tmp, &rq->entities, list) { + if (bad->s_fence->scheduled.context == + entity->fence_context) { + if (entity->guilty) + atomic_set(entity->guilty, type); + break; + } + } + spin_unlock(&rq->lock); + if (&entity->list != &rq->entities) + break; + } + } +} +EXPORT_SYMBOL(drm_sched_increase_karma_ext); -- cgit