1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47#include <linux/kthread.h>
48#include <linux/wait.h>
49#include <linux/sched.h>
50#include <linux/completion.h>
51#include <uapi/linux/sched/types.h>
52
53#include <drm/drm_print.h>
54#include <drm/gpu_scheduler.h>
55#include <drm/spsc_queue.h>
56
57#define CREATE_TRACE_POINTS
58#include "gpu_scheduler_trace.h"
59
60#define to_drm_sched_job(sched_job) \
61 container_of((sched_job), struct drm_sched_job, queue_node)
62
63static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
64
65
66
67
68
69
70
71
72static void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
73 struct drm_sched_rq *rq)
74{
75 spin_lock_init(&rq->lock);
76 INIT_LIST_HEAD(&rq->entities);
77 rq->current_entity = NULL;
78 rq->sched = sched;
79}
80
81
82
83
84
85
86
87
88
89void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
90 struct drm_sched_entity *entity)
91{
92 if (!list_empty(&entity->list))
93 return;
94 spin_lock(&rq->lock);
95 atomic_inc(&rq->sched->score);
96 list_add_tail(&entity->list, &rq->entities);
97 spin_unlock(&rq->lock);
98}
99
100
101
102
103
104
105
106
107
108void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
109 struct drm_sched_entity *entity)
110{
111 if (list_empty(&entity->list))
112 return;
113 spin_lock(&rq->lock);
114 atomic_dec(&rq->sched->score);
115 list_del_init(&entity->list);
116 if (rq->current_entity == entity)
117 rq->current_entity = NULL;
118 spin_unlock(&rq->lock);
119}
120
121
122
123
124
125
126
127
128static struct drm_sched_entity *
129drm_sched_rq_select_entity(struct drm_sched_rq *rq)
130{
131 struct drm_sched_entity *entity;
132
133 spin_lock(&rq->lock);
134
135 entity = rq->current_entity;
136 if (entity) {
137 list_for_each_entry_continue(entity, &rq->entities, list) {
138 if (drm_sched_entity_is_ready(entity)) {
139 rq->current_entity = entity;
140 reinit_completion(&entity->entity_idle);
141 spin_unlock(&rq->lock);
142 return entity;
143 }
144 }
145 }
146
147 list_for_each_entry(entity, &rq->entities, list) {
148
149 if (drm_sched_entity_is_ready(entity)) {
150 rq->current_entity = entity;
151 reinit_completion(&entity->entity_idle);
152 spin_unlock(&rq->lock);
153 return entity;
154 }
155
156 if (entity == rq->current_entity)
157 break;
158 }
159
160 spin_unlock(&rq->lock);
161
162 return NULL;
163}
164
165
166
167
168
169
170
171
172
173bool drm_sched_dependency_optimized(struct dma_fence* fence,
174 struct drm_sched_entity *entity)
175{
176 struct drm_gpu_scheduler *sched = entity->rq->sched;
177 struct drm_sched_fence *s_fence;
178
179 if (!fence || dma_fence_is_signaled(fence))
180 return false;
181 if (fence->context == entity->fence_context)
182 return true;
183 s_fence = to_drm_sched_fence(fence);
184 if (s_fence && s_fence->sched == sched)
185 return true;
186
187 return false;
188}
189EXPORT_SYMBOL(drm_sched_dependency_optimized);
190
191
192
193
194
195
196
197
198static void drm_sched_start_timeout(struct drm_gpu_scheduler *sched)
199{
200 if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
201 !list_empty(&sched->ring_mirror_list))
202 schedule_delayed_work(&sched->work_tdr, sched->timeout);
203}
204
205
206
207
208
209
210
211
212void drm_sched_fault(struct drm_gpu_scheduler *sched)
213{
214 mod_delayed_work(system_wq, &sched->work_tdr, 0);
215}
216EXPORT_SYMBOL(drm_sched_fault);
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched)
232{
233 unsigned long sched_timeout, now = jiffies;
234
235 sched_timeout = sched->work_tdr.timer.expires;
236
237
238
239
240
241 if (mod_delayed_work(system_wq, &sched->work_tdr, MAX_SCHEDULE_TIMEOUT)
242 && time_after(sched_timeout, now))
243 return sched_timeout - now;
244 else
245 return sched->timeout;
246}
247EXPORT_SYMBOL(drm_sched_suspend_timeout);
248
249
250
251
252
253
254
255
256
257
258void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched,
259 unsigned long remaining)
260{
261 unsigned long flags;
262
263 spin_lock_irqsave(&sched->job_list_lock, flags);
264
265 if (list_empty(&sched->ring_mirror_list))
266 cancel_delayed_work(&sched->work_tdr);
267 else
268 mod_delayed_work(system_wq, &sched->work_tdr, remaining);
269
270 spin_unlock_irqrestore(&sched->job_list_lock, flags);
271}
272EXPORT_SYMBOL(drm_sched_resume_timeout);
273
274static void drm_sched_job_begin(struct drm_sched_job *s_job)
275{
276 struct drm_gpu_scheduler *sched = s_job->sched;
277 unsigned long flags;
278
279 spin_lock_irqsave(&sched->job_list_lock, flags);
280 list_add_tail(&s_job->node, &sched->ring_mirror_list);
281 drm_sched_start_timeout(sched);
282 spin_unlock_irqrestore(&sched->job_list_lock, flags);
283}
284
285static void drm_sched_job_timedout(struct work_struct *work)
286{
287 struct drm_gpu_scheduler *sched;
288 struct drm_sched_job *job;
289 unsigned long flags;
290
291 sched = container_of(work, struct drm_gpu_scheduler, work_tdr.work);
292
293
294 spin_lock_irqsave(&sched->job_list_lock, flags);
295 job = list_first_entry_or_null(&sched->ring_mirror_list,
296 struct drm_sched_job, node);
297
298 if (job) {
299
300
301
302
303
304 list_del_init(&job->node);
305 spin_unlock_irqrestore(&sched->job_list_lock, flags);
306
307 job->sched->ops->timedout_job(job);
308
309
310
311
312
313 if (sched->free_guilty) {
314 job->sched->ops->free_job(job);
315 sched->free_guilty = false;
316 }
317 } else {
318 spin_unlock_irqrestore(&sched->job_list_lock, flags);
319 }
320
321 spin_lock_irqsave(&sched->job_list_lock, flags);
322 drm_sched_start_timeout(sched);
323 spin_unlock_irqrestore(&sched->job_list_lock, flags);
324}
325
326
327
328
329
330
331
332
333
334
335void drm_sched_increase_karma(struct drm_sched_job *bad)
336{
337 int i;
338 struct drm_sched_entity *tmp;
339 struct drm_sched_entity *entity;
340 struct drm_gpu_scheduler *sched = bad->sched;
341
342
343
344
345
346 if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
347 atomic_inc(&bad->karma);
348 for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL;
349 i++) {
350 struct drm_sched_rq *rq = &sched->sched_rq[i];
351
352 spin_lock(&rq->lock);
353 list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
354 if (bad->s_fence->scheduled.context ==
355 entity->fence_context) {
356 if (atomic_read(&bad->karma) >
357 bad->sched->hang_limit)
358 if (entity->guilty)
359 atomic_set(entity->guilty, 1);
360 break;
361 }
362 }
363 spin_unlock(&rq->lock);
364 if (&entity->list != &rq->entities)
365 break;
366 }
367 }
368}
369EXPORT_SYMBOL(drm_sched_increase_karma);
370
371
372
373
374
375
376
377
378
379
380
381
382
383void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
384{
385 struct drm_sched_job *s_job, *tmp;
386 unsigned long flags;
387
388 kthread_park(sched->thread);
389
390
391
392
393
394
395
396
397 if (bad && bad->sched == sched)
398
399
400
401
402 list_add(&bad->node, &sched->ring_mirror_list);
403
404
405
406
407
408
409
410 list_for_each_entry_safe_reverse(s_job, tmp, &sched->ring_mirror_list, node) {
411 if (s_job->s_fence->parent &&
412 dma_fence_remove_callback(s_job->s_fence->parent,
413 &s_job->cb)) {
414 atomic_dec(&sched->hw_rq_count);
415 } else {
416
417
418
419
420 spin_lock_irqsave(&sched->job_list_lock, flags);
421 list_del_init(&s_job->node);
422 spin_unlock_irqrestore(&sched->job_list_lock, flags);
423
424
425
426
427
428
429
430 dma_fence_wait(&s_job->s_fence->finished, false);
431
432
433
434
435
436
437 if (bad != s_job)
438 sched->ops->free_job(s_job);
439 else
440 sched->free_guilty = true;
441 }
442 }
443
444
445
446
447
448
449
450 cancel_delayed_work(&sched->work_tdr);
451}
452
453EXPORT_SYMBOL(drm_sched_stop);
454
455
456
457
458
459
460
461
462void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery)
463{
464 struct drm_sched_job *s_job, *tmp;
465 unsigned long flags;
466 int r;
467
468
469
470
471
472
473 list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
474 struct dma_fence *fence = s_job->s_fence->parent;
475
476 atomic_inc(&sched->hw_rq_count);
477
478 if (!full_recovery)
479 continue;
480
481 if (fence) {
482 r = dma_fence_add_callback(fence, &s_job->cb,
483 drm_sched_process_job);
484 if (r == -ENOENT)
485 drm_sched_process_job(fence, &s_job->cb);
486 else if (r)
487 DRM_ERROR("fence add callback failed (%d)\n",
488 r);
489 } else
490 drm_sched_process_job(NULL, &s_job->cb);
491 }
492
493 if (full_recovery) {
494 spin_lock_irqsave(&sched->job_list_lock, flags);
495 drm_sched_start_timeout(sched);
496 spin_unlock_irqrestore(&sched->job_list_lock, flags);
497 }
498
499 kthread_unpark(sched->thread);
500}
501EXPORT_SYMBOL(drm_sched_start);
502
503
504
505
506
507
508
509void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched)
510{
511 struct drm_sched_job *s_job, *tmp;
512 uint64_t guilty_context;
513 bool found_guilty = false;
514 struct dma_fence *fence;
515
516 list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
517 struct drm_sched_fence *s_fence = s_job->s_fence;
518
519 if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) {
520 found_guilty = true;
521 guilty_context = s_job->s_fence->scheduled.context;
522 }
523
524 if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
525 dma_fence_set_error(&s_fence->finished, -ECANCELED);
526
527 dma_fence_put(s_job->s_fence->parent);
528 fence = sched->ops->run_job(s_job);
529
530 if (IS_ERR_OR_NULL(fence)) {
531 if (IS_ERR(fence))
532 dma_fence_set_error(&s_fence->finished, PTR_ERR(fence));
533
534 s_job->s_fence->parent = NULL;
535 } else {
536 s_job->s_fence->parent = fence;
537 }
538
539
540 }
541}
542EXPORT_SYMBOL(drm_sched_resubmit_jobs);
543
544
545
546
547
548
549
550
551
552
553
554
555
556int drm_sched_job_init(struct drm_sched_job *job,
557 struct drm_sched_entity *entity,
558 void *owner)
559{
560 struct drm_gpu_scheduler *sched;
561
562 drm_sched_entity_select_rq(entity);
563 if (!entity->rq)
564 return -ENOENT;
565
566 sched = entity->rq->sched;
567
568 job->sched = sched;
569 job->entity = entity;
570 job->s_priority = entity->rq - sched->sched_rq;
571 job->s_fence = drm_sched_fence_create(entity, owner);
572 if (!job->s_fence)
573 return -ENOMEM;
574 job->id = atomic64_inc_return(&sched->job_id_count);
575
576 INIT_LIST_HEAD(&job->node);
577
578 return 0;
579}
580EXPORT_SYMBOL(drm_sched_job_init);
581
582
583
584
585
586
587void drm_sched_job_cleanup(struct drm_sched_job *job)
588{
589 dma_fence_put(&job->s_fence->finished);
590 job->s_fence = NULL;
591}
592EXPORT_SYMBOL(drm_sched_job_cleanup);
593
594
595
596
597
598
599
600
601static bool drm_sched_ready(struct drm_gpu_scheduler *sched)
602{
603 return atomic_read(&sched->hw_rq_count) <
604 sched->hw_submission_limit;
605}
606
607
608
609
610
611
612
613void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
614{
615 if (drm_sched_ready(sched))
616 wake_up_interruptible(&sched->wake_up_worker);
617}
618
619
620
621
622
623
624
625
626static struct drm_sched_entity *
627drm_sched_select_entity(struct drm_gpu_scheduler *sched)
628{
629 struct drm_sched_entity *entity;
630 int i;
631
632 if (!drm_sched_ready(sched))
633 return NULL;
634
635
636 for (i = DRM_SCHED_PRIORITY_MAX - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
637 entity = drm_sched_rq_select_entity(&sched->sched_rq[i]);
638 if (entity)
639 break;
640 }
641
642 return entity;
643}
644
645
646
647
648
649
650
651
652
653static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
654{
655 struct drm_sched_job *s_job = container_of(cb, struct drm_sched_job, cb);
656 struct drm_sched_fence *s_fence = s_job->s_fence;
657 struct drm_gpu_scheduler *sched = s_fence->sched;
658
659 atomic_dec(&sched->hw_rq_count);
660 atomic_dec(&sched->score);
661
662 trace_drm_sched_process_job(s_fence);
663
664 dma_fence_get(&s_fence->finished);
665 drm_sched_fence_finished(s_fence);
666 dma_fence_put(&s_fence->finished);
667 wake_up_interruptible(&sched->wake_up_worker);
668}
669
670
671
672
673
674
675
676
677
678static struct drm_sched_job *
679drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
680{
681 struct drm_sched_job *job;
682 unsigned long flags;
683
684
685
686
687
688 if ((sched->timeout != MAX_SCHEDULE_TIMEOUT &&
689 !cancel_delayed_work(&sched->work_tdr)) ||
690 __kthread_should_park(sched->thread))
691 return NULL;
692
693 spin_lock_irqsave(&sched->job_list_lock, flags);
694
695 job = list_first_entry_or_null(&sched->ring_mirror_list,
696 struct drm_sched_job, node);
697
698 if (job && dma_fence_is_signaled(&job->s_fence->finished)) {
699
700 list_del_init(&job->node);
701 } else {
702 job = NULL;
703
704 drm_sched_start_timeout(sched);
705 }
706
707 spin_unlock_irqrestore(&sched->job_list_lock, flags);
708
709 return job;
710}
711
712
713
714
715
716
717
718
719static bool drm_sched_blocked(struct drm_gpu_scheduler *sched)
720{
721 if (kthread_should_park()) {
722 kthread_parkme();
723 return true;
724 }
725
726 return false;
727}
728
729
730
731
732
733
734
735
736static int drm_sched_main(void *param)
737{
738 struct sched_param sparam = {.sched_priority = 1};
739 struct drm_gpu_scheduler *sched = (struct drm_gpu_scheduler *)param;
740 int r;
741
742 sched_setscheduler(current, SCHED_FIFO, &sparam);
743
744 while (!kthread_should_stop()) {
745 struct drm_sched_entity *entity = NULL;
746 struct drm_sched_fence *s_fence;
747 struct drm_sched_job *sched_job;
748 struct dma_fence *fence;
749 struct drm_sched_job *cleanup_job = NULL;
750
751 wait_event_interruptible(sched->wake_up_worker,
752 (cleanup_job = drm_sched_get_cleanup_job(sched)) ||
753 (!drm_sched_blocked(sched) &&
754 (entity = drm_sched_select_entity(sched))) ||
755 kthread_should_stop());
756
757 if (cleanup_job) {
758 sched->ops->free_job(cleanup_job);
759
760 drm_sched_start_timeout(sched);
761 }
762
763 if (!entity)
764 continue;
765
766 sched_job = drm_sched_entity_pop_job(entity);
767
768 complete(&entity->entity_idle);
769
770 if (!sched_job)
771 continue;
772
773 s_fence = sched_job->s_fence;
774
775 atomic_inc(&sched->hw_rq_count);
776 drm_sched_job_begin(sched_job);
777
778 fence = sched->ops->run_job(sched_job);
779 drm_sched_fence_scheduled(s_fence);
780
781 if (!IS_ERR_OR_NULL(fence)) {
782 s_fence->parent = dma_fence_get(fence);
783 r = dma_fence_add_callback(fence, &sched_job->cb,
784 drm_sched_process_job);
785 if (r == -ENOENT)
786 drm_sched_process_job(fence, &sched_job->cb);
787 else if (r)
788 DRM_ERROR("fence add callback failed (%d)\n",
789 r);
790 dma_fence_put(fence);
791 } else {
792 if (IS_ERR(fence))
793 dma_fence_set_error(&s_fence->finished, PTR_ERR(fence));
794
795 drm_sched_process_job(NULL, &sched_job->cb);
796 }
797
798 wake_up(&sched->job_scheduled);
799 }
800 return 0;
801}
802
803
804
805
806
807
808
809
810
811
812
813
814
815int drm_sched_init(struct drm_gpu_scheduler *sched,
816 const struct drm_sched_backend_ops *ops,
817 unsigned hw_submission,
818 unsigned hang_limit,
819 long timeout,
820 const char *name)
821{
822 int i, ret;
823 sched->ops = ops;
824 sched->hw_submission_limit = hw_submission;
825 sched->name = name;
826 sched->timeout = timeout;
827 sched->hang_limit = hang_limit;
828 for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_MAX; i++)
829 drm_sched_rq_init(sched, &sched->sched_rq[i]);
830
831 init_waitqueue_head(&sched->wake_up_worker);
832 init_waitqueue_head(&sched->job_scheduled);
833 INIT_LIST_HEAD(&sched->ring_mirror_list);
834 spin_lock_init(&sched->job_list_lock);
835 atomic_set(&sched->hw_rq_count, 0);
836 INIT_DELAYED_WORK(&sched->work_tdr, drm_sched_job_timedout);
837 atomic_set(&sched->score, 0);
838 atomic64_set(&sched->job_id_count, 0);
839
840
841 sched->thread = kthread_run(drm_sched_main, sched, sched->name);
842 if (IS_ERR(sched->thread)) {
843 ret = PTR_ERR(sched->thread);
844 sched->thread = NULL;
845 DRM_ERROR("Failed to create scheduler for %s.\n", name);
846 return ret;
847 }
848
849 sched->ready = true;
850 return 0;
851}
852EXPORT_SYMBOL(drm_sched_init);
853
854
855
856
857
858
859
860
861void drm_sched_fini(struct drm_gpu_scheduler *sched)
862{
863 if (sched->thread)
864 kthread_stop(sched->thread);
865
866 sched->ready = false;
867}
868EXPORT_SYMBOL(drm_sched_fini);
869