Commit 78efe21b authored by Boris Brezillon's avatar Boris Brezillon
Browse files

drm/sched: Allow using a dedicated workqueue for the timeout/fault tdr



Mali Midgard/Bifrost GPUs have 3 hardware queues but only a global GPU
reset. This leads to extra complexity when we need to synchronize timeout
works with the reset work. One solution to address that is to have an
ordered workqueue at the driver level that will be used by the different
schedulers to queue their timeout work. Thanks to the serialization
provided by the ordered workqueue we are guaranteed that timeout
handlers are executed sequentially, and can thus easily reset the GPU
from the timeout handler without extra synchronization.

v5:
* Add a new paragraph to the timedout_job() method

v3:
* New patch

v4:
* Actually use the timeout_wq to queue the timeout work

Suggested-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: default avatarSteven Price <steven.price@arm.com>
Reviewed-by: default avatarLucas Stach <l.stach@pengutronix.de>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Cc: Qiang Yu <yuq825@gmail.com>
Cc: Emma Anholt <emma@anholt.net>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: "Christian König" <christian.koenig@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210630062751.2832545-3-boris.brezillon@collabora.com
parent 1fad1b7e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -490,7 +490,7 @@ int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring,

	r = drm_sched_init(&ring->sched, &amdgpu_sched_ops,
			   num_hw_submission, amdgpu_job_hang_limit,
			   timeout, sched_score, ring->name);
			   timeout, NULL, sched_score, ring->name);
	if (r) {
		DRM_ERROR("Failed to create scheduler on ring %s.\n",
			  ring->name);
+2 −1
Original line number Diff line number Diff line
@@ -190,7 +190,8 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu)

	ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops,
			     etnaviv_hw_jobs_limit, etnaviv_job_hang_limit,
			     msecs_to_jiffies(500), NULL, dev_name(gpu->dev));
			     msecs_to_jiffies(500), NULL, NULL,
			     dev_name(gpu->dev));
	if (ret)
		return ret;

+2 −1
Original line number Diff line number Diff line
@@ -508,7 +508,8 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name)
	INIT_WORK(&pipe->recover_work, lima_sched_recover_work);

	return drm_sched_init(&pipe->base, &lima_sched_ops, 1,
			      lima_job_hang_limit, msecs_to_jiffies(timeout),
			      lima_job_hang_limit,
			      msecs_to_jiffies(timeout), NULL,
			      NULL, name);
}

+2 −1
Original line number Diff line number Diff line
@@ -615,7 +615,8 @@ int panfrost_job_init(struct panfrost_device *pfdev)

		ret = drm_sched_init(&js->queue[j].sched,
				     &panfrost_sched_ops,
				     1, 0, msecs_to_jiffies(JOB_TIMEOUT_MS),
				     1, 0,
				     msecs_to_jiffies(JOB_TIMEOUT_MS), NULL,
				     NULL, "pan_js");
		if (ret) {
			dev_err(pfdev->dev, "Failed to create scheduler: %d.", ret);
+9 −5
Original line number Diff line number Diff line
@@ -232,7 +232,7 @@ static void drm_sched_start_timeout(struct drm_gpu_scheduler *sched)
{
	if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
	    !list_empty(&sched->pending_list))
		schedule_delayed_work(&sched->work_tdr, sched->timeout);
		queue_delayed_work(sched->timeout_wq, &sched->work_tdr, sched->timeout);
}

/**
@@ -244,7 +244,7 @@ static void drm_sched_start_timeout(struct drm_gpu_scheduler *sched)
 */
void drm_sched_fault(struct drm_gpu_scheduler *sched)
{
	mod_delayed_work(system_wq, &sched->work_tdr, 0);
	mod_delayed_work(sched->timeout_wq, &sched->work_tdr, 0);
}
EXPORT_SYMBOL(drm_sched_fault);

@@ -270,7 +270,7 @@ unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched)
	 * Modify the timeout to an arbitrarily large value. This also prevents
	 * the timeout to be restarted when new submissions arrive
	 */
	if (mod_delayed_work(system_wq, &sched->work_tdr, MAX_SCHEDULE_TIMEOUT)
	if (mod_delayed_work(sched->timeout_wq, &sched->work_tdr, MAX_SCHEDULE_TIMEOUT)
			&& time_after(sched_timeout, now))
		return sched_timeout - now;
	else
@@ -294,7 +294,7 @@ void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched,
	if (list_empty(&sched->pending_list))
		cancel_delayed_work(&sched->work_tdr);
	else
		mod_delayed_work(system_wq, &sched->work_tdr, remaining);
		mod_delayed_work(sched->timeout_wq, &sched->work_tdr, remaining);

	spin_unlock(&sched->job_list_lock);
}
@@ -847,6 +847,8 @@ 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
 * @timeout_wq: workqueue to use for timeout work. If NULL, the system_wq is
 *		used
 * @score: optional score atomic shared with other schedulers
 * @name: name used for debugging
 *
@@ -854,7 +856,8 @@ static int drm_sched_main(void *param)
 */
int drm_sched_init(struct drm_gpu_scheduler *sched,
		   const struct drm_sched_backend_ops *ops,
		   unsigned hw_submission, unsigned hang_limit, long timeout,
		   unsigned hw_submission, unsigned hang_limit,
		   long timeout, struct workqueue_struct *timeout_wq,
		   atomic_t *score, const char *name)
{
	int i, ret;
@@ -862,6 +865,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
	sched->hw_submission_limit = hw_submission;
	sched->name = name;
	sched->timeout = timeout;
	sched->timeout_wq = timeout_wq ? : system_wq;
	sched->hang_limit = hang_limit;
	sched->score = score ? score : &sched->_score;
	for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_COUNT; i++)
Loading