aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch')
-rw-r--r--common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch b/common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch
new file mode 100644
index 00000000..c4f92298
--- /dev/null
+++ b/common/recipes-kernel/linux/linux-yocto-4.19.8/0705-drm-scheduler-move-entity-handling-into-separate-fil.patch
@@ -0,0 +1,1061 @@
+From 61e599a3c395e62b2e3d32d1d4f536340138d5a6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Christian=20K=C3=B6nig?= <christian.koenig@amd.com>
+Date: Mon, 6 Aug 2018 14:25:32 +0200
+Subject: [PATCH 0705/2940] drm/scheduler: move entity handling into separate
+ file
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is complex enough on it's own. Move it into a separate C file.
+
+Signed-off-by: Christian König <christian.koenig@amd.com>
+Reviewed-by: Huang Rui <ray.huang@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Chaudhary Amit Kumar <Chaudharyamit.Kumar@amd.com>
+---
+ drivers/gpu/drm/scheduler/Makefile | 2 +-
+ drivers/gpu/drm/scheduler/gpu_scheduler.c | 476 +---------------------
+ drivers/gpu/drm/scheduler/sched_entity.c | 459 +++++++++++++++++++++
+ include/drm/gpu_scheduler.h | 18 +
+ 4 files changed, 483 insertions(+), 472 deletions(-)
+ create mode 100644 drivers/gpu/drm/scheduler/sched_entity.c
+
+diff --git a/drivers/gpu/drm/scheduler/Makefile b/drivers/gpu/drm/scheduler/Makefile
+index 7665883f81d4..f23785d4b3c8 100644
+--- a/drivers/gpu/drm/scheduler/Makefile
++++ b/drivers/gpu/drm/scheduler/Makefile
+@@ -20,6 +20,6 @@
+ # OTHER DEALINGS IN THE SOFTWARE.
+ #
+ #
+-gpu-sched-y := gpu_scheduler.o sched_fence.o
++gpu-sched-y := gpu_scheduler.o sched_fence.o sched_entity.o
+
+ obj-$(CONFIG_DRM_SCHED) += gpu-sched.o
+diff --git a/drivers/gpu/drm/scheduler/gpu_scheduler.c b/drivers/gpu/drm/scheduler/gpu_scheduler.c
+index 1f65ede2b20e..9ca741f3a0bc 100644
+--- a/drivers/gpu/drm/scheduler/gpu_scheduler.c
++++ b/drivers/gpu/drm/scheduler/gpu_scheduler.c
+@@ -58,8 +58,6 @@
+ #define to_drm_sched_job(sched_job) \
+ container_of((sched_job), struct drm_sched_job, queue_node)
+
+-static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity);
+-static void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
+ static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
+
+ /**
+@@ -86,8 +84,8 @@ static void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
+ *
+ * Adds a scheduler entity to the run queue.
+ */
+-static void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
+- struct drm_sched_entity *entity)
++void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
++ struct drm_sched_entity *entity)
+ {
+ if (!list_empty(&entity->list))
+ return;
+@@ -104,8 +102,8 @@ static void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
+ *
+ * Removes a scheduler entity from the run queue.
+ */
+-static void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
+- struct drm_sched_entity *entity)
++void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
++ struct drm_sched_entity *entity)
+ {
+ if (list_empty(&entity->list))
+ return;
+@@ -158,336 +156,6 @@ drm_sched_rq_select_entity(struct drm_sched_rq *rq)
+ return NULL;
+ }
+
+-/**
+- * drm_sched_entity_init - Init a context entity used by scheduler when
+- * submit to HW ring.
+- *
+- * @entity: scheduler entity to init
+- * @rq_list: the list of run queue on which jobs from this
+- * entity can be submitted
+- * @num_rq_list: number of run queue in rq_list
+- * @guilty: atomic_t set to 1 when a job on this queue
+- * is found to be guilty causing a timeout
+- *
+- * Note: the rq_list should have atleast one element to schedule
+- * the entity
+- *
+- * Returns 0 on success or a negative error code on failure.
+-*/
+-int drm_sched_entity_init(struct drm_sched_entity *entity,
+- struct drm_sched_rq **rq_list,
+- unsigned int num_rq_list,
+- atomic_t *guilty)
+-{
+- int i;
+-
+- if (!(entity && rq_list && num_rq_list > 0 && rq_list[0]))
+- return -EINVAL;
+-
+- memset(entity, 0, sizeof(struct drm_sched_entity));
+- INIT_LIST_HEAD(&entity->list);
+- entity->rq = rq_list[0];
+- entity->guilty = guilty;
+- entity->num_rq_list = num_rq_list;
+- entity->rq_list = kcalloc(num_rq_list, sizeof(struct drm_sched_rq *),
+- GFP_KERNEL);
+- if (!entity->rq_list)
+- return -ENOMEM;
+-
+- for (i = 0; i < num_rq_list; ++i)
+- entity->rq_list[i] = rq_list[i];
+- entity->last_scheduled = NULL;
+-
+- spin_lock_init(&entity->rq_lock);
+- spsc_queue_init(&entity->job_queue);
+-
+- atomic_set(&entity->fence_seq, 0);
+- entity->fence_context = dma_fence_context_alloc(2);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(drm_sched_entity_init);
+-
+-/**
+- * drm_sched_entity_is_idle - Check if entity is idle
+- *
+- * @entity: scheduler entity
+- *
+- * Returns true if the entity does not have any unscheduled jobs.
+- */
+-static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
+-{
+- rmb();
+-
+- if (list_empty(&entity->list) ||
+- spsc_queue_peek(&entity->job_queue) == NULL)
+- return true;
+-
+- return false;
+-}
+-
+-/**
+- * drm_sched_entity_is_ready - Check if entity is ready
+- *
+- * @entity: scheduler entity
+- *
+- * Return true if entity could provide a job.
+- */
+-static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity)
+-{
+- if (spsc_queue_peek(&entity->job_queue) == NULL)
+- return false;
+-
+- if (READ_ONCE(entity->dependency))
+- return false;
+-
+- return true;
+-}
+-
+-/**
+- * drm_sched_entity_get_free_sched - Get the rq from rq_list with least load
+- *
+- * @entity: scheduler entity
+- *
+- * Return the pointer to the rq with least load.
+- */
+-static struct drm_sched_rq *
+-drm_sched_entity_get_free_sched(struct drm_sched_entity *entity)
+-{
+- struct drm_sched_rq *rq = NULL;
+- unsigned int min_jobs = UINT_MAX, num_jobs;
+- int i;
+-
+- for (i = 0; i < entity->num_rq_list; ++i) {
+- num_jobs = atomic_read(&entity->rq_list[i]->sched->num_jobs);
+- if (num_jobs < min_jobs) {
+- min_jobs = num_jobs;
+- rq = entity->rq_list[i];
+- }
+- }
+-
+- return rq;
+-}
+-
+-static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
+- struct dma_fence_cb *cb)
+-{
+- struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
+- finish_cb);
+- drm_sched_fence_finished(job->s_fence);
+- WARN_ON(job->s_fence->parent);
+- dma_fence_put(&job->s_fence->finished);
+- job->sched->ops->free_job(job);
+-}
+-
+-
+-/**
+- * drm_sched_entity_flush - Flush a context entity
+- *
+- * @entity: scheduler entity
+- * @timeout: time to wait in for Q to become empty in jiffies.
+- *
+- * Splitting drm_sched_entity_fini() into two functions, The first one does the waiting,
+- * removes the entity from the runqueue and returns an error when the process was killed.
+- *
+- * Returns the remaining time in jiffies left from the input timeout
+- */
+-long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
+-{
+- struct drm_gpu_scheduler *sched;
+- struct task_struct *last_user;
+- long ret = timeout;
+-
+- sched = entity->rq->sched;
+- /**
+- * The client will not queue more IBs during this fini, consume existing
+- * queued IBs or discard them on SIGKILL
+- */
+- if (current->flags & PF_EXITING) {
+- if (timeout)
+- ret = wait_event_timeout(
+- sched->job_scheduled,
+- drm_sched_entity_is_idle(entity),
+- timeout);
+- } else
+- wait_event_killable(sched->job_scheduled, drm_sched_entity_is_idle(entity));
+-
+-
+- /* For killed process disable any more IBs enqueue right now */
+- last_user = cmpxchg(&entity->last_user, current->group_leader, NULL);
+- if ((!last_user || last_user == current->group_leader) &&
+- (current->flags & PF_EXITING) && (current->exit_code == SIGKILL))
+- drm_sched_rq_remove_entity(entity->rq, entity);
+-
+- return ret;
+-}
+-EXPORT_SYMBOL(drm_sched_entity_flush);
+-
+-/**
+- * drm_sched_entity_cleanup - Destroy a context entity
+- *
+- * @entity: scheduler entity
+- *
+- * This should be called after @drm_sched_entity_do_release. It goes over the
+- * entity and signals all jobs with an error code if the process was killed.
+- *
+- */
+-void drm_sched_entity_fini(struct drm_sched_entity *entity)
+-{
+- struct drm_gpu_scheduler *sched;
+-
+- sched = entity->rq->sched;
+- drm_sched_rq_remove_entity(entity->rq, entity);
+-
+- /* Consumption of existing IBs wasn't completed. Forcefully
+- * remove them here.
+- */
+- if (spsc_queue_peek(&entity->job_queue)) {
+- struct drm_sched_job *job;
+- int r;
+-
+- /* Park the kernel for a moment to make sure it isn't processing
+- * our enity.
+- */
+- kthread_park(sched->thread);
+- kthread_unpark(sched->thread);
+- if (entity->dependency) {
+- dma_fence_remove_callback(entity->dependency,
+- &entity->cb);
+- dma_fence_put(entity->dependency);
+- entity->dependency = NULL;
+- }
+-
+- while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) {
+- struct drm_sched_fence *s_fence = job->s_fence;
+- drm_sched_fence_scheduled(s_fence);
+- dma_fence_set_error(&s_fence->finished, -ESRCH);
+-
+- /*
+- * When pipe is hanged by older entity, new entity might
+- * not even have chance to submit it's first job to HW
+- * and so entity->last_scheduled will remain NULL
+- */
+- if (!entity->last_scheduled) {
+- drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
+- } else {
+- r = dma_fence_add_callback(entity->last_scheduled, &job->finish_cb,
+- drm_sched_entity_kill_jobs_cb);
+- if (r == -ENOENT)
+- drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
+- else if (r)
+- DRM_ERROR("fence add callback failed (%d)\n", r);
+- }
+- }
+- }
+-
+- dma_fence_put(entity->last_scheduled);
+- entity->last_scheduled = NULL;
+- kfree(entity->rq_list);
+-}
+-EXPORT_SYMBOL(drm_sched_entity_fini);
+-
+-/**
+- * drm_sched_entity_fini - Destroy a context entity
+- *
+- * @entity: scheduler entity
+- *
+- * Calls drm_sched_entity_do_release() and drm_sched_entity_cleanup()
+- */
+-void drm_sched_entity_destroy(struct drm_sched_entity *entity)
+-{
+- drm_sched_entity_flush(entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
+- drm_sched_entity_fini(entity);
+-}
+-EXPORT_SYMBOL(drm_sched_entity_destroy);
+-
+-static void drm_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
+-{
+- struct drm_sched_entity *entity =
+- container_of(cb, struct drm_sched_entity, cb);
+- entity->dependency = NULL;
+- dma_fence_put(f);
+- drm_sched_wakeup(entity->rq->sched);
+-}
+-
+-static void drm_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb)
+-{
+- struct drm_sched_entity *entity =
+- container_of(cb, struct drm_sched_entity, cb);
+- entity->dependency = NULL;
+- dma_fence_put(f);
+-}
+-
+-/**
+- * drm_sched_entity_set_rq_priority - helper for drm_sched_entity_set_priority
+- */
+-static void drm_sched_entity_set_rq_priority(struct drm_sched_rq **rq,
+- enum drm_sched_priority priority)
+-{
+- *rq = &(*rq)->sched->sched_rq[priority];
+-}
+-
+-/**
+- * drm_sched_entity_set_priority - Sets priority of the entity
+- *
+- * @entity: scheduler entity
+- * @priority: scheduler priority
+- *
+- * Update the priority of runqueus used for the entity.
+- */
+-void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
+- enum drm_sched_priority priority)
+-{
+- unsigned int i;
+-
+- spin_lock(&entity->rq_lock);
+-
+- for (i = 0; i < entity->num_rq_list; ++i)
+- drm_sched_entity_set_rq_priority(&entity->rq_list[i], priority);
+-
+- drm_sched_rq_remove_entity(entity->rq, entity);
+- drm_sched_entity_set_rq_priority(&entity->rq, priority);
+- drm_sched_rq_add_entity(entity->rq, entity);
+-
+- spin_unlock(&entity->rq_lock);
+-}
+-EXPORT_SYMBOL(drm_sched_entity_set_priority);
+-
+-/**
+- * drm_sched_entity_set_rq_priority - helper for drm_sched_entity_set_priority
+- */
+-static void drm_sched_entity_set_rq_priority(struct drm_sched_rq **rq,
+- enum drm_sched_priority priority)
+-{
+- *rq = &(*rq)->sched->sched_rq[priority];
+-}
+-
+-/**
+- * drm_sched_entity_set_priority - Sets priority of the entity
+- *
+- * @entity: scheduler entity
+- * @priority: scheduler priority
+- *
+- * Update the priority of runqueus used for the entity.
+- */
+-void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
+- enum drm_sched_priority priority)
+-{
+- unsigned int i;
+-
+- spin_lock(&entity->rq_lock);
+-
+- for (i = 0; i < entity->num_rq_list; ++i)
+- drm_sched_entity_set_rq_priority(&entity->rq_list[i], priority);
+-
+- drm_sched_rq_remove_entity(entity->rq, entity);
+- drm_sched_entity_set_rq_priority(&entity->rq, priority);
+- drm_sched_rq_add_entity(entity->rq, entity);
+-
+- spin_unlock(&entity->rq_lock);
+-}
+-EXPORT_SYMBOL(drm_sched_entity_set_priority);
+-
+ /**
+ * drm_sched_dependency_optimized
+ *
+@@ -514,140 +182,6 @@ bool drm_sched_dependency_optimized(struct dma_fence* fence,
+ }
+ EXPORT_SYMBOL(drm_sched_dependency_optimized);
+
+-static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity)
+-{
+- struct drm_gpu_scheduler *sched = entity->rq->sched;
+- struct dma_fence * fence = entity->dependency;
+- struct drm_sched_fence *s_fence;
+-
+- if (fence->context == entity->fence_context ||
+- fence->context == entity->fence_context + 1) {
+- /*
+- * Fence is a scheduled/finished fence from a job
+- * which belongs to the same entity, we can ignore
+- * fences from ourself
+- */
+- dma_fence_put(entity->dependency);
+- return false;
+- }
+-
+- s_fence = to_drm_sched_fence(fence);
+- if (s_fence && s_fence->sched == sched) {
+-
+- /*
+- * Fence is from the same scheduler, only need to wait for
+- * it to be scheduled
+- */
+- fence = dma_fence_get(&s_fence->scheduled);
+- dma_fence_put(entity->dependency);
+- entity->dependency = fence;
+- if (!dma_fence_add_callback(fence, &entity->cb,
+- drm_sched_entity_clear_dep))
+- return true;
+-
+- /* Ignore it when it is already scheduled */
+- dma_fence_put(fence);
+- return false;
+- }
+-
+- if (!dma_fence_add_callback(entity->dependency, &entity->cb,
+- drm_sched_entity_wakeup))
+- return true;
+-
+- dma_fence_put(entity->dependency);
+- return false;
+-}
+-
+-static 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 = to_drm_sched_job(
+- spsc_queue_peek(&entity->job_queue));
+-
+- if (!sched_job)
+- return NULL;
+-
+- while ((entity->dependency = sched->ops->dependency(sched_job, entity))) {
+- if (drm_sched_entity_add_dependency_cb(entity)) {
+-
+- trace_drm_sched_job_wait_dep(sched_job, entity->dependency);
+- return NULL;
+- }
+- }
+-
+- /* skip jobs from entity that marked guilty */
+- if (entity->guilty && atomic_read(entity->guilty))
+- dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
+-
+- dma_fence_put(entity->last_scheduled);
+- entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished);
+-
+- spsc_queue_pop(&entity->job_queue);
+- return sched_job;
+-}
+-
+-/**
+- * drm_sched_entity_select_rq - select a new rq for the entity
+- *
+- * @entity: scheduler entity
+- *
+- * Check all prerequisites and select a new rq for the entity for load
+- * balancing.
+- */
+-static void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
+-{
+- struct dma_fence *fence;
+- struct drm_sched_rq *rq;
+-
+- if (!spsc_queue_count(&entity->job_queue) == 0 ||
+- entity->num_rq_list <= 1)
+- return;
+-
+- fence = READ_ONCE(entity->last_scheduled);
+- if (fence && !dma_fence_is_signaled(fence))
+- return;
+-
+- rq = drm_sched_entity_get_free_sched(entity);
+- spin_lock(&entity->rq_lock);
+- drm_sched_rq_remove_entity(entity->rq, entity);
+- entity->rq = rq;
+- spin_unlock(&entity->rq_lock);
+-}
+-
+-/**
+- * drm_sched_entity_push_job - Submit a job to the entity's job queue
+- *
+- * @sched_job: job to submit
+- * @entity: scheduler entity
+- *
+- * Note: To guarantee that the order of insertion to queue matches
+- * the job's fence sequence number this function should be
+- * called with drm_sched_job_init under common lock.
+- *
+- * Returns 0 for success, negative error code otherwise.
+- */
+-void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
+- struct drm_sched_entity *entity)
+-{
+- bool first;
+-
+- trace_drm_sched_job(sched_job, entity);
+- atomic_inc(&entity->rq->sched->num_jobs);
+- WRITE_ONCE(entity->last_user, current->group_leader);
+- first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
+-
+- /* first job wakes up scheduler */
+- if (first) {
+- /* Add the entity to the run queue */
+- spin_lock(&entity->rq_lock);
+- drm_sched_rq_add_entity(entity->rq, entity);
+- spin_unlock(&entity->rq_lock);
+- drm_sched_wakeup(entity->rq->sched);
+- }
+-}
+-EXPORT_SYMBOL(drm_sched_entity_push_job);
+-
+ /* job_finish is called after hw fence signaled
+ */
+ static void drm_sched_job_finish(struct work_struct *work)
+@@ -875,7 +409,7 @@ static bool drm_sched_ready(struct drm_gpu_scheduler *sched)
+ * @sched: scheduler instance
+ *
+ */
+-static void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
++void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
+ {
+ if (drm_sched_ready(sched))
+ wake_up_interruptible(&sched->wake_up_worker);
+diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
+new file mode 100644
+index 000000000000..1053f27af9df
+--- /dev/null
++++ b/drivers/gpu/drm/scheduler/sched_entity.c
+@@ -0,0 +1,459 @@
++/*
++ * Copyright 2015 Advanced Micro Devices, Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include <linux/kthread.h>
++#include <drm/gpu_scheduler.h>
++
++#include "gpu_scheduler_trace.h"
++
++#define to_drm_sched_job(sched_job) \
++ container_of((sched_job), struct drm_sched_job, queue_node)
++
++/**
++ * drm_sched_entity_init - Init a context entity used by scheduler when
++ * submit to HW ring.
++ *
++ * @entity: scheduler entity to init
++ * @rq_list: the list of run queue on which jobs from this
++ * entity can be submitted
++ * @num_rq_list: number of run queue in rq_list
++ * @guilty: atomic_t set to 1 when a job on this queue
++ * is found to be guilty causing a timeout
++ *
++ * Note: the rq_list should have atleast one element to schedule
++ * the entity
++ *
++ * Returns 0 on success or a negative error code on failure.
++*/
++int drm_sched_entity_init(struct drm_sched_entity *entity,
++ struct drm_sched_rq **rq_list,
++ unsigned int num_rq_list,
++ atomic_t *guilty)
++{
++ int i;
++
++ if (!(entity && rq_list && num_rq_list > 0 && rq_list[0]))
++ return -EINVAL;
++
++ memset(entity, 0, sizeof(struct drm_sched_entity));
++ INIT_LIST_HEAD(&entity->list);
++ entity->rq = rq_list[0];
++ entity->guilty = guilty;
++ entity->num_rq_list = num_rq_list;
++ entity->rq_list = kcalloc(num_rq_list, sizeof(struct drm_sched_rq *),
++ GFP_KERNEL);
++ if (!entity->rq_list)
++ return -ENOMEM;
++
++ for (i = 0; i < num_rq_list; ++i)
++ entity->rq_list[i] = rq_list[i];
++ entity->last_scheduled = NULL;
++
++ spin_lock_init(&entity->rq_lock);
++ spsc_queue_init(&entity->job_queue);
++
++ atomic_set(&entity->fence_seq, 0);
++ entity->fence_context = dma_fence_context_alloc(2);
++
++ return 0;
++}
++EXPORT_SYMBOL(drm_sched_entity_init);
++
++/**
++ * drm_sched_entity_is_idle - Check if entity is idle
++ *
++ * @entity: scheduler entity
++ *
++ * Returns true if the entity does not have any unscheduled jobs.
++ */
++static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
++{
++ rmb();
++
++ if (list_empty(&entity->list) ||
++ spsc_queue_peek(&entity->job_queue) == NULL)
++ return true;
++
++ return false;
++}
++
++/**
++ * drm_sched_entity_is_ready - Check if entity is ready
++ *
++ * @entity: scheduler entity
++ *
++ * Return true if entity could provide a job.
++ */
++bool drm_sched_entity_is_ready(struct drm_sched_entity *entity)
++{
++ if (spsc_queue_peek(&entity->job_queue) == NULL)
++ return false;
++
++ if (READ_ONCE(entity->dependency))
++ return false;
++
++ return true;
++}
++
++/**
++ * drm_sched_entity_get_free_sched - Get the rq from rq_list with least load
++ *
++ * @entity: scheduler entity
++ *
++ * Return the pointer to the rq with least load.
++ */
++static struct drm_sched_rq *
++drm_sched_entity_get_free_sched(struct drm_sched_entity *entity)
++{
++ struct drm_sched_rq *rq = NULL;
++ unsigned int min_jobs = UINT_MAX, num_jobs;
++ int i;
++
++ for (i = 0; i < entity->num_rq_list; ++i) {
++ num_jobs = atomic_read(&entity->rq_list[i]->sched->num_jobs);
++ if (num_jobs < min_jobs) {
++ min_jobs = num_jobs;
++ rq = entity->rq_list[i];
++ }
++ }
++
++ return rq;
++}
++
++static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
++ struct dma_fence_cb *cb)
++{
++ struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
++ finish_cb);
++ drm_sched_fence_finished(job->s_fence);
++ WARN_ON(job->s_fence->parent);
++ dma_fence_put(&job->s_fence->finished);
++ job->sched->ops->free_job(job);
++}
++
++
++/**
++ * drm_sched_entity_flush - Flush a context entity
++ *
++ * @entity: scheduler entity
++ * @timeout: time to wait in for Q to become empty in jiffies.
++ *
++ * Splitting drm_sched_entity_fini() into two functions, The first one does the waiting,
++ * removes the entity from the runqueue and returns an error when the process was killed.
++ *
++ * Returns the remaining time in jiffies left from the input timeout
++ */
++long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
++{
++ struct drm_gpu_scheduler *sched;
++ struct task_struct *last_user;
++ long ret = timeout;
++
++ sched = entity->rq->sched;
++ /**
++ * The client will not queue more IBs during this fini, consume existing
++ * queued IBs or discard them on SIGKILL
++ */
++ if (current->flags & PF_EXITING) {
++ if (timeout)
++ ret = wait_event_timeout(
++ sched->job_scheduled,
++ drm_sched_entity_is_idle(entity),
++ timeout);
++ } else {
++ wait_event_killable(sched->job_scheduled,
++ drm_sched_entity_is_idle(entity));
++ }
++
++ /* For killed process disable any more IBs enqueue right now */
++ last_user = cmpxchg(&entity->last_user, current->group_leader, NULL);
++ if ((!last_user || last_user == current->group_leader) &&
++ (current->flags & PF_EXITING) && (current->exit_code == SIGKILL))
++ drm_sched_rq_remove_entity(entity->rq, entity);
++
++ return ret;
++}
++EXPORT_SYMBOL(drm_sched_entity_flush);
++
++/**
++ * drm_sched_entity_cleanup - Destroy a context entity
++ *
++ * @entity: scheduler entity
++ *
++ * This should be called after @drm_sched_entity_do_release. It goes over the
++ * entity and signals all jobs with an error code if the process was killed.
++ *
++ */
++void drm_sched_entity_fini(struct drm_sched_entity *entity)
++{
++ struct drm_gpu_scheduler *sched;
++
++ sched = entity->rq->sched;
++ drm_sched_rq_remove_entity(entity->rq, entity);
++
++ /* Consumption of existing IBs wasn't completed. Forcefully
++ * remove them here.
++ */
++ if (spsc_queue_peek(&entity->job_queue)) {
++ struct drm_sched_job *job;
++ int r;
++
++ /* Park the kernel for a moment to make sure it isn't processing
++ * our enity.
++ */
++ kthread_park(sched->thread);
++ kthread_unpark(sched->thread);
++ if (entity->dependency) {
++ dma_fence_remove_callback(entity->dependency,
++ &entity->cb);
++ dma_fence_put(entity->dependency);
++ entity->dependency = NULL;
++ }
++
++ while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) {
++ struct drm_sched_fence *s_fence = job->s_fence;
++ drm_sched_fence_scheduled(s_fence);
++ dma_fence_set_error(&s_fence->finished, -ESRCH);
++
++ /*
++ * When pipe is hanged by older entity, new entity might
++ * not even have chance to submit it's first job to HW
++ * and so entity->last_scheduled will remain NULL
++ */
++ if (!entity->last_scheduled) {
++ drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
++ } else {
++ r = dma_fence_add_callback(entity->last_scheduled, &job->finish_cb,
++ drm_sched_entity_kill_jobs_cb);
++ if (r == -ENOENT)
++ drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
++ else if (r)
++ DRM_ERROR("fence add callback failed (%d)\n", r);
++ }
++ }
++ }
++
++ dma_fence_put(entity->last_scheduled);
++ entity->last_scheduled = NULL;
++ kfree(entity->rq_list);
++}
++EXPORT_SYMBOL(drm_sched_entity_fini);
++
++/**
++ * drm_sched_entity_fini - Destroy a context entity
++ *
++ * @entity: scheduler entity
++ *
++ * Calls drm_sched_entity_do_release() and drm_sched_entity_cleanup()
++ */
++void drm_sched_entity_destroy(struct drm_sched_entity *entity)
++{
++ drm_sched_entity_flush(entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
++ drm_sched_entity_fini(entity);
++}
++EXPORT_SYMBOL(drm_sched_entity_destroy);
++
++static void drm_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
++{
++ struct drm_sched_entity *entity =
++ container_of(cb, struct drm_sched_entity, cb);
++ entity->dependency = NULL;
++ dma_fence_put(f);
++ drm_sched_wakeup(entity->rq->sched);
++}
++
++static void drm_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb)
++{
++ struct drm_sched_entity *entity =
++ container_of(cb, struct drm_sched_entity, cb);
++ entity->dependency = NULL;
++ dma_fence_put(f);
++}
++
++/**
++ * drm_sched_entity_set_rq_priority - helper for drm_sched_entity_set_priority
++ */
++static void drm_sched_entity_set_rq_priority(struct drm_sched_rq **rq,
++ enum drm_sched_priority priority)
++{
++ *rq = &(*rq)->sched->sched_rq[priority];
++}
++
++/**
++ * drm_sched_entity_set_priority - Sets priority of the entity
++ *
++ * @entity: scheduler entity
++ * @priority: scheduler priority
++ *
++ * Update the priority of runqueus used for the entity.
++ */
++void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
++ enum drm_sched_priority priority)
++{
++ unsigned int i;
++
++ spin_lock(&entity->rq_lock);
++
++ for (i = 0; i < entity->num_rq_list; ++i)
++ drm_sched_entity_set_rq_priority(&entity->rq_list[i], priority);
++
++ drm_sched_rq_remove_entity(entity->rq, entity);
++ drm_sched_entity_set_rq_priority(&entity->rq, priority);
++ drm_sched_rq_add_entity(entity->rq, entity);
++
++ spin_unlock(&entity->rq_lock);
++}
++EXPORT_SYMBOL(drm_sched_entity_set_priority);
++
++static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity)
++{
++ struct drm_gpu_scheduler *sched = entity->rq->sched;
++ struct dma_fence * fence = entity->dependency;
++ struct drm_sched_fence *s_fence;
++
++ if (fence->context == entity->fence_context ||
++ fence->context == entity->fence_context + 1) {
++ /*
++ * Fence is a scheduled/finished fence from a job
++ * which belongs to the same entity, we can ignore
++ * fences from ourself
++ */
++ dma_fence_put(entity->dependency);
++ return false;
++ }
++
++ s_fence = to_drm_sched_fence(fence);
++ if (s_fence && s_fence->sched == sched) {
++
++ /*
++ * Fence is from the same scheduler, only need to wait for
++ * it to be scheduled
++ */
++ fence = dma_fence_get(&s_fence->scheduled);
++ dma_fence_put(entity->dependency);
++ entity->dependency = fence;
++ if (!dma_fence_add_callback(fence, &entity->cb,
++ drm_sched_entity_clear_dep))
++ return true;
++
++ /* Ignore it when it is already scheduled */
++ dma_fence_put(fence);
++ return false;
++ }
++
++ if (!dma_fence_add_callback(entity->dependency, &entity->cb,
++ drm_sched_entity_wakeup))
++ return true;
++
++ dma_fence_put(entity->dependency);
++ return false;
++}
++
++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 = to_drm_sched_job(
++ spsc_queue_peek(&entity->job_queue));
++
++ if (!sched_job)
++ return NULL;
++
++ while ((entity->dependency = sched->ops->dependency(sched_job, entity))) {
++ if (drm_sched_entity_add_dependency_cb(entity)) {
++
++ trace_drm_sched_job_wait_dep(sched_job, entity->dependency);
++ return NULL;
++ }
++ }
++
++ /* skip jobs from entity that marked guilty */
++ if (entity->guilty && atomic_read(entity->guilty))
++ dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
++
++ dma_fence_put(entity->last_scheduled);
++ entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished);
++
++ spsc_queue_pop(&entity->job_queue);
++ return sched_job;
++}
++
++/**
++ * drm_sched_entity_select_rq - select a new rq for the entity
++ *
++ * @entity: scheduler entity
++ *
++ * Check all prerequisites and select a new rq for the entity for load
++ * balancing.
++ */
++void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
++{
++ struct dma_fence *fence;
++ struct drm_sched_rq *rq;
++
++ if (!spsc_queue_count(&entity->job_queue) == 0 ||
++ entity->num_rq_list <= 1)
++ return;
++
++ fence = READ_ONCE(entity->last_scheduled);
++ if (fence && !dma_fence_is_signaled(fence))
++ return;
++
++ rq = drm_sched_entity_get_free_sched(entity);
++ spin_lock(&entity->rq_lock);
++ drm_sched_rq_remove_entity(entity->rq, entity);
++ entity->rq = rq;
++ spin_unlock(&entity->rq_lock);
++}
++
++/**
++ * drm_sched_entity_push_job - Submit a job to the entity's job queue
++ *
++ * @sched_job: job to submit
++ * @entity: scheduler entity
++ *
++ * Note: To guarantee that the order of insertion to queue matches
++ * the job's fence sequence number this function should be
++ * called with drm_sched_job_init under common lock.
++ *
++ * Returns 0 for success, negative error code otherwise.
++ */
++void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
++ struct drm_sched_entity *entity)
++{
++ bool first;
++
++ trace_drm_sched_job(sched_job, entity);
++ atomic_inc(&entity->rq->sched->num_jobs);
++ WRITE_ONCE(entity->last_user, current->group_leader);
++ first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
++
++ /* first job wakes up scheduler */
++ if (first) {
++ /* Add the entity to the run queue */
++ spin_lock(&entity->rq_lock);
++ drm_sched_rq_add_entity(entity->rq, entity);
++ spin_unlock(&entity->rq_lock);
++ drm_sched_wakeup(entity->rq->sched);
++ }
++}
++EXPORT_SYMBOL(drm_sched_entity_push_job);
+diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h
+index 551f92ce2164..fa7ea410b63c 100644
+--- a/include/drm/gpu_scheduler.h
++++ b/include/drm/gpu_scheduler.h
+@@ -285,6 +285,21 @@ int drm_sched_init(struct drm_gpu_scheduler *sched,
+ uint32_t hw_submission, unsigned hang_limit, long timeout,
+ const char *name);
+ void drm_sched_fini(struct drm_gpu_scheduler *sched);
++int drm_sched_job_init(struct drm_sched_job *job,
++ struct drm_sched_entity *entity,
++ void *owner);
++void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
++void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched,
++ struct drm_sched_job *job);
++void drm_sched_job_recovery(struct drm_gpu_scheduler *sched);
++bool drm_sched_dependency_optimized(struct dma_fence* fence,
++ struct drm_sched_entity *entity);
++void drm_sched_job_kickout(struct drm_sched_job *s_job);
++
++void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
++ struct drm_sched_entity *entity);
++void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
++ struct drm_sched_entity *entity);
+
+ int drm_sched_entity_init(struct drm_sched_entity *entity,
+ struct drm_sched_rq **rq_list,
+@@ -293,6 +308,9 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
+ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout);
+ void drm_sched_entity_fini(struct drm_sched_entity *entity);
+ void drm_sched_entity_destroy(struct drm_sched_entity *entity);
++void drm_sched_entity_select_rq(struct drm_sched_entity *entity);
++struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity);
++bool drm_sched_entity_is_ready(struct drm_sched_entity *entity);
+ void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
+ struct drm_sched_entity *entity);
+ void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
+--
+2.17.1
+