linux/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015 Advanced Micro Devices, Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 *
  23 */
  24#include <linux/kthread.h>
  25#include <linux/wait.h>
  26#include <linux/sched.h>
  27#include <drm/drmP.h>
  28#include "gpu_scheduler.h"
  29
  30#define CREATE_TRACE_POINTS
  31#include "gpu_sched_trace.h"
  32
  33static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity);
  34static void amd_sched_wakeup(struct amd_gpu_scheduler *sched);
  35static void amd_sched_process_job(struct fence *f, struct fence_cb *cb);
  36
  37struct kmem_cache *sched_fence_slab;
  38atomic_t sched_fence_slab_ref = ATOMIC_INIT(0);
  39
  40/* Initialize a given run queue struct */
  41static void amd_sched_rq_init(struct amd_sched_rq *rq)
  42{
  43        spin_lock_init(&rq->lock);
  44        INIT_LIST_HEAD(&rq->entities);
  45        rq->current_entity = NULL;
  46}
  47
  48static void amd_sched_rq_add_entity(struct amd_sched_rq *rq,
  49                                    struct amd_sched_entity *entity)
  50{
  51        if (!list_empty(&entity->list))
  52                return;
  53        spin_lock(&rq->lock);
  54        list_add_tail(&entity->list, &rq->entities);
  55        spin_unlock(&rq->lock);
  56}
  57
  58static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq,
  59                                       struct amd_sched_entity *entity)
  60{
  61        if (list_empty(&entity->list))
  62                return;
  63        spin_lock(&rq->lock);
  64        list_del_init(&entity->list);
  65        if (rq->current_entity == entity)
  66                rq->current_entity = NULL;
  67        spin_unlock(&rq->lock);
  68}
  69
  70/**
  71 * Select an entity which could provide a job to run
  72 *
  73 * @rq          The run queue to check.
  74 *
  75 * Try to find a ready entity, returns NULL if none found.
  76 */
  77static struct amd_sched_entity *
  78amd_sched_rq_select_entity(struct amd_sched_rq *rq)
  79{
  80        struct amd_sched_entity *entity;
  81
  82        spin_lock(&rq->lock);
  83
  84        entity = rq->current_entity;
  85        if (entity) {
  86                list_for_each_entry_continue(entity, &rq->entities, list) {
  87                        if (amd_sched_entity_is_ready(entity)) {
  88                                rq->current_entity = entity;
  89                                spin_unlock(&rq->lock);
  90                                return entity;
  91                        }
  92                }
  93        }
  94
  95        list_for_each_entry(entity, &rq->entities, list) {
  96
  97                if (amd_sched_entity_is_ready(entity)) {
  98                        rq->current_entity = entity;
  99                        spin_unlock(&rq->lock);
 100                        return entity;
 101                }
 102
 103                if (entity == rq->current_entity)
 104                        break;
 105        }
 106
 107        spin_unlock(&rq->lock);
 108
 109        return NULL;
 110}
 111
 112/**
 113 * Init a context entity used by scheduler when submit to HW ring.
 114 *
 115 * @sched       The pointer to the scheduler
 116 * @entity      The pointer to a valid amd_sched_entity
 117 * @rq          The run queue this entity belongs
 118 * @kernel      If this is an entity for the kernel
 119 * @jobs        The max number of jobs in the job queue
 120 *
 121 * return 0 if succeed. negative error code on failure
 122*/
 123int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
 124                          struct amd_sched_entity *entity,
 125                          struct amd_sched_rq *rq,
 126                          uint32_t jobs)
 127{
 128        int r;
 129
 130        if (!(sched && entity && rq))
 131                return -EINVAL;
 132
 133        memset(entity, 0, sizeof(struct amd_sched_entity));
 134        INIT_LIST_HEAD(&entity->list);
 135        entity->rq = rq;
 136        entity->sched = sched;
 137
 138        spin_lock_init(&entity->queue_lock);
 139        r = kfifo_alloc(&entity->job_queue, jobs * sizeof(void *), GFP_KERNEL);
 140        if (r)
 141                return r;
 142
 143        atomic_set(&entity->fence_seq, 0);
 144        entity->fence_context = fence_context_alloc(2);
 145
 146        return 0;
 147}
 148
 149/**
 150 * Query if entity is initialized
 151 *
 152 * @sched       Pointer to scheduler instance
 153 * @entity      The pointer to a valid scheduler entity
 154 *
 155 * return true if entity is initialized, false otherwise
 156*/
 157static bool amd_sched_entity_is_initialized(struct amd_gpu_scheduler *sched,
 158                                            struct amd_sched_entity *entity)
 159{
 160        return entity->sched == sched &&
 161                entity->rq != NULL;
 162}
 163
 164/**
 165 * Check if entity is idle
 166 *
 167 * @entity      The pointer to a valid scheduler entity
 168 *
 169 * Return true if entity don't has any unscheduled jobs.
 170 */
 171static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity)
 172{
 173        rmb();
 174        if (kfifo_is_empty(&entity->job_queue))
 175                return true;
 176
 177        return false;
 178}
 179
 180/**
 181 * Check if entity is ready
 182 *
 183 * @entity      The pointer to a valid scheduler entity
 184 *
 185 * Return true if entity could provide a job.
 186 */
 187static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity)
 188{
 189        if (kfifo_is_empty(&entity->job_queue))
 190                return false;
 191
 192        if (ACCESS_ONCE(entity->dependency))
 193                return false;
 194
 195        return true;
 196}
 197
 198/**
 199 * Destroy a context entity
 200 *
 201 * @sched       Pointer to scheduler instance
 202 * @entity      The pointer to a valid scheduler entity
 203 *
 204 * Cleanup and free the allocated resources.
 205 */
 206void amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
 207                           struct amd_sched_entity *entity)
 208{
 209        struct amd_sched_rq *rq = entity->rq;
 210
 211        if (!amd_sched_entity_is_initialized(sched, entity))
 212                return;
 213
 214        /**
 215         * The client will not queue more IBs during this fini, consume existing
 216         * queued IBs
 217        */
 218        wait_event(sched->job_scheduled, amd_sched_entity_is_idle(entity));
 219
 220        amd_sched_rq_remove_entity(rq, entity);
 221        kfifo_free(&entity->job_queue);
 222}
 223
 224static void amd_sched_entity_wakeup(struct fence *f, struct fence_cb *cb)
 225{
 226        struct amd_sched_entity *entity =
 227                container_of(cb, struct amd_sched_entity, cb);
 228        entity->dependency = NULL;
 229        fence_put(f);
 230        amd_sched_wakeup(entity->sched);
 231}
 232
 233static void amd_sched_entity_clear_dep(struct fence *f, struct fence_cb *cb)
 234{
 235        struct amd_sched_entity *entity =
 236                container_of(cb, struct amd_sched_entity, cb);
 237        entity->dependency = NULL;
 238        fence_put(f);
 239}
 240
 241static bool amd_sched_entity_add_dependency_cb(struct amd_sched_entity *entity)
 242{
 243        struct amd_gpu_scheduler *sched = entity->sched;
 244        struct fence * fence = entity->dependency;
 245        struct amd_sched_fence *s_fence;
 246
 247        if (fence->context == entity->fence_context) {
 248                /* We can ignore fences from ourself */
 249                fence_put(entity->dependency);
 250                return false;
 251        }
 252
 253        s_fence = to_amd_sched_fence(fence);
 254        if (s_fence && s_fence->sched == sched) {
 255
 256                /*
 257                 * Fence is from the same scheduler, only need to wait for
 258                 * it to be scheduled
 259                 */
 260                fence = fence_get(&s_fence->scheduled);
 261                fence_put(entity->dependency);
 262                entity->dependency = fence;
 263                if (!fence_add_callback(fence, &entity->cb,
 264                                        amd_sched_entity_clear_dep))
 265                        return true;
 266
 267                /* Ignore it when it is already scheduled */
 268                fence_put(fence);
 269                return false;
 270        }
 271
 272        if (!fence_add_callback(entity->dependency, &entity->cb,
 273                                amd_sched_entity_wakeup))
 274                return true;
 275
 276        fence_put(entity->dependency);
 277        return false;
 278}
 279
 280static struct amd_sched_job *
 281amd_sched_entity_pop_job(struct amd_sched_entity *entity)
 282{
 283        struct amd_gpu_scheduler *sched = entity->sched;
 284        struct amd_sched_job *sched_job;
 285
 286        if (!kfifo_out_peek(&entity->job_queue, &sched_job, sizeof(sched_job)))
 287                return NULL;
 288
 289        while ((entity->dependency = sched->ops->dependency(sched_job)))
 290                if (amd_sched_entity_add_dependency_cb(entity))
 291                        return NULL;
 292
 293        return sched_job;
 294}
 295
 296/**
 297 * Helper to submit a job to the job queue
 298 *
 299 * @sched_job           The pointer to job required to submit
 300 *
 301 * Returns true if we could submit the job.
 302 */
 303static bool amd_sched_entity_in(struct amd_sched_job *sched_job)
 304{
 305        struct amd_gpu_scheduler *sched = sched_job->sched;
 306        struct amd_sched_entity *entity = sched_job->s_entity;
 307        bool added, first = false;
 308
 309        spin_lock(&entity->queue_lock);
 310        added = kfifo_in(&entity->job_queue, &sched_job,
 311                        sizeof(sched_job)) == sizeof(sched_job);
 312
 313        if (added && kfifo_len(&entity->job_queue) == sizeof(sched_job))
 314                first = true;
 315
 316        spin_unlock(&entity->queue_lock);
 317
 318        /* first job wakes up scheduler */
 319        if (first) {
 320                /* Add the entity to the run queue */
 321                amd_sched_rq_add_entity(entity->rq, entity);
 322                amd_sched_wakeup(sched);
 323        }
 324        return added;
 325}
 326
 327/* job_finish is called after hw fence signaled, and
 328 * the job had already been deleted from ring_mirror_list
 329 */
 330static void amd_sched_job_finish(struct work_struct *work)
 331{
 332        struct amd_sched_job *s_job = container_of(work, struct amd_sched_job,
 333                                                   finish_work);
 334        struct amd_gpu_scheduler *sched = s_job->sched;
 335
 336        /* remove job from ring_mirror_list */
 337        spin_lock(&sched->job_list_lock);
 338        list_del_init(&s_job->node);
 339        if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
 340                struct amd_sched_job *next;
 341
 342                spin_unlock(&sched->job_list_lock);
 343                cancel_delayed_work_sync(&s_job->work_tdr);
 344                spin_lock(&sched->job_list_lock);
 345
 346                /* queue TDR for next job */
 347                next = list_first_entry_or_null(&sched->ring_mirror_list,
 348                                                struct amd_sched_job, node);
 349
 350                if (next)
 351                        schedule_delayed_work(&next->work_tdr, sched->timeout);
 352        }
 353        spin_unlock(&sched->job_list_lock);
 354        sched->ops->free_job(s_job);
 355}
 356
 357static void amd_sched_job_finish_cb(struct fence *f, struct fence_cb *cb)
 358{
 359        struct amd_sched_job *job = container_of(cb, struct amd_sched_job,
 360                                                 finish_cb);
 361        schedule_work(&job->finish_work);
 362}
 363
 364static void amd_sched_job_begin(struct amd_sched_job *s_job)
 365{
 366        struct amd_gpu_scheduler *sched = s_job->sched;
 367
 368        spin_lock(&sched->job_list_lock);
 369        list_add_tail(&s_job->node, &sched->ring_mirror_list);
 370        if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
 371            list_first_entry_or_null(&sched->ring_mirror_list,
 372                                     struct amd_sched_job, node) == s_job)
 373                schedule_delayed_work(&s_job->work_tdr, sched->timeout);
 374        spin_unlock(&sched->job_list_lock);
 375}
 376
 377static void amd_sched_job_timedout(struct work_struct *work)
 378{
 379        struct amd_sched_job *job = container_of(work, struct amd_sched_job,
 380                                                 work_tdr.work);
 381
 382        job->sched->ops->timedout_job(job);
 383}
 384
 385void amd_sched_hw_job_reset(struct amd_gpu_scheduler *sched)
 386{
 387        struct amd_sched_job *s_job;
 388
 389        spin_lock(&sched->job_list_lock);
 390        list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) {
 391                if (fence_remove_callback(s_job->s_fence->parent, &s_job->s_fence->cb)) {
 392                        fence_put(s_job->s_fence->parent);
 393                        s_job->s_fence->parent = NULL;
 394                }
 395        }
 396        atomic_set(&sched->hw_rq_count, 0);
 397        spin_unlock(&sched->job_list_lock);
 398}
 399
 400void amd_sched_job_recovery(struct amd_gpu_scheduler *sched)
 401{
 402        struct amd_sched_job *s_job, *tmp;
 403        int r;
 404
 405        spin_lock(&sched->job_list_lock);
 406        s_job = list_first_entry_or_null(&sched->ring_mirror_list,
 407                                         struct amd_sched_job, node);
 408        if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT)
 409                schedule_delayed_work(&s_job->work_tdr, sched->timeout);
 410
 411        list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
 412                struct amd_sched_fence *s_fence = s_job->s_fence;
 413                struct fence *fence;
 414
 415                spin_unlock(&sched->job_list_lock);
 416                fence = sched->ops->run_job(s_job);
 417                atomic_inc(&sched->hw_rq_count);
 418                if (fence) {
 419                        s_fence->parent = fence_get(fence);
 420                        r = fence_add_callback(fence, &s_fence->cb,
 421                                               amd_sched_process_job);
 422                        if (r == -ENOENT)
 423                                amd_sched_process_job(fence, &s_fence->cb);
 424                        else if (r)
 425                                DRM_ERROR("fence add callback failed (%d)\n",
 426                                          r);
 427                        fence_put(fence);
 428                } else {
 429                        DRM_ERROR("Failed to run job!\n");
 430                        amd_sched_process_job(NULL, &s_fence->cb);
 431                }
 432                spin_lock(&sched->job_list_lock);
 433        }
 434        spin_unlock(&sched->job_list_lock);
 435}
 436
 437/**
 438 * Submit a job to the job queue
 439 *
 440 * @sched_job           The pointer to job required to submit
 441 *
 442 * Returns 0 for success, negative error code otherwise.
 443 */
 444void amd_sched_entity_push_job(struct amd_sched_job *sched_job)
 445{
 446        struct amd_sched_entity *entity = sched_job->s_entity;
 447
 448        trace_amd_sched_job(sched_job);
 449        fence_add_callback(&sched_job->s_fence->finished, &sched_job->finish_cb,
 450                           amd_sched_job_finish_cb);
 451        wait_event(entity->sched->job_scheduled,
 452                   amd_sched_entity_in(sched_job));
 453}
 454
 455/* init a sched_job with basic field */
 456int amd_sched_job_init(struct amd_sched_job *job,
 457                       struct amd_gpu_scheduler *sched,
 458                       struct amd_sched_entity *entity,
 459                       void *owner)
 460{
 461        job->sched = sched;
 462        job->s_entity = entity;
 463        job->s_fence = amd_sched_fence_create(entity, owner);
 464        if (!job->s_fence)
 465                return -ENOMEM;
 466
 467        INIT_WORK(&job->finish_work, amd_sched_job_finish);
 468        INIT_LIST_HEAD(&job->node);
 469        INIT_DELAYED_WORK(&job->work_tdr, amd_sched_job_timedout);
 470
 471        return 0;
 472}
 473
 474/**
 475 * Return ture if we can push more jobs to the hw.
 476 */
 477static bool amd_sched_ready(struct amd_gpu_scheduler *sched)
 478{
 479        return atomic_read(&sched->hw_rq_count) <
 480                sched->hw_submission_limit;
 481}
 482
 483/**
 484 * Wake up the scheduler when it is ready
 485 */
 486static void amd_sched_wakeup(struct amd_gpu_scheduler *sched)
 487{
 488        if (amd_sched_ready(sched))
 489                wake_up_interruptible(&sched->wake_up_worker);
 490}
 491
 492/**
 493 * Select next entity to process
 494*/
 495static struct amd_sched_entity *
 496amd_sched_select_entity(struct amd_gpu_scheduler *sched)
 497{
 498        struct amd_sched_entity *entity;
 499        int i;
 500
 501        if (!amd_sched_ready(sched))
 502                return NULL;
 503
 504        /* Kernel run queue has higher priority than normal run queue*/
 505        for (i = 0; i < AMD_SCHED_MAX_PRIORITY; i++) {
 506                entity = amd_sched_rq_select_entity(&sched->sched_rq[i]);
 507                if (entity)
 508                        break;
 509        }
 510
 511        return entity;
 512}
 513
 514static void amd_sched_process_job(struct fence *f, struct fence_cb *cb)
 515{
 516        struct amd_sched_fence *s_fence =
 517                container_of(cb, struct amd_sched_fence, cb);
 518        struct amd_gpu_scheduler *sched = s_fence->sched;
 519
 520        atomic_dec(&sched->hw_rq_count);
 521        amd_sched_fence_finished(s_fence);
 522
 523        trace_amd_sched_process_job(s_fence);
 524        fence_put(&s_fence->finished);
 525        wake_up_interruptible(&sched->wake_up_worker);
 526}
 527
 528static bool amd_sched_blocked(struct amd_gpu_scheduler *sched)
 529{
 530        if (kthread_should_park()) {
 531                kthread_parkme();
 532                return true;
 533        }
 534
 535        return false;
 536}
 537
 538static int amd_sched_main(void *param)
 539{
 540        struct sched_param sparam = {.sched_priority = 1};
 541        struct amd_gpu_scheduler *sched = (struct amd_gpu_scheduler *)param;
 542        int r, count;
 543
 544        sched_setscheduler(current, SCHED_FIFO, &sparam);
 545
 546        while (!kthread_should_stop()) {
 547                struct amd_sched_entity *entity = NULL;
 548                struct amd_sched_fence *s_fence;
 549                struct amd_sched_job *sched_job;
 550                struct fence *fence;
 551
 552                wait_event_interruptible(sched->wake_up_worker,
 553                                         (!amd_sched_blocked(sched) &&
 554                                          (entity = amd_sched_select_entity(sched))) ||
 555                                         kthread_should_stop());
 556
 557                if (!entity)
 558                        continue;
 559
 560                sched_job = amd_sched_entity_pop_job(entity);
 561                if (!sched_job)
 562                        continue;
 563
 564                s_fence = sched_job->s_fence;
 565
 566                atomic_inc(&sched->hw_rq_count);
 567                amd_sched_job_begin(sched_job);
 568
 569                fence = sched->ops->run_job(sched_job);
 570                amd_sched_fence_scheduled(s_fence);
 571                if (fence) {
 572                        s_fence->parent = fence_get(fence);
 573                        r = fence_add_callback(fence, &s_fence->cb,
 574                                               amd_sched_process_job);
 575                        if (r == -ENOENT)
 576                                amd_sched_process_job(fence, &s_fence->cb);
 577                        else if (r)
 578                                DRM_ERROR("fence add callback failed (%d)\n",
 579                                          r);
 580                        fence_put(fence);
 581                } else {
 582                        DRM_ERROR("Failed to run job!\n");
 583                        amd_sched_process_job(NULL, &s_fence->cb);
 584                }
 585
 586                count = kfifo_out(&entity->job_queue, &sched_job,
 587                                sizeof(sched_job));
 588                WARN_ON(count != sizeof(sched_job));
 589                wake_up(&sched->job_scheduled);
 590        }
 591        return 0;
 592}
 593
 594/**
 595 * Init a gpu scheduler instance
 596 *
 597 * @sched               The pointer to the scheduler
 598 * @ops                 The backend operations for this scheduler.
 599 * @hw_submissions      Number of hw submissions to do.
 600 * @name                Name used for debugging
 601 *
 602 * Return 0 on success, otherwise error code.
 603*/
 604int amd_sched_init(struct amd_gpu_scheduler *sched,
 605                   const struct amd_sched_backend_ops *ops,
 606                   unsigned hw_submission, long timeout, const char *name)
 607{
 608        int i;
 609        sched->ops = ops;
 610        sched->hw_submission_limit = hw_submission;
 611        sched->name = name;
 612        sched->timeout = timeout;
 613        for (i = 0; i < AMD_SCHED_MAX_PRIORITY; i++)
 614                amd_sched_rq_init(&sched->sched_rq[i]);
 615
 616        init_waitqueue_head(&sched->wake_up_worker);
 617        init_waitqueue_head(&sched->job_scheduled);
 618        INIT_LIST_HEAD(&sched->ring_mirror_list);
 619        spin_lock_init(&sched->job_list_lock);
 620        atomic_set(&sched->hw_rq_count, 0);
 621        if (atomic_inc_return(&sched_fence_slab_ref) == 1) {
 622                sched_fence_slab = kmem_cache_create(
 623                        "amd_sched_fence", sizeof(struct amd_sched_fence), 0,
 624                        SLAB_HWCACHE_ALIGN, NULL);
 625                if (!sched_fence_slab)
 626                        return -ENOMEM;
 627        }
 628
 629        /* Each scheduler will run on a seperate kernel thread */
 630        sched->thread = kthread_run(amd_sched_main, sched, sched->name);
 631        if (IS_ERR(sched->thread)) {
 632                DRM_ERROR("Failed to create scheduler for %s.\n", name);
 633                return PTR_ERR(sched->thread);
 634        }
 635
 636        return 0;
 637}
 638
 639/**
 640 * Destroy a gpu scheduler
 641 *
 642 * @sched       The pointer to the scheduler
 643 */
 644void amd_sched_fini(struct amd_gpu_scheduler *sched)
 645{
 646        if (sched->thread)
 647                kthread_stop(sched->thread);
 648        if (atomic_dec_and_test(&sched_fence_slab_ref))
 649                kmem_cache_destroy(sched_fence_slab);
 650}
 651