drm/sched: Convert the GPU scheduler to variable number of run-queues

The GPU scheduler has now a variable number of run-queues, which are set up at
drm_sched_init() time. This way, each driver announces how many run-queues it
requires (supports) per each GPU scheduler it creates. Note, that run-queues
correspond to scheduler "priorities", thus if the number of run-queues is set
to 1 at drm_sched_init(), then that scheduler supports a single run-queue,
i.e. single "priority". If a driver further sets a single entity per
run-queue, then this creates a 1-to-1 correspondence between a scheduler and
a scheduled entity.

Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Russell King <linux+etnaviv@armlinux.org.uk>
Cc: Qiang Yu <yuq825@gmail.com>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Abhinav Kumar <quic_abhinavk@quicinc.com>
Cc: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Cc: Danilo Krummrich <dakr@redhat.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Emma Anholt <emma@anholt.net>
Cc: etnaviv@lists.freedesktop.org
Cc: lima@lists.freedesktop.org
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: nouveau@lists.freedesktop.org
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Christian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/r/20231023032251.164775-1-luben.tuikov@amd.com
This commit is contained in:
Luben Tuikov 2023-10-14 21:15:35 -04:00
parent 3ddba96b0d
commit 56e449603f
11 changed files with 98 additions and 25 deletions

View File

@ -2280,6 +2280,7 @@ static int amdgpu_device_init_schedulers(struct amdgpu_device *adev)
} }
r = drm_sched_init(&ring->sched, &amdgpu_sched_ops, r = drm_sched_init(&ring->sched, &amdgpu_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
ring->num_hw_submission, 0, ring->num_hw_submission, 0,
timeout, adev->reset_domain->wq, timeout, adev->reset_domain->wq,
ring->sched_score, ring->name, ring->sched_score, ring->name,

View File

@ -325,8 +325,8 @@ void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched)
int i; int i;
/* Signal all jobs not yet scheduled */ /* Signal all jobs not yet scheduled */
for (i = DRM_SCHED_PRIORITY_COUNT - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) { for (i = sched->num_rqs - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
struct drm_sched_rq *rq = &sched->sched_rq[i]; struct drm_sched_rq *rq = sched->sched_rq[i];
spin_lock(&rq->lock); spin_lock(&rq->lock);
list_for_each_entry(s_entity, &rq->entities, list) { list_for_each_entry(s_entity, &rq->entities, list) {
while ((s_job = to_drm_sched_job(spsc_queue_pop(&s_entity->job_queue)))) { while ((s_job = to_drm_sched_job(spsc_queue_pop(&s_entity->job_queue)))) {

View File

@ -135,6 +135,7 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu)
int ret; int ret;
ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops, ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
etnaviv_hw_jobs_limit, etnaviv_job_hang_limit, etnaviv_hw_jobs_limit, etnaviv_job_hang_limit,
msecs_to_jiffies(500), NULL, NULL, msecs_to_jiffies(500), NULL, NULL,
dev_name(gpu->dev), gpu->dev); dev_name(gpu->dev), gpu->dev);

View File

@ -488,7 +488,9 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name)
INIT_WORK(&pipe->recover_work, lima_sched_recover_work); INIT_WORK(&pipe->recover_work, lima_sched_recover_work);
return drm_sched_init(&pipe->base, &lima_sched_ops, 1, return drm_sched_init(&pipe->base, &lima_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
1,
lima_job_hang_limit, lima_job_hang_limit,
msecs_to_jiffies(timeout), NULL, msecs_to_jiffies(timeout), NULL,
NULL, name, pipe->ldev->dev); NULL, name, pipe->ldev->dev);

View File

@ -95,8 +95,9 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
sched_timeout = MAX_SCHEDULE_TIMEOUT; sched_timeout = MAX_SCHEDULE_TIMEOUT;
ret = drm_sched_init(&ring->sched, &msm_sched_ops, ret = drm_sched_init(&ring->sched, &msm_sched_ops,
num_hw_submissions, 0, sched_timeout, DRM_SCHED_PRIORITY_COUNT,
NULL, NULL, to_msm_bo(ring->bo)->name, gpu->dev->dev); num_hw_submissions, 0, sched_timeout,
NULL, NULL, to_msm_bo(ring->bo)->name, gpu->dev->dev);
if (ret) { if (ret) {
goto fail; goto fail;
} }

View File

@ -430,6 +430,7 @@ int nouveau_sched_init(struct nouveau_drm *drm)
return -ENOMEM; return -ENOMEM;
return drm_sched_init(sched, &nouveau_sched_ops, return drm_sched_init(sched, &nouveau_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
NOUVEAU_SCHED_HW_SUBMISSIONS, 0, job_hang_limit, NOUVEAU_SCHED_HW_SUBMISSIONS, 0, job_hang_limit,
NULL, NULL, "nouveau_sched", drm->dev->dev); NULL, NULL, "nouveau_sched", drm->dev->dev);
} }

View File

@ -853,6 +853,7 @@ int panfrost_job_init(struct panfrost_device *pfdev)
ret = drm_sched_init(&js->queue[j].sched, ret = drm_sched_init(&js->queue[j].sched,
&panfrost_sched_ops, &panfrost_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
nentries, 0, nentries, 0,
msecs_to_jiffies(JOB_TIMEOUT_MS), msecs_to_jiffies(JOB_TIMEOUT_MS),
pfdev->reset.wq, pfdev->reset.wq,

View File

@ -75,8 +75,20 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
RCU_INIT_POINTER(entity->last_scheduled, NULL); RCU_INIT_POINTER(entity->last_scheduled, NULL);
RB_CLEAR_NODE(&entity->rb_tree_node); RB_CLEAR_NODE(&entity->rb_tree_node);
if(num_sched_list) if (!sched_list[0]->sched_rq) {
entity->rq = &sched_list[0]->sched_rq[entity->priority]; /* Warn drivers not to do this and to fix their DRM
* calling order.
*/
pr_warn("%s: called with uninitialized scheduler\n", __func__);
} else if (num_sched_list) {
/* The "priority" of an entity cannot exceed the number
* of run-queues of a scheduler.
*/
if (entity->priority >= sched_list[0]->num_rqs)
entity->priority = max_t(u32, sched_list[0]->num_rqs,
DRM_SCHED_PRIORITY_MIN);
entity->rq = sched_list[0]->sched_rq[entity->priority];
}
init_completion(&entity->entity_idle); init_completion(&entity->entity_idle);
@ -533,7 +545,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
spin_lock(&entity->rq_lock); spin_lock(&entity->rq_lock);
sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list); sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list);
rq = sched ? &sched->sched_rq[entity->priority] : NULL; rq = sched ? sched->sched_rq[entity->priority] : NULL;
if (rq != entity->rq) { if (rq != entity->rq) {
drm_sched_rq_remove_entity(entity->rq, entity); drm_sched_rq_remove_entity(entity->rq, entity);
entity->rq = rq; entity->rq = rq;

View File

@ -632,8 +632,14 @@ int drm_sched_job_init(struct drm_sched_job *job,
struct drm_sched_entity *entity, struct drm_sched_entity *entity,
void *owner) void *owner)
{ {
if (!entity->rq) if (!entity->rq) {
/* This will most likely be followed by missing frames
* or worse--a blank screen--leave a trail in the
* logs, so this can be debugged easier.
*/
drm_err(job->sched, "%s: entity has no rq!\n", __func__);
return -ENOENT; return -ENOENT;
}
job->entity = entity; job->entity = entity;
job->s_fence = drm_sched_fence_alloc(entity, owner); job->s_fence = drm_sched_fence_alloc(entity, owner);
@ -671,7 +677,7 @@ void drm_sched_job_arm(struct drm_sched_job *job)
sched = entity->rq->sched; sched = entity->rq->sched;
job->sched = sched; job->sched = sched;
job->s_priority = entity->rq - sched->sched_rq; job->s_priority = entity->priority;
job->id = atomic64_inc_return(&sched->job_id_count); job->id = atomic64_inc_return(&sched->job_id_count);
drm_sched_fence_init(job->s_fence, job->entity); drm_sched_fence_init(job->s_fence, job->entity);
@ -888,10 +894,10 @@ drm_sched_select_entity(struct drm_gpu_scheduler *sched)
return NULL; return NULL;
/* Kernel run queue has higher priority than normal run queue*/ /* Kernel run queue has higher priority than normal run queue*/
for (i = DRM_SCHED_PRIORITY_COUNT - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) { for (i = sched->num_rqs - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
entity = drm_sched_policy == DRM_SCHED_POLICY_FIFO ? entity = drm_sched_policy == DRM_SCHED_POLICY_FIFO ?
drm_sched_rq_select_entity_fifo(&sched->sched_rq[i]) : drm_sched_rq_select_entity_fifo(sched->sched_rq[i]) :
drm_sched_rq_select_entity_rr(&sched->sched_rq[i]); drm_sched_rq_select_entity_rr(sched->sched_rq[i]);
if (entity) if (entity)
break; break;
} }
@ -1071,6 +1077,7 @@ static int drm_sched_main(void *param)
* *
* @sched: scheduler instance * @sched: scheduler instance
* @ops: backend operations for this scheduler * @ops: backend operations for this scheduler
* @num_rqs: number of runqueues, one for each priority, up to DRM_SCHED_PRIORITY_COUNT
* @hw_submission: number of hw submissions that can be in flight * @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 * @hang_limit: number of times to allow a job to hang before dropping it
* @timeout: timeout value in jiffies for the scheduler * @timeout: timeout value in jiffies for the scheduler
@ -1084,11 +1091,12 @@ static int drm_sched_main(void *param)
*/ */
int drm_sched_init(struct drm_gpu_scheduler *sched, int drm_sched_init(struct drm_gpu_scheduler *sched,
const struct drm_sched_backend_ops *ops, const struct drm_sched_backend_ops *ops,
unsigned hw_submission, unsigned hang_limit, u32 num_rqs, uint32_t hw_submission, unsigned int hang_limit,
long timeout, struct workqueue_struct *timeout_wq, long timeout, struct workqueue_struct *timeout_wq,
atomic_t *score, const char *name, struct device *dev) atomic_t *score, const char *name, struct device *dev)
{ {
int i, ret; int i, ret;
sched->ops = ops; sched->ops = ops;
sched->hw_submission_limit = hw_submission; sched->hw_submission_limit = hw_submission;
sched->name = name; sched->name = name;
@ -1097,8 +1105,36 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
sched->hang_limit = hang_limit; sched->hang_limit = hang_limit;
sched->score = score ? score : &sched->_score; sched->score = score ? score : &sched->_score;
sched->dev = dev; sched->dev = dev;
for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_COUNT; i++)
drm_sched_rq_init(sched, &sched->sched_rq[i]); if (num_rqs > DRM_SCHED_PRIORITY_COUNT) {
/* This is a gross violation--tell drivers what the problem is.
*/
drm_err(sched, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n",
__func__);
return -EINVAL;
} else if (sched->sched_rq) {
/* Not an error, but warn anyway so drivers can
* fine-tune their DRM calling order, and return all
* is good.
*/
drm_warn(sched, "%s: scheduler already initialized!\n", __func__);
return 0;
}
sched->sched_rq = kmalloc_array(num_rqs, sizeof(*sched->sched_rq),
GFP_KERNEL | __GFP_ZERO);
if (!sched->sched_rq) {
drm_err(sched, "%s: out of memory for sched_rq\n", __func__);
return -ENOMEM;
}
sched->num_rqs = num_rqs;
ret = -ENOMEM;
for (i = DRM_SCHED_PRIORITY_MIN; i < sched->num_rqs; i++) {
sched->sched_rq[i] = kzalloc(sizeof(*sched->sched_rq[i]), GFP_KERNEL);
if (!sched->sched_rq[i])
goto Out_unroll;
drm_sched_rq_init(sched, sched->sched_rq[i]);
}
init_waitqueue_head(&sched->wake_up_worker); init_waitqueue_head(&sched->wake_up_worker);
init_waitqueue_head(&sched->job_scheduled); init_waitqueue_head(&sched->job_scheduled);
@ -1115,11 +1151,18 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
ret = PTR_ERR(sched->thread); ret = PTR_ERR(sched->thread);
sched->thread = NULL; sched->thread = NULL;
DRM_DEV_ERROR(sched->dev, "Failed to create scheduler for %s.\n", name); DRM_DEV_ERROR(sched->dev, "Failed to create scheduler for %s.\n", name);
return ret; goto Out_unroll;
} }
sched->ready = true; sched->ready = true;
return 0; return 0;
Out_unroll:
for (--i ; i >= DRM_SCHED_PRIORITY_MIN; i--)
kfree(sched->sched_rq[i]);
kfree(sched->sched_rq);
sched->sched_rq = NULL;
drm_err(sched, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
return ret;
} }
EXPORT_SYMBOL(drm_sched_init); EXPORT_SYMBOL(drm_sched_init);
@ -1138,8 +1181,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
if (sched->thread) if (sched->thread)
kthread_stop(sched->thread); kthread_stop(sched->thread);
for (i = DRM_SCHED_PRIORITY_COUNT - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) { for (i = sched->num_rqs - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
struct drm_sched_rq *rq = &sched->sched_rq[i]; struct drm_sched_rq *rq = sched->sched_rq[i];
spin_lock(&rq->lock); spin_lock(&rq->lock);
list_for_each_entry(s_entity, &rq->entities, list) list_for_each_entry(s_entity, &rq->entities, list)
@ -1150,7 +1193,7 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
*/ */
s_entity->stopped = true; s_entity->stopped = true;
spin_unlock(&rq->lock); spin_unlock(&rq->lock);
kfree(sched->sched_rq[i]);
} }
/* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */ /* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */
@ -1160,6 +1203,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
cancel_delayed_work_sync(&sched->work_tdr); cancel_delayed_work_sync(&sched->work_tdr);
sched->ready = false; sched->ready = false;
kfree(sched->sched_rq);
sched->sched_rq = NULL;
} }
EXPORT_SYMBOL(drm_sched_fini); EXPORT_SYMBOL(drm_sched_fini);
@ -1186,9 +1231,10 @@ void drm_sched_increase_karma(struct drm_sched_job *bad)
if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) { if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
atomic_inc(&bad->karma); atomic_inc(&bad->karma);
for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; for (i = DRM_SCHED_PRIORITY_MIN;
i < min_t(typeof(sched->num_rqs), sched->num_rqs, DRM_SCHED_PRIORITY_KERNEL);
i++) { i++) {
struct drm_sched_rq *rq = &sched->sched_rq[i]; struct drm_sched_rq *rq = sched->sched_rq[i];
spin_lock(&rq->lock); spin_lock(&rq->lock);
list_for_each_entry_safe(entity, tmp, &rq->entities, list) { list_for_each_entry_safe(entity, tmp, &rq->entities, list) {

View File

@ -389,6 +389,7 @@ v3d_sched_init(struct v3d_dev *v3d)
ret = drm_sched_init(&v3d->queue[V3D_BIN].sched, ret = drm_sched_init(&v3d->queue[V3D_BIN].sched,
&v3d_bin_sched_ops, &v3d_bin_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
hw_jobs_limit, job_hang_limit, hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL, msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_bin", v3d->drm.dev); NULL, "v3d_bin", v3d->drm.dev);
@ -397,6 +398,7 @@ v3d_sched_init(struct v3d_dev *v3d)
ret = drm_sched_init(&v3d->queue[V3D_RENDER].sched, ret = drm_sched_init(&v3d->queue[V3D_RENDER].sched,
&v3d_render_sched_ops, &v3d_render_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
hw_jobs_limit, job_hang_limit, hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL, msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_render", v3d->drm.dev); NULL, "v3d_render", v3d->drm.dev);
@ -405,6 +407,7 @@ v3d_sched_init(struct v3d_dev *v3d)
ret = drm_sched_init(&v3d->queue[V3D_TFU].sched, ret = drm_sched_init(&v3d->queue[V3D_TFU].sched,
&v3d_tfu_sched_ops, &v3d_tfu_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
hw_jobs_limit, job_hang_limit, hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL, msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_tfu", v3d->drm.dev); NULL, "v3d_tfu", v3d->drm.dev);
@ -414,6 +417,7 @@ v3d_sched_init(struct v3d_dev *v3d)
if (v3d_has_csd(v3d)) { if (v3d_has_csd(v3d)) {
ret = drm_sched_init(&v3d->queue[V3D_CSD].sched, ret = drm_sched_init(&v3d->queue[V3D_CSD].sched,
&v3d_csd_sched_ops, &v3d_csd_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
hw_jobs_limit, job_hang_limit, hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL, msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_csd", v3d->drm.dev); NULL, "v3d_csd", v3d->drm.dev);
@ -422,6 +426,7 @@ v3d_sched_init(struct v3d_dev *v3d)
ret = drm_sched_init(&v3d->queue[V3D_CACHE_CLEAN].sched, ret = drm_sched_init(&v3d->queue[V3D_CACHE_CLEAN].sched,
&v3d_cache_clean_sched_ops, &v3d_cache_clean_sched_ops,
DRM_SCHED_PRIORITY_COUNT,
hw_jobs_limit, job_hang_limit, hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL, msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_cache_clean", v3d->drm.dev); NULL, "v3d_cache_clean", v3d->drm.dev);

View File

@ -472,7 +472,9 @@ struct drm_sched_backend_ops {
* @hw_submission_limit: the max size of the hardware queue. * @hw_submission_limit: the max size of the hardware queue.
* @timeout: the time after which a job is removed from the scheduler. * @timeout: the time after which a job is removed from the scheduler.
* @name: name of the ring for which this scheduler is being used. * @name: name of the ring for which this scheduler is being used.
* @sched_rq: priority wise array of run queues. * @num_rqs: Number of run-queues. This is at most DRM_SCHED_PRIORITY_COUNT,
* as there's usually one run-queue per priority, but could be less.
* @sched_rq: An allocated array of run-queues of size @num_rqs;
* @wake_up_worker: the wait queue on which the scheduler sleeps until a job * @wake_up_worker: the wait queue on which the scheduler sleeps until a job
* is ready to be scheduled. * is ready to be scheduled.
* @job_scheduled: once @drm_sched_entity_do_release is called the scheduler * @job_scheduled: once @drm_sched_entity_do_release is called the scheduler
@ -501,7 +503,8 @@ struct drm_gpu_scheduler {
uint32_t hw_submission_limit; uint32_t hw_submission_limit;
long timeout; long timeout;
const char *name; const char *name;
struct drm_sched_rq sched_rq[DRM_SCHED_PRIORITY_COUNT]; u32 num_rqs;
struct drm_sched_rq **sched_rq;
wait_queue_head_t wake_up_worker; wait_queue_head_t wake_up_worker;
wait_queue_head_t job_scheduled; wait_queue_head_t job_scheduled;
atomic_t hw_rq_count; atomic_t hw_rq_count;
@ -521,7 +524,7 @@ struct drm_gpu_scheduler {
int drm_sched_init(struct drm_gpu_scheduler *sched, int drm_sched_init(struct drm_gpu_scheduler *sched,
const struct drm_sched_backend_ops *ops, const struct drm_sched_backend_ops *ops,
uint32_t hw_submission, unsigned hang_limit, u32 num_rqs, uint32_t hw_submission, unsigned int hang_limit,
long timeout, struct workqueue_struct *timeout_wq, long timeout, struct workqueue_struct *timeout_wq,
atomic_t *score, const char *name, struct device *dev); atomic_t *score, const char *name, struct device *dev);