diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3856-drm-amd-amdkcl-add-fence-functions.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3856-drm-amd-amdkcl-add-fence-functions.patch | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3856-drm-amd-amdkcl-add-fence-functions.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3856-drm-amd-amdkcl-add-fence-functions.patch new file mode 100644 index 00000000..f0af996e --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3856-drm-amd-amdkcl-add-fence-functions.patch @@ -0,0 +1,1080 @@ +From fd817d44483158725d4ac55b5a4d1f7fc1eaec7d Mon Sep 17 00:00:00 2001 +From: Junwei Zhang <Jerry.Zhang@amd.com> +Date: Fri, 23 Dec 2016 16:28:35 +0800 +Subject: [PATCH 3856/4131] drm/amd/amdkcl: add fence functions + +Change-Id: I5ecbe63c5fd6765cd28ee570420e347e8283b8d0 +Signed-off-by: Junwei Zhang <Jerry.Zhang@amd.com> +Reviewed-by: Qiang Yu <Qiang.Yu@amd.com> +--- + drivers/gpu/drm/amd/scheduler/gpu_scheduler.c | 687 ++++++++++++++++++++++++++ + drivers/gpu/drm/amd/scheduler/gpu_scheduler.h | 174 +++++++ + drivers/gpu/drm/amd/scheduler/sched_fence.c | 181 +++++++ + 3 files changed, 1042 insertions(+) + create mode 100644 drivers/gpu/drm/amd/scheduler/gpu_scheduler.c + create mode 100644 drivers/gpu/drm/amd/scheduler/gpu_scheduler.h + create mode 100644 drivers/gpu/drm/amd/scheduler/sched_fence.c + +diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +new file mode 100644 +index 0000000..735a5a4 +--- /dev/null ++++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +@@ -0,0 +1,687 @@ ++/* ++ * 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 <linux/wait.h> ++#include <linux/sched.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) ++#include <uapi/linux/sched/types.h> ++#endif ++#include <drm/drmP.h> ++#include "gpu_scheduler.h" ++ ++#define CREATE_TRACE_POINTS ++#include "gpu_sched_trace.h" ++ ++static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity); ++static void amd_sched_wakeup(struct amd_gpu_scheduler *sched); ++static void amd_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb); ++ ++/* Initialize a given run queue struct */ ++static void amd_sched_rq_init(struct amd_sched_rq *rq) ++{ ++ spin_lock_init(&rq->lock); ++ INIT_LIST_HEAD(&rq->entities); ++ rq->current_entity = NULL; ++} ++ ++static void amd_sched_rq_add_entity(struct amd_sched_rq *rq, ++ struct amd_sched_entity *entity) ++{ ++ if (!list_empty(&entity->list)) ++ return; ++ spin_lock(&rq->lock); ++ list_add_tail(&entity->list, &rq->entities); ++ spin_unlock(&rq->lock); ++} ++ ++static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq, ++ struct amd_sched_entity *entity) ++{ ++ if (list_empty(&entity->list)) ++ return; ++ spin_lock(&rq->lock); ++ list_del_init(&entity->list); ++ if (rq->current_entity == entity) ++ rq->current_entity = NULL; ++ spin_unlock(&rq->lock); ++} ++ ++/** ++ * Select an entity which could provide a job to run ++ * ++ * @rq The run queue to check. ++ * ++ * Try to find a ready entity, returns NULL if none found. ++ */ ++static struct amd_sched_entity * ++amd_sched_rq_select_entity(struct amd_sched_rq *rq) ++{ ++ struct amd_sched_entity *entity; ++ ++ spin_lock(&rq->lock); ++ ++ entity = rq->current_entity; ++ if (entity) { ++ list_for_each_entry_continue(entity, &rq->entities, list) { ++ if (amd_sched_entity_is_ready(entity)) { ++ rq->current_entity = entity; ++ spin_unlock(&rq->lock); ++ return entity; ++ } ++ } ++ } ++ ++ list_for_each_entry(entity, &rq->entities, list) { ++ ++ if (amd_sched_entity_is_ready(entity)) { ++ rq->current_entity = entity; ++ spin_unlock(&rq->lock); ++ return entity; ++ } ++ ++ if (entity == rq->current_entity) ++ break; ++ } ++ ++ spin_unlock(&rq->lock); ++ ++ return NULL; ++} ++ ++/** ++ * Init a context entity used by scheduler when submit to HW ring. ++ * ++ * @sched The pointer to the scheduler ++ * @entity The pointer to a valid amd_sched_entity ++ * @rq The run queue this entity belongs ++ * @kernel If this is an entity for the kernel ++ * @jobs The max number of jobs in the job queue ++ * ++ * return 0 if succeed. negative error code on failure ++*/ ++int amd_sched_entity_init(struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity, ++ struct amd_sched_rq *rq, ++ uint32_t jobs) ++{ ++ int r; ++ ++ if (!(sched && entity && rq)) ++ return -EINVAL; ++ ++ memset(entity, 0, sizeof(struct amd_sched_entity)); ++ INIT_LIST_HEAD(&entity->list); ++ entity->rq = rq; ++ entity->sched = sched; ++ ++ spin_lock_init(&entity->queue_lock); ++ r = kfifo_alloc(&entity->job_queue, jobs * sizeof(void *), GFP_KERNEL); ++ if (r) ++ return r; ++ ++ atomic_set(&entity->fence_seq, 0); ++ entity->fence_context = kcl_fence_context_alloc(2); ++ ++ return 0; ++} ++ ++/** ++ * Query if entity is initialized ++ * ++ * @sched Pointer to scheduler instance ++ * @entity The pointer to a valid scheduler entity ++ * ++ * return true if entity is initialized, false otherwise ++*/ ++static bool amd_sched_entity_is_initialized(struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity) ++{ ++ return entity->sched == sched && ++ entity->rq != NULL; ++} ++ ++/** ++ * Check if entity is idle ++ * ++ * @entity The pointer to a valid scheduler entity ++ * ++ * Return true if entity don't has any unscheduled jobs. ++ */ ++static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity) ++{ ++ rmb(); ++ if (kfifo_is_empty(&entity->job_queue)) ++ return true; ++ ++ return false; ++} ++ ++/** ++ * Check if entity is ready ++ * ++ * @entity The pointer to a valid scheduler entity ++ * ++ * Return true if entity could provide a job. ++ */ ++static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity) ++{ ++ if (kfifo_is_empty(&entity->job_queue)) ++ return false; ++ ++ if (ACCESS_ONCE(entity->dependency)) ++ return false; ++ ++ return true; ++} ++ ++/** ++ * Destroy a context entity ++ * ++ * @sched Pointer to scheduler instance ++ * @entity The pointer to a valid scheduler entity ++ * ++ * Cleanup and free the allocated resources. ++ */ ++void amd_sched_entity_fini(struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity) ++{ ++ struct amd_sched_rq *rq = entity->rq; ++ int r; ++ ++ if (!amd_sched_entity_is_initialized(sched, entity)) ++ return; ++ /** ++ * The client will not queue more IBs during this fini, consume existing ++ * queued IBs or discard them on SIGKILL ++ */ ++ if ((current->flags & PF_SIGNALED) && current->exit_code == SIGKILL) ++ r = -ERESTARTSYS; ++ else ++ r = wait_event_killable(sched->job_scheduled, ++ amd_sched_entity_is_idle(entity)); ++ amd_sched_rq_remove_entity(rq, entity); ++ if (r) { ++ struct amd_sched_job *job; ++ ++ /* Park the kernel for a moment to make sure it isn't processing ++ * our enity. ++ */ ++ kcl_kthread_park(sched->thread); ++ kcl_kthread_unpark(sched->thread); ++ while (kfifo_out(&entity->job_queue, &job, sizeof(job))) ++ sched->ops->free_job(job); ++ ++ } ++ kfifo_free(&entity->job_queue); ++} ++ ++static void amd_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb) ++{ ++ struct amd_sched_entity *entity = ++ container_of(cb, struct amd_sched_entity, cb); ++ entity->dependency = NULL; ++ dma_fence_put(f); ++ amd_sched_wakeup(entity->sched); ++} ++ ++static void amd_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb) ++{ ++ struct amd_sched_entity *entity = ++ container_of(cb, struct amd_sched_entity, cb); ++ entity->dependency = NULL; ++ dma_fence_put(f); ++} ++ ++bool amd_sched_dependency_optimized(struct dma_fence* fence, ++ struct amd_sched_entity *entity) ++{ ++ struct amd_gpu_scheduler *sched = entity->sched; ++ struct amd_sched_fence *s_fence; ++ ++ if (!fence || dma_fence_is_signaled(fence)) ++ return false; ++ if (fence->context == entity->fence_context) ++ return true; ++ s_fence = to_amd_sched_fence(fence); ++ if (s_fence && s_fence->sched == sched) ++ return true; ++ ++ return false; ++} ++ ++static bool amd_sched_entity_add_dependency_cb(struct amd_sched_entity *entity) ++{ ++ struct amd_gpu_scheduler *sched = entity->sched; ++ struct dma_fence * fence = entity->dependency; ++ struct amd_sched_fence *s_fence; ++ ++ if (fence->context == entity->fence_context) { ++ /* We can ignore fences from ourself */ ++ dma_fence_put(entity->dependency); ++ return false; ++ } ++ ++ s_fence = to_amd_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, ++ amd_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, ++ amd_sched_entity_wakeup)) ++ return true; ++ ++ dma_fence_put(entity->dependency); ++ return false; ++} ++ ++static struct amd_sched_job * ++amd_sched_entity_pop_job(struct amd_sched_entity *entity) ++{ ++ struct amd_gpu_scheduler *sched = entity->sched; ++ struct amd_sched_job *sched_job; ++ ++ if (!kfifo_out_peek(&entity->job_queue, &sched_job, sizeof(sched_job))) ++ return NULL; ++ ++ while ((entity->dependency = sched->ops->dependency(sched_job))) ++ if (amd_sched_entity_add_dependency_cb(entity)) ++ return NULL; ++ ++ return sched_job; ++} ++ ++/** ++ * Helper to submit a job to the job queue ++ * ++ * @sched_job The pointer to job required to submit ++ * ++ * Returns true if we could submit the job. ++ */ ++static bool amd_sched_entity_in(struct amd_sched_job *sched_job) ++{ ++ struct amd_gpu_scheduler *sched = sched_job->sched; ++ struct amd_sched_entity *entity = sched_job->s_entity; ++ bool added, first = false; ++ ++ spin_lock(&entity->queue_lock); ++ added = kfifo_in(&entity->job_queue, &sched_job, ++ sizeof(sched_job)) == sizeof(sched_job); ++ ++ if (added && kfifo_len(&entity->job_queue) == sizeof(sched_job)) ++ first = true; ++ ++ spin_unlock(&entity->queue_lock); ++ ++ /* first job wakes up scheduler */ ++ if (first) { ++ /* Add the entity to the run queue */ ++ amd_sched_rq_add_entity(entity->rq, entity); ++ amd_sched_wakeup(sched); ++ } ++ return added; ++} ++ ++/* job_finish is called after hw fence signaled, and ++ * the job had already been deleted from ring_mirror_list ++ */ ++static void amd_sched_job_finish(struct work_struct *work) ++{ ++ struct amd_sched_job *s_job = container_of(work, struct amd_sched_job, ++ finish_work); ++ struct amd_gpu_scheduler *sched = s_job->sched; ++ ++ /* remove job from ring_mirror_list */ ++ spin_lock(&sched->job_list_lock); ++ list_del_init(&s_job->node); ++ if (sched->timeout != MAX_SCHEDULE_TIMEOUT) { ++ struct amd_sched_job *next; ++ ++ spin_unlock(&sched->job_list_lock); ++ cancel_delayed_work_sync(&s_job->work_tdr); ++ spin_lock(&sched->job_list_lock); ++ ++ /* queue TDR for next job */ ++ next = list_first_entry_or_null(&sched->ring_mirror_list, ++ struct amd_sched_job, node); ++ ++ if (next) ++ schedule_delayed_work(&next->work_tdr, sched->timeout); ++ } ++ spin_unlock(&sched->job_list_lock); ++ sched->ops->free_job(s_job); ++} ++ ++static void amd_sched_job_finish_cb(struct dma_fence *f, ++ struct dma_fence_cb *cb) ++{ ++ struct amd_sched_job *job = container_of(cb, struct amd_sched_job, ++ finish_cb); ++ schedule_work(&job->finish_work); ++} ++ ++static void amd_sched_job_begin(struct amd_sched_job *s_job) ++{ ++ struct amd_gpu_scheduler *sched = s_job->sched; ++ ++ spin_lock(&sched->job_list_lock); ++ list_add_tail(&s_job->node, &sched->ring_mirror_list); ++ if (sched->timeout != MAX_SCHEDULE_TIMEOUT && ++ list_first_entry_or_null(&sched->ring_mirror_list, ++ struct amd_sched_job, node) == s_job) ++ schedule_delayed_work(&s_job->work_tdr, sched->timeout); ++ spin_unlock(&sched->job_list_lock); ++} ++ ++static void amd_sched_job_timedout(struct work_struct *work) ++{ ++ struct amd_sched_job *job = container_of(work, struct amd_sched_job, ++ work_tdr.work); ++ ++ job->sched->ops->timedout_job(job); ++} ++ ++void amd_sched_hw_job_reset(struct amd_gpu_scheduler *sched) ++{ ++ struct amd_sched_job *s_job; ++ ++ spin_lock(&sched->job_list_lock); ++ list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) { ++ if (s_job->s_fence->parent && ++ dma_fence_remove_callback(s_job->s_fence->parent, ++ &s_job->s_fence->cb)) { ++ dma_fence_put(s_job->s_fence->parent); ++ s_job->s_fence->parent = NULL; ++ atomic_dec(&sched->hw_rq_count); ++ } ++ } ++ spin_unlock(&sched->job_list_lock); ++} ++ ++void amd_sched_job_kickout(struct amd_sched_job *s_job) ++{ ++ struct amd_gpu_scheduler *sched = s_job->sched; ++ ++ spin_lock(&sched->job_list_lock); ++ list_del_init(&s_job->node); ++ spin_unlock(&sched->job_list_lock); ++} ++ ++void amd_sched_job_recovery(struct amd_gpu_scheduler *sched) ++{ ++ struct amd_sched_job *s_job, *tmp; ++ int r; ++ ++ spin_lock(&sched->job_list_lock); ++ s_job = list_first_entry_or_null(&sched->ring_mirror_list, ++ struct amd_sched_job, node); ++ if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT) ++ schedule_delayed_work(&s_job->work_tdr, sched->timeout); ++ ++ list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) { ++ struct amd_sched_fence *s_fence = s_job->s_fence; ++ struct dma_fence *fence; ++ ++ spin_unlock(&sched->job_list_lock); ++ fence = sched->ops->run_job(s_job); ++ atomic_inc(&sched->hw_rq_count); ++ if (fence) { ++ s_fence->parent = dma_fence_get(fence); ++ r = dma_fence_add_callback(fence, &s_fence->cb, ++ amd_sched_process_job); ++ if (r == -ENOENT) ++ amd_sched_process_job(fence, &s_fence->cb); ++ else if (r) ++ DRM_ERROR("fence add callback failed (%d)\n", ++ r); ++ dma_fence_put(fence); ++ } else { ++ DRM_ERROR("Failed to run job!\n"); ++ amd_sched_process_job(NULL, &s_fence->cb); ++ } ++ spin_lock(&sched->job_list_lock); ++ } ++ spin_unlock(&sched->job_list_lock); ++} ++ ++/** ++ * Submit a job to the job queue ++ * ++ * @sched_job The pointer to job required to submit ++ * ++ * Returns 0 for success, negative error code otherwise. ++ */ ++void amd_sched_entity_push_job(struct amd_sched_job *sched_job) ++{ ++ struct amd_sched_entity *entity = sched_job->s_entity; ++ ++ trace_amd_sched_job(sched_job); ++ dma_fence_add_callback(&sched_job->s_fence->finished, &sched_job->finish_cb, ++ amd_sched_job_finish_cb); ++ wait_event(entity->sched->job_scheduled, ++ amd_sched_entity_in(sched_job)); ++} ++ ++/* init a sched_job with basic field */ ++int amd_sched_job_init(struct amd_sched_job *job, ++ struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity, ++ void *owner) ++{ ++ job->sched = sched; ++ job->s_entity = entity; ++ job->s_fence = amd_sched_fence_create(entity, owner); ++ if (!job->s_fence) ++ return -ENOMEM; ++ job->id = atomic64_inc_return(&sched->job_id_count); ++ ++ INIT_WORK(&job->finish_work, amd_sched_job_finish); ++ INIT_LIST_HEAD(&job->node); ++ INIT_DELAYED_WORK(&job->work_tdr, amd_sched_job_timedout); ++ ++ return 0; ++} ++ ++/** ++ * Return ture if we can push more jobs to the hw. ++ */ ++static bool amd_sched_ready(struct amd_gpu_scheduler *sched) ++{ ++ return atomic_read(&sched->hw_rq_count) < ++ sched->hw_submission_limit; ++} ++ ++/** ++ * Wake up the scheduler when it is ready ++ */ ++static void amd_sched_wakeup(struct amd_gpu_scheduler *sched) ++{ ++ if (amd_sched_ready(sched)) ++ wake_up_interruptible(&sched->wake_up_worker); ++} ++ ++/** ++ * Select next entity to process ++*/ ++static struct amd_sched_entity * ++amd_sched_select_entity(struct amd_gpu_scheduler *sched) ++{ ++ struct amd_sched_entity *entity; ++ int i; ++ ++ if (!amd_sched_ready(sched)) ++ return NULL; ++ ++ /* Kernel run queue has higher priority than normal run queue*/ ++ for (i = AMD_SCHED_PRIORITY_MAX - 1; i >= AMD_SCHED_PRIORITY_MIN; i--) { ++ entity = amd_sched_rq_select_entity(&sched->sched_rq[i]); ++ if (entity) ++ break; ++ } ++ ++ return entity; ++} ++ ++static void amd_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb) ++{ ++ struct amd_sched_fence *s_fence = ++ container_of(cb, struct amd_sched_fence, cb); ++ struct amd_gpu_scheduler *sched = s_fence->sched; ++ ++ atomic_dec(&sched->hw_rq_count); ++ amd_sched_fence_finished(s_fence); ++ ++ trace_amd_sched_process_job(s_fence); ++ dma_fence_put(&s_fence->finished); ++ wake_up_interruptible(&sched->wake_up_worker); ++} ++ ++static bool amd_sched_blocked(struct amd_gpu_scheduler *sched) ++{ ++ if (kcl_kthread_should_park()) { ++ kcl_kthread_parkme(); ++ return true; ++ } ++ ++ return false; ++} ++ ++static int amd_sched_main(void *param) ++{ ++ struct sched_param sparam = {.sched_priority = 1}; ++ struct amd_gpu_scheduler *sched = (struct amd_gpu_scheduler *)param; ++ int r, count; ++ ++ sched_setscheduler(current, SCHED_FIFO, &sparam); ++ ++ while (!kthread_should_stop()) { ++ struct amd_sched_entity *entity = NULL; ++ struct amd_sched_fence *s_fence; ++ struct amd_sched_job *sched_job; ++ struct dma_fence *fence; ++ ++ wait_event_interruptible(sched->wake_up_worker, ++ (!amd_sched_blocked(sched) && ++ (entity = amd_sched_select_entity(sched))) || ++ kthread_should_stop()); ++ ++ if (!entity) ++ continue; ++ ++ sched_job = amd_sched_entity_pop_job(entity); ++ if (!sched_job) ++ continue; ++ ++ s_fence = sched_job->s_fence; ++ ++ atomic_inc(&sched->hw_rq_count); ++ amd_sched_job_begin(sched_job); ++ ++ fence = sched->ops->run_job(sched_job); ++ amd_sched_fence_scheduled(s_fence); ++ if (fence) { ++ s_fence->parent = dma_fence_get(fence); ++ r = dma_fence_add_callback(fence, &s_fence->cb, ++ amd_sched_process_job); ++ if (r == -ENOENT) ++ amd_sched_process_job(fence, &s_fence->cb); ++ else if (r) ++ DRM_ERROR("fence add callback failed (%d)\n", ++ r); ++ dma_fence_put(fence); ++ } else { ++ DRM_ERROR("Failed to run job!\n"); ++ amd_sched_process_job(NULL, &s_fence->cb); ++ } ++ ++ count = kfifo_out(&entity->job_queue, &sched_job, ++ sizeof(sched_job)); ++ WARN_ON(count != sizeof(sched_job)); ++ wake_up(&sched->job_scheduled); ++ } ++ return 0; ++} ++ ++/** ++ * Init a gpu scheduler instance ++ * ++ * @sched The pointer to the scheduler ++ * @ops The backend operations for this scheduler. ++ * @hw_submissions Number of hw submissions to do. ++ * @name Name used for debugging ++ * ++ * Return 0 on success, otherwise error code. ++*/ ++int amd_sched_init(struct amd_gpu_scheduler *sched, ++ const struct amd_sched_backend_ops *ops, ++ unsigned hw_submission, long timeout, const char *name) ++{ ++ int i; ++ sched->ops = ops; ++ sched->hw_submission_limit = hw_submission; ++ sched->name = name; ++ sched->timeout = timeout; ++ for (i = AMD_SCHED_PRIORITY_MIN; i < AMD_SCHED_PRIORITY_MAX; i++) ++ amd_sched_rq_init(&sched->sched_rq[i]); ++ ++ init_waitqueue_head(&sched->wake_up_worker); ++ init_waitqueue_head(&sched->job_scheduled); ++ INIT_LIST_HEAD(&sched->ring_mirror_list); ++ spin_lock_init(&sched->job_list_lock); ++ atomic_set(&sched->hw_rq_count, 0); ++ atomic64_set(&sched->job_id_count, 0); ++ ++ /* Each scheduler will run on a seperate kernel thread */ ++ sched->thread = kthread_run(amd_sched_main, sched, sched->name); ++ if (IS_ERR(sched->thread)) { ++ DRM_ERROR("Failed to create scheduler for %s.\n", name); ++ return PTR_ERR(sched->thread); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Destroy a gpu scheduler ++ * ++ * @sched The pointer to the scheduler ++ */ ++void amd_sched_fini(struct amd_gpu_scheduler *sched) ++{ ++ if (sched->thread) ++ kthread_stop(sched->thread); ++} +diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +new file mode 100644 +index 0000000..92aef31 +--- /dev/null ++++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +@@ -0,0 +1,174 @@ ++/* ++ * 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. ++ * ++ */ ++ ++#ifndef _GPU_SCHEDULER_H_ ++#define _GPU_SCHEDULER_H_ ++ ++#if (defined OS_NAME_RHEL) && (OS_VERSION_MAJOR == 6) ++#include <linux/kfifo-new.h> ++#else ++#include <linux/kfifo.h> ++#endif ++#include <kcl/kcl_fence.h> ++ ++struct amd_gpu_scheduler; ++struct amd_sched_rq; ++ ++/** ++ * A scheduler entity is a wrapper around a job queue or a group ++ * of other entities. Entities take turns emitting jobs from their ++ * job queues to corresponding hardware ring based on scheduling ++ * policy. ++*/ ++struct amd_sched_entity { ++ struct list_head list; ++ struct amd_sched_rq *rq; ++ struct amd_gpu_scheduler *sched; ++ ++ spinlock_t queue_lock; ++ struct kfifo job_queue; ++ ++ atomic_t fence_seq; ++ uint64_t fence_context; ++ ++ struct dma_fence *dependency; ++ struct dma_fence_cb cb; ++}; ++ ++/** ++ * Run queue is a set of entities scheduling command submissions for ++ * one specific ring. It implements the scheduling policy that selects ++ * the next entity to emit commands from. ++*/ ++struct amd_sched_rq { ++ spinlock_t lock; ++ struct list_head entities; ++ struct amd_sched_entity *current_entity; ++}; ++ ++struct amd_sched_fence { ++ struct dma_fence scheduled; ++ struct dma_fence finished; ++ struct dma_fence_cb cb; ++ struct dma_fence *parent; ++ struct amd_gpu_scheduler *sched; ++ spinlock_t lock; ++ void *owner; ++}; ++ ++struct amd_sched_job { ++ struct amd_gpu_scheduler *sched; ++ struct amd_sched_entity *s_entity; ++ struct amd_sched_fence *s_fence; ++ struct dma_fence_cb finish_cb; ++ struct work_struct finish_work; ++ struct list_head node; ++ struct delayed_work work_tdr; ++ uint64_t id; ++ atomic_t karma; ++}; ++ ++extern const struct dma_fence_ops amd_sched_fence_ops_scheduled; ++extern const struct dma_fence_ops amd_sched_fence_ops_finished; ++static inline struct amd_sched_fence *to_amd_sched_fence(struct dma_fence *f) ++{ ++ if (f->ops == &amd_sched_fence_ops_scheduled) ++ return container_of(f, struct amd_sched_fence, scheduled); ++ ++ if (f->ops == &amd_sched_fence_ops_finished) ++ return container_of(f, struct amd_sched_fence, finished); ++ ++ return NULL; ++} ++ ++static inline bool amd_sched_invalidate_job(struct amd_sched_job *s_job, int threshold) ++{ ++ return (s_job && atomic_inc_return(&s_job->karma) > threshold); ++} ++ ++/** ++ * Define the backend operations called by the scheduler, ++ * these functions should be implemented in driver side ++*/ ++struct amd_sched_backend_ops { ++ struct dma_fence *(*dependency)(struct amd_sched_job *sched_job); ++ struct dma_fence *(*run_job)(struct amd_sched_job *sched_job); ++ void (*timedout_job)(struct amd_sched_job *sched_job); ++ void (*free_job)(struct amd_sched_job *sched_job); ++}; ++ ++enum amd_sched_priority { ++ AMD_SCHED_PRIORITY_MIN, ++ AMD_SCHED_PRIORITY_NORMAL = AMD_SCHED_PRIORITY_MIN, ++ AMD_SCHED_PRIORITY_KERNEL, ++ AMD_SCHED_PRIORITY_MAX ++}; ++ ++/** ++ * One scheduler is implemented for each hardware ring ++*/ ++struct amd_gpu_scheduler { ++ const struct amd_sched_backend_ops *ops; ++ uint32_t hw_submission_limit; ++ long timeout; ++ const char *name; ++ struct amd_sched_rq sched_rq[AMD_SCHED_PRIORITY_MAX]; ++ wait_queue_head_t wake_up_worker; ++ wait_queue_head_t job_scheduled; ++ atomic_t hw_rq_count; ++ atomic64_t job_id_count; ++ struct task_struct *thread; ++ struct list_head ring_mirror_list; ++ spinlock_t job_list_lock; ++}; ++ ++int amd_sched_init(struct amd_gpu_scheduler *sched, ++ const struct amd_sched_backend_ops *ops, ++ uint32_t hw_submission, long timeout, const char *name); ++void amd_sched_fini(struct amd_gpu_scheduler *sched); ++ ++int amd_sched_entity_init(struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity, ++ struct amd_sched_rq *rq, ++ uint32_t jobs); ++void amd_sched_entity_fini(struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity); ++void amd_sched_entity_push_job(struct amd_sched_job *sched_job); ++ ++int amd_sched_fence_slab_init(void); ++void amd_sched_fence_slab_fini(void); ++ ++struct amd_sched_fence *amd_sched_fence_create( ++ struct amd_sched_entity *s_entity, void *owner); ++void amd_sched_fence_scheduled(struct amd_sched_fence *fence); ++void amd_sched_fence_finished(struct amd_sched_fence *fence); ++int amd_sched_job_init(struct amd_sched_job *job, ++ struct amd_gpu_scheduler *sched, ++ struct amd_sched_entity *entity, ++ void *owner); ++void amd_sched_hw_job_reset(struct amd_gpu_scheduler *sched); ++void amd_sched_job_recovery(struct amd_gpu_scheduler *sched); ++bool amd_sched_dependency_optimized(struct dma_fence* fence, ++ struct amd_sched_entity *entity); ++void amd_sched_job_kickout(struct amd_sched_job *s_job); ++#endif +diff --git a/drivers/gpu/drm/amd/scheduler/sched_fence.c b/drivers/gpu/drm/amd/scheduler/sched_fence.c +new file mode 100644 +index 0000000..8620615 +--- /dev/null ++++ b/drivers/gpu/drm/amd/scheduler/sched_fence.c +@@ -0,0 +1,181 @@ ++/* ++ * 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 <linux/wait.h> ++#include <linux/sched.h> ++#include <drm/drmP.h> ++#include "gpu_scheduler.h" ++ ++static struct kmem_cache *sched_fence_slab; ++ ++int amd_sched_fence_slab_init(void) ++{ ++ sched_fence_slab = kmem_cache_create( ++ "amd_sched_fence", sizeof(struct amd_sched_fence), 0, ++ SLAB_HWCACHE_ALIGN, NULL); ++ if (!sched_fence_slab) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++void amd_sched_fence_slab_fini(void) ++{ ++ rcu_barrier(); ++ kmem_cache_destroy(sched_fence_slab); ++} ++ ++struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *entity, ++ void *owner) ++{ ++ struct amd_sched_fence *fence = NULL; ++ unsigned seq; ++ ++ fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL); ++ if (fence == NULL) ++ return NULL; ++ ++ fence->owner = owner; ++ fence->sched = entity->sched; ++ spin_lock_init(&fence->lock); ++ ++ seq = atomic_inc_return(&entity->fence_seq); ++ kcl_fence_init(&fence->scheduled, &amd_sched_fence_ops_scheduled, ++ &fence->lock, entity->fence_context, seq); ++ kcl_fence_init(&fence->finished, &amd_sched_fence_ops_finished, ++ &fence->lock, entity->fence_context + 1, seq); ++ ++ return fence; ++} ++ ++void amd_sched_fence_scheduled(struct amd_sched_fence *fence) ++{ ++ int ret = dma_fence_signal(&fence->scheduled); ++ ++ if (!ret) ++ DMA_FENCE_TRACE(&fence->scheduled, ++ "signaled from irq context\n"); ++ else ++ DMA_FENCE_TRACE(&fence->scheduled, ++ "was already signaled\n"); ++} ++ ++void amd_sched_fence_finished(struct amd_sched_fence *fence) ++{ ++ int ret = dma_fence_signal(&fence->finished); ++ ++ if (!ret) ++ DMA_FENCE_TRACE(&fence->finished, ++ "signaled from irq context\n"); ++ else ++ DMA_FENCE_TRACE(&fence->finished, ++ "was already signaled\n"); ++} ++ ++static const char *amd_sched_fence_get_driver_name(struct dma_fence *fence) ++{ ++ return "amd_sched"; ++} ++ ++static const char *amd_sched_fence_get_timeline_name(struct dma_fence *f) ++{ ++ struct amd_sched_fence *fence = to_amd_sched_fence(f); ++ return (const char *)fence->sched->name; ++} ++ ++static bool amd_sched_fence_enable_signaling(struct dma_fence *f) ++{ ++ return true; ++} ++ ++/** ++ * amd_sched_fence_free - free up the fence memory ++ * ++ * @rcu: RCU callback head ++ * ++ * Free up the fence memory after the RCU grace period. ++ */ ++static void amd_sched_fence_free(struct rcu_head *rcu) ++{ ++ struct dma_fence *f = container_of(rcu, struct dma_fence, rcu); ++ struct amd_sched_fence *fence = to_amd_sched_fence(f); ++ ++ dma_fence_put(fence->parent); ++ kmem_cache_free(sched_fence_slab, fence); ++} ++ ++/** ++ * amd_sched_fence_release_scheduled - callback that fence can be freed ++ * ++ * @fence: fence ++ * ++ * This function is called when the reference count becomes zero. ++ * It just RCU schedules freeing up the fence. ++ */ ++static void amd_sched_fence_release_scheduled(struct dma_fence *f) ++{ ++ struct amd_sched_fence *fence = to_amd_sched_fence(f); ++ ++ call_rcu(&fence->finished.rcu, amd_sched_fence_free); ++} ++ ++/** ++ * amd_sched_fence_release_finished - drop extra reference ++ * ++ * @f: fence ++ * ++ * Drop the extra reference from the scheduled fence to the base fence. ++ */ ++static void amd_sched_fence_release_finished(struct dma_fence *f) ++{ ++ struct amd_sched_fence *fence = to_amd_sched_fence(f); ++ ++ dma_fence_put(&fence->scheduled); ++} ++ ++const struct dma_fence_ops amd_sched_fence_ops_scheduled = { ++ .get_driver_name = amd_sched_fence_get_driver_name, ++ .get_timeline_name = amd_sched_fence_get_timeline_name, ++ .enable_signaling = amd_sched_fence_enable_signaling, ++ .signaled = NULL, ++#if defined(BUILD_AS_DKMS) && !defined(OS_NAME_RHEL_7_4) ++ .wait = kcl_fence_default_wait, ++#else ++ .wait = dma_fence_default_wait, ++#endif ++ .release = amd_sched_fence_release_scheduled, ++}; ++ ++const struct dma_fence_ops amd_sched_fence_ops_finished = { ++ .get_driver_name = amd_sched_fence_get_driver_name, ++ .get_timeline_name = amd_sched_fence_get_timeline_name, ++ .enable_signaling = amd_sched_fence_enable_signaling, ++ .signaled = NULL, ++#if defined(BUILD_AS_DKMS) && !defined(OS_NAME_RHEL_7_4) ++ .wait = kcl_fence_default_wait, ++#else ++ .wait = dma_fence_default_wait, ++#endif ++ .release = amd_sched_fence_release_finished, ++}; +-- +2.7.4 + |