]> Git Repo - linux.git/blob - drivers/gpu/drm/scheduler/sched_main.c
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
[linux.git] / drivers / gpu / drm / scheduler / sched_main.c
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 /**
25  * DOC: Overview
26  *
27  * The GPU scheduler provides entities which allow userspace to push jobs
28  * into software queues which are then scheduled on a hardware run queue.
29  * The software queues have a priority among them. The scheduler selects the entities
30  * from the run queue using a FIFO. The scheduler provides dependency handling
31  * features among jobs. The driver is supposed to provide callback functions for
32  * backend operations to the scheduler like submitting a job to hardware run queue,
33  * returning the dependencies of a job etc.
34  *
35  * The organisation of the scheduler is the following:
36  *
37  * 1. Each hw run queue has one scheduler
38  * 2. Each scheduler has multiple run queues with different priorities
39  *    (e.g., HIGH_HW,HIGH_SW, KERNEL, NORMAL)
40  * 3. Each scheduler run queue has a queue of entities to schedule
41  * 4. Entities themselves maintain a queue of jobs that will be scheduled on
42  *    the hardware.
43  *
44  * The jobs in a entity are always scheduled in the order that they were pushed.
45  */
46
47 #include <linux/kthread.h>
48 #include <linux/wait.h>
49 #include <linux/sched.h>
50 #include <uapi/linux/sched/types.h>
51 #include <drm/drmP.h>
52 #include <drm/gpu_scheduler.h>
53 #include <drm/spsc_queue.h>
54
55 #define CREATE_TRACE_POINTS
56 #include "gpu_scheduler_trace.h"
57
58 #define to_drm_sched_job(sched_job)             \
59                 container_of((sched_job), struct drm_sched_job, queue_node)
60
61 static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
62
63 /**
64  * drm_sched_rq_init - initialize a given run queue struct
65  *
66  * @rq: scheduler run queue
67  *
68  * Initializes a scheduler runqueue.
69  */
70 static void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
71                               struct drm_sched_rq *rq)
72 {
73         spin_lock_init(&rq->lock);
74         INIT_LIST_HEAD(&rq->entities);
75         rq->current_entity = NULL;
76         rq->sched = sched;
77 }
78
79 /**
80  * drm_sched_rq_add_entity - add an entity
81  *
82  * @rq: scheduler run queue
83  * @entity: scheduler entity
84  *
85  * Adds a scheduler entity to the run queue.
86  */
87 void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
88                              struct drm_sched_entity *entity)
89 {
90         if (!list_empty(&entity->list))
91                 return;
92         spin_lock(&rq->lock);
93         list_add_tail(&entity->list, &rq->entities);
94         spin_unlock(&rq->lock);
95 }
96
97 /**
98  * drm_sched_rq_remove_entity - remove an entity
99  *
100  * @rq: scheduler run queue
101  * @entity: scheduler entity
102  *
103  * Removes a scheduler entity from the run queue.
104  */
105 void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
106                                 struct drm_sched_entity *entity)
107 {
108         if (list_empty(&entity->list))
109                 return;
110         spin_lock(&rq->lock);
111         list_del_init(&entity->list);
112         if (rq->current_entity == entity)
113                 rq->current_entity = NULL;
114         spin_unlock(&rq->lock);
115 }
116
117 /**
118  * drm_sched_rq_select_entity - Select an entity which could provide a job to run
119  *
120  * @rq: scheduler run queue to check.
121  *
122  * Try to find a ready entity, returns NULL if none found.
123  */
124 static struct drm_sched_entity *
125 drm_sched_rq_select_entity(struct drm_sched_rq *rq)
126 {
127         struct drm_sched_entity *entity;
128
129         spin_lock(&rq->lock);
130
131         entity = rq->current_entity;
132         if (entity) {
133                 list_for_each_entry_continue(entity, &rq->entities, list) {
134                         if (drm_sched_entity_is_ready(entity)) {
135                                 rq->current_entity = entity;
136                                 spin_unlock(&rq->lock);
137                                 return entity;
138                         }
139                 }
140         }
141
142         list_for_each_entry(entity, &rq->entities, list) {
143
144                 if (drm_sched_entity_is_ready(entity)) {
145                         rq->current_entity = entity;
146                         spin_unlock(&rq->lock);
147                         return entity;
148                 }
149
150                 if (entity == rq->current_entity)
151                         break;
152         }
153
154         spin_unlock(&rq->lock);
155
156         return NULL;
157 }
158
159 /**
160  * drm_sched_dependency_optimized
161  *
162  * @fence: the dependency fence
163  * @entity: the entity which depends on the above fence
164  *
165  * Returns true if the dependency can be optimized and false otherwise
166  */
167 bool drm_sched_dependency_optimized(struct dma_fence* fence,
168                                     struct drm_sched_entity *entity)
169 {
170         struct drm_gpu_scheduler *sched = entity->rq->sched;
171         struct drm_sched_fence *s_fence;
172
173         if (!fence || dma_fence_is_signaled(fence))
174                 return false;
175         if (fence->context == entity->fence_context)
176                 return true;
177         s_fence = to_drm_sched_fence(fence);
178         if (s_fence && s_fence->sched == sched)
179                 return true;
180
181         return false;
182 }
183 EXPORT_SYMBOL(drm_sched_dependency_optimized);
184
185 /**
186  * drm_sched_start_timeout - start timeout for reset worker
187  *
188  * @sched: scheduler instance to start the worker for
189  *
190  * Start the timeout for the given scheduler.
191  */
192 static void drm_sched_start_timeout(struct drm_gpu_scheduler *sched)
193 {
194         if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
195             !list_empty(&sched->ring_mirror_list))
196                 schedule_delayed_work(&sched->work_tdr, sched->timeout);
197 }
198
199 /* job_finish is called after hw fence signaled
200  */
201 static void drm_sched_job_finish(struct work_struct *work)
202 {
203         struct drm_sched_job *s_job = container_of(work, struct drm_sched_job,
204                                                    finish_work);
205         struct drm_gpu_scheduler *sched = s_job->sched;
206
207         /*
208          * Canceling the timeout without removing our job from the ring mirror
209          * list is safe, as we will only end up in this worker if our jobs
210          * finished fence has been signaled. So even if some another worker
211          * manages to find this job as the next job in the list, the fence
212          * signaled check below will prevent the timeout to be restarted.
213          */
214         cancel_delayed_work_sync(&sched->work_tdr);
215
216         spin_lock(&sched->job_list_lock);
217         /* remove job from ring_mirror_list */
218         list_del(&s_job->node);
219         /* queue TDR for next job */
220         drm_sched_start_timeout(sched);
221         spin_unlock(&sched->job_list_lock);
222
223         dma_fence_put(&s_job->s_fence->finished);
224         sched->ops->free_job(s_job);
225 }
226
227 static void drm_sched_job_finish_cb(struct dma_fence *f,
228                                     struct dma_fence_cb *cb)
229 {
230         struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
231                                                  finish_cb);
232         schedule_work(&job->finish_work);
233 }
234
235 static void drm_sched_job_begin(struct drm_sched_job *s_job)
236 {
237         struct drm_gpu_scheduler *sched = s_job->sched;
238
239         dma_fence_add_callback(&s_job->s_fence->finished, &s_job->finish_cb,
240                                drm_sched_job_finish_cb);
241
242         spin_lock(&sched->job_list_lock);
243         list_add_tail(&s_job->node, &sched->ring_mirror_list);
244         drm_sched_start_timeout(sched);
245         spin_unlock(&sched->job_list_lock);
246 }
247
248 static void drm_sched_job_timedout(struct work_struct *work)
249 {
250         struct drm_gpu_scheduler *sched;
251         struct drm_sched_job *job;
252         int r;
253
254         sched = container_of(work, struct drm_gpu_scheduler, work_tdr.work);
255
256         spin_lock(&sched->job_list_lock);
257         list_for_each_entry_reverse(job, &sched->ring_mirror_list, node) {
258                 struct drm_sched_fence *fence = job->s_fence;
259
260                 if (!dma_fence_remove_callback(fence->parent, &fence->cb))
261                         goto already_signaled;
262         }
263
264         job = list_first_entry_or_null(&sched->ring_mirror_list,
265                                        struct drm_sched_job, node);
266         spin_unlock(&sched->job_list_lock);
267
268         if (job)
269                 sched->ops->timedout_job(job);
270
271         spin_lock(&sched->job_list_lock);
272         list_for_each_entry(job, &sched->ring_mirror_list, node) {
273                 struct drm_sched_fence *fence = job->s_fence;
274
275                 if (!fence->parent || !list_empty(&fence->cb.node))
276                         continue;
277
278                 r = dma_fence_add_callback(fence->parent, &fence->cb,
279                                            drm_sched_process_job);
280                 if (r)
281                         drm_sched_process_job(fence->parent, &fence->cb);
282
283 already_signaled:
284                 ;
285         }
286         spin_unlock(&sched->job_list_lock);
287 }
288
289 /**
290  * drm_sched_hw_job_reset - stop the scheduler if it contains the bad job
291  *
292  * @sched: scheduler instance
293  * @bad: bad scheduler job
294  *
295  */
296 void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
297 {
298         struct drm_sched_job *s_job;
299         struct drm_sched_entity *entity, *tmp;
300         int i;
301
302         spin_lock(&sched->job_list_lock);
303         list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) {
304                 if (s_job->s_fence->parent &&
305                     dma_fence_remove_callback(s_job->s_fence->parent,
306                                               &s_job->s_fence->cb)) {
307                         dma_fence_put(s_job->s_fence->parent);
308                         s_job->s_fence->parent = NULL;
309                         atomic_dec(&sched->hw_rq_count);
310                 }
311         }
312         spin_unlock(&sched->job_list_lock);
313
314         if (bad && bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
315                 atomic_inc(&bad->karma);
316                 /* don't increase @bad's karma if it's from KERNEL RQ,
317                  * becuase sometimes GPU hang would cause kernel jobs (like VM updating jobs)
318                  * corrupt but keep in mind that kernel jobs always considered good.
319                  */
320                 for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; i++ ) {
321                         struct drm_sched_rq *rq = &sched->sched_rq[i];
322
323                         spin_lock(&rq->lock);
324                         list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
325                                 if (bad->s_fence->scheduled.context == entity->fence_context) {
326                                     if (atomic_read(&bad->karma) > bad->sched->hang_limit)
327                                                 if (entity->guilty)
328                                                         atomic_set(entity->guilty, 1);
329                                         break;
330                                 }
331                         }
332                         spin_unlock(&rq->lock);
333                         if (&entity->list != &rq->entities)
334                                 break;
335                 }
336         }
337 }
338 EXPORT_SYMBOL(drm_sched_hw_job_reset);
339
340 /**
341  * drm_sched_job_recovery - recover jobs after a reset
342  *
343  * @sched: scheduler instance
344  *
345  */
346 void drm_sched_job_recovery(struct drm_gpu_scheduler *sched)
347 {
348         struct drm_sched_job *s_job, *tmp;
349         bool found_guilty = false;
350         int r;
351
352         spin_lock(&sched->job_list_lock);
353         list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
354                 struct drm_sched_fence *s_fence = s_job->s_fence;
355                 struct dma_fence *fence;
356                 uint64_t guilty_context;
357
358                 if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) {
359                         found_guilty = true;
360                         guilty_context = s_job->s_fence->scheduled.context;
361                 }
362
363                 if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
364                         dma_fence_set_error(&s_fence->finished, -ECANCELED);
365
366                 spin_unlock(&sched->job_list_lock);
367                 fence = sched->ops->run_job(s_job);
368                 atomic_inc(&sched->hw_rq_count);
369
370                 if (fence) {
371                         s_fence->parent = dma_fence_get(fence);
372                         r = dma_fence_add_callback(fence, &s_fence->cb,
373                                                    drm_sched_process_job);
374                         if (r == -ENOENT)
375                                 drm_sched_process_job(fence, &s_fence->cb);
376                         else if (r)
377                                 DRM_ERROR("fence add callback failed (%d)\n",
378                                           r);
379                         dma_fence_put(fence);
380                 } else {
381                         drm_sched_process_job(NULL, &s_fence->cb);
382                 }
383                 spin_lock(&sched->job_list_lock);
384         }
385         drm_sched_start_timeout(sched);
386         spin_unlock(&sched->job_list_lock);
387 }
388 EXPORT_SYMBOL(drm_sched_job_recovery);
389
390 /**
391  * drm_sched_job_init - init a scheduler job
392  *
393  * @job: scheduler job to init
394  * @entity: scheduler entity to use
395  * @owner: job owner for debugging
396  *
397  * Refer to drm_sched_entity_push_job() documentation
398  * for locking considerations.
399  *
400  * Returns 0 for success, negative error code otherwise.
401  */
402 int drm_sched_job_init(struct drm_sched_job *job,
403                        struct drm_sched_entity *entity,
404                        void *owner)
405 {
406         struct drm_gpu_scheduler *sched;
407
408         drm_sched_entity_select_rq(entity);
409         sched = entity->rq->sched;
410
411         job->sched = sched;
412         job->entity = entity;
413         job->s_priority = entity->rq - sched->sched_rq;
414         job->s_fence = drm_sched_fence_create(entity, owner);
415         if (!job->s_fence)
416                 return -ENOMEM;
417         job->id = atomic64_inc_return(&sched->job_id_count);
418
419         INIT_WORK(&job->finish_work, drm_sched_job_finish);
420         INIT_LIST_HEAD(&job->node);
421
422         return 0;
423 }
424 EXPORT_SYMBOL(drm_sched_job_init);
425
426 /**
427  * drm_sched_ready - is the scheduler ready
428  *
429  * @sched: scheduler instance
430  *
431  * Return true if we can push more jobs to the hw, otherwise false.
432  */
433 static bool drm_sched_ready(struct drm_gpu_scheduler *sched)
434 {
435         return atomic_read(&sched->hw_rq_count) <
436                 sched->hw_submission_limit;
437 }
438
439 /**
440  * drm_sched_wakeup - Wake up the scheduler when it is ready
441  *
442  * @sched: scheduler instance
443  *
444  */
445 void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
446 {
447         if (drm_sched_ready(sched))
448                 wake_up_interruptible(&sched->wake_up_worker);
449 }
450
451 /**
452  * drm_sched_select_entity - Select next entity to process
453  *
454  * @sched: scheduler instance
455  *
456  * Returns the entity to process or NULL if none are found.
457  */
458 static struct drm_sched_entity *
459 drm_sched_select_entity(struct drm_gpu_scheduler *sched)
460 {
461         struct drm_sched_entity *entity;
462         int i;
463
464         if (!drm_sched_ready(sched))
465                 return NULL;
466
467         /* Kernel run queue has higher priority than normal run queue*/
468         for (i = DRM_SCHED_PRIORITY_MAX - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
469                 entity = drm_sched_rq_select_entity(&sched->sched_rq[i]);
470                 if (entity)
471                         break;
472         }
473
474         return entity;
475 }
476
477 /**
478  * drm_sched_process_job - process a job
479  *
480  * @f: fence
481  * @cb: fence callbacks
482  *
483  * Called after job has finished execution.
484  */
485 static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
486 {
487         struct drm_sched_fence *s_fence =
488                 container_of(cb, struct drm_sched_fence, cb);
489         struct drm_gpu_scheduler *sched = s_fence->sched;
490
491         dma_fence_get(&s_fence->finished);
492         atomic_dec(&sched->hw_rq_count);
493         atomic_dec(&sched->num_jobs);
494         drm_sched_fence_finished(s_fence);
495
496         trace_drm_sched_process_job(s_fence);
497         dma_fence_put(&s_fence->finished);
498         wake_up_interruptible(&sched->wake_up_worker);
499 }
500
501 /**
502  * drm_sched_blocked - check if the scheduler is blocked
503  *
504  * @sched: scheduler instance
505  *
506  * Returns true if blocked, otherwise false.
507  */
508 static bool drm_sched_blocked(struct drm_gpu_scheduler *sched)
509 {
510         if (kthread_should_park()) {
511                 kthread_parkme();
512                 return true;
513         }
514
515         return false;
516 }
517
518 /**
519  * drm_sched_main - main scheduler thread
520  *
521  * @param: scheduler instance
522  *
523  * Returns 0.
524  */
525 static int drm_sched_main(void *param)
526 {
527         struct sched_param sparam = {.sched_priority = 1};
528         struct drm_gpu_scheduler *sched = (struct drm_gpu_scheduler *)param;
529         int r;
530
531         sched_setscheduler(current, SCHED_FIFO, &sparam);
532
533         while (!kthread_should_stop()) {
534                 struct drm_sched_entity *entity = NULL;
535                 struct drm_sched_fence *s_fence;
536                 struct drm_sched_job *sched_job;
537                 struct dma_fence *fence;
538
539                 wait_event_interruptible(sched->wake_up_worker,
540                                          (!drm_sched_blocked(sched) &&
541                                           (entity = drm_sched_select_entity(sched))) ||
542                                          kthread_should_stop());
543
544                 if (!entity)
545                         continue;
546
547                 sched_job = drm_sched_entity_pop_job(entity);
548                 if (!sched_job)
549                         continue;
550
551                 s_fence = sched_job->s_fence;
552
553                 atomic_inc(&sched->hw_rq_count);
554                 drm_sched_job_begin(sched_job);
555
556                 fence = sched->ops->run_job(sched_job);
557                 drm_sched_fence_scheduled(s_fence);
558
559                 if (fence) {
560                         s_fence->parent = dma_fence_get(fence);
561                         r = dma_fence_add_callback(fence, &s_fence->cb,
562                                                    drm_sched_process_job);
563                         if (r == -ENOENT)
564                                 drm_sched_process_job(fence, &s_fence->cb);
565                         else if (r)
566                                 DRM_ERROR("fence add callback failed (%d)\n",
567                                           r);
568                         dma_fence_put(fence);
569                 } else {
570                         drm_sched_process_job(NULL, &s_fence->cb);
571                 }
572
573                 wake_up(&sched->job_scheduled);
574         }
575         return 0;
576 }
577
578 /**
579  * drm_sched_init - Init a gpu scheduler instance
580  *
581  * @sched: scheduler instance
582  * @ops: backend operations for this scheduler
583  * @hw_submission: number of hw submissions that can be in flight
584  * @hang_limit: number of times to allow a job to hang before dropping it
585  * @timeout: timeout value in jiffies for the scheduler
586  * @name: name used for debugging
587  *
588  * Return 0 on success, otherwise error code.
589  */
590 int drm_sched_init(struct drm_gpu_scheduler *sched,
591                    const struct drm_sched_backend_ops *ops,
592                    unsigned hw_submission,
593                    unsigned hang_limit,
594                    long timeout,
595                    const char *name)
596 {
597         int i;
598         sched->ops = ops;
599         sched->hw_submission_limit = hw_submission;
600         sched->name = name;
601         sched->timeout = timeout;
602         sched->hang_limit = hang_limit;
603         for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_MAX; i++)
604                 drm_sched_rq_init(sched, &sched->sched_rq[i]);
605
606         init_waitqueue_head(&sched->wake_up_worker);
607         init_waitqueue_head(&sched->job_scheduled);
608         INIT_LIST_HEAD(&sched->ring_mirror_list);
609         spin_lock_init(&sched->job_list_lock);
610         atomic_set(&sched->hw_rq_count, 0);
611         INIT_DELAYED_WORK(&sched->work_tdr, drm_sched_job_timedout);
612         atomic_set(&sched->num_jobs, 0);
613         atomic64_set(&sched->job_id_count, 0);
614
615         /* Each scheduler will run on a seperate kernel thread */
616         sched->thread = kthread_run(drm_sched_main, sched, sched->name);
617         if (IS_ERR(sched->thread)) {
618                 DRM_ERROR("Failed to create scheduler for %s.\n", name);
619                 return PTR_ERR(sched->thread);
620         }
621
622         return 0;
623 }
624 EXPORT_SYMBOL(drm_sched_init);
625
626 /**
627  * drm_sched_fini - Destroy a gpu scheduler
628  *
629  * @sched: scheduler instance
630  *
631  * Tears down and cleans up the scheduler.
632  */
633 void drm_sched_fini(struct drm_gpu_scheduler *sched)
634 {
635         if (sched->thread)
636                 kthread_stop(sched->thread);
637 }
638 EXPORT_SYMBOL(drm_sched_fini);
This page took 0.089052 seconds and 4 git commands to generate.