Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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