1 // SPDX-License-Identifier: MIT
3 #include <linux/slab.h>
4 #include <drm/gpu_scheduler.h>
5 #include <drm/drm_syncobj.h>
7 #include "nouveau_drv.h"
8 #include "nouveau_gem.h"
9 #include "nouveau_mem.h"
10 #include "nouveau_dma.h"
11 #include "nouveau_exec.h"
12 #include "nouveau_abi16.h"
13 #include "nouveau_sched.h"
17 * We want to make sure that jobs currently executing can't be deferred by
18 * other jobs competing for the hardware. Otherwise we might end up with job
19 * timeouts just because of too many clients submitting too many jobs. We don't
20 * want jobs to time out because of system load, but because of the job being
23 * For now allow for up to 16 concurrent jobs in flight until we know how many
24 * rings the hardware can process in parallel.
26 #define NOUVEAU_SCHED_HW_SUBMISSIONS 16
27 #define NOUVEAU_SCHED_JOB_TIMEOUT_MS 10000
30 nouveau_job_init(struct nouveau_job *job,
31 struct nouveau_job_args *args)
33 struct nouveau_sched_entity *entity = args->sched_entity;
36 job->file_priv = args->file_priv;
37 job->cli = nouveau_cli(args->file_priv);
40 job->sync = args->sync;
41 job->resv_usage = args->resv_usage;
45 job->in_sync.count = args->in_sync.count;
46 if (job->in_sync.count) {
50 job->in_sync.data = kmemdup(args->in_sync.s,
51 sizeof(*args->in_sync.s) *
54 if (!job->in_sync.data)
58 job->out_sync.count = args->out_sync.count;
59 if (job->out_sync.count) {
62 goto err_free_in_sync;
65 job->out_sync.data = kmemdup(args->out_sync.s,
66 sizeof(*args->out_sync.s) *
69 if (!job->out_sync.data) {
71 goto err_free_in_sync;
74 job->out_sync.objs = kcalloc(job->out_sync.count,
75 sizeof(*job->out_sync.objs),
77 if (!job->out_sync.objs) {
79 goto err_free_out_sync;
82 job->out_sync.chains = kcalloc(job->out_sync.count,
83 sizeof(*job->out_sync.chains),
85 if (!job->out_sync.chains) {
92 ret = drm_sched_job_init(&job->base, &entity->base, NULL);
96 job->state = NOUVEAU_JOB_INITIALIZED;
101 kfree(job->out_sync.chains);
103 kfree(job->out_sync.objs);
105 kfree(job->out_sync.data);
107 kfree(job->in_sync.data);
112 nouveau_job_free(struct nouveau_job *job)
114 kfree(job->in_sync.data);
115 kfree(job->out_sync.data);
116 kfree(job->out_sync.objs);
117 kfree(job->out_sync.chains);
120 void nouveau_job_fini(struct nouveau_job *job)
122 dma_fence_put(job->done_fence);
123 drm_sched_job_cleanup(&job->base);
128 sync_find_fence(struct nouveau_job *job,
129 struct drm_nouveau_sync *sync,
130 struct dma_fence **fence)
132 u32 stype = sync->flags & DRM_NOUVEAU_SYNC_TYPE_MASK;
136 if (stype != DRM_NOUVEAU_SYNC_SYNCOBJ &&
137 stype != DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ)
140 if (stype == DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ)
141 point = sync->timeline_value;
143 ret = drm_syncobj_find_fence(job->file_priv,
145 0 /* flags */, fence);
153 nouveau_job_add_deps(struct nouveau_job *job)
155 struct dma_fence *in_fence = NULL;
158 for (i = 0; i < job->in_sync.count; i++) {
159 struct drm_nouveau_sync *sync = &job->in_sync.data[i];
161 ret = sync_find_fence(job, sync, &in_fence);
163 NV_PRINTK(warn, job->cli,
164 "Failed to find syncobj (-> in): handle=%d\n",
169 ret = drm_sched_job_add_dependency(&job->base, in_fence);
178 nouveau_job_fence_attach_cleanup(struct nouveau_job *job)
182 for (i = 0; i < job->out_sync.count; i++) {
183 struct drm_syncobj *obj = job->out_sync.objs[i];
184 struct dma_fence_chain *chain = job->out_sync.chains[i];
187 drm_syncobj_put(obj);
190 dma_fence_chain_free(chain);
195 nouveau_job_fence_attach_prepare(struct nouveau_job *job)
199 for (i = 0; i < job->out_sync.count; i++) {
200 struct drm_nouveau_sync *sync = &job->out_sync.data[i];
201 struct drm_syncobj **pobj = &job->out_sync.objs[i];
202 struct dma_fence_chain **pchain = &job->out_sync.chains[i];
203 u32 stype = sync->flags & DRM_NOUVEAU_SYNC_TYPE_MASK;
205 if (stype != DRM_NOUVEAU_SYNC_SYNCOBJ &&
206 stype != DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ) {
208 goto err_sync_cleanup;
211 *pobj = drm_syncobj_find(job->file_priv, sync->handle);
213 NV_PRINTK(warn, job->cli,
214 "Failed to find syncobj (-> out): handle=%d\n",
217 goto err_sync_cleanup;
220 if (stype == DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ) {
221 *pchain = dma_fence_chain_alloc();
224 goto err_sync_cleanup;
232 nouveau_job_fence_attach_cleanup(job);
237 nouveau_job_fence_attach(struct nouveau_job *job)
239 struct dma_fence *fence = job->done_fence;
242 for (i = 0; i < job->out_sync.count; i++) {
243 struct drm_nouveau_sync *sync = &job->out_sync.data[i];
244 struct drm_syncobj **pobj = &job->out_sync.objs[i];
245 struct dma_fence_chain **pchain = &job->out_sync.chains[i];
246 u32 stype = sync->flags & DRM_NOUVEAU_SYNC_TYPE_MASK;
248 if (stype == DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ) {
249 drm_syncobj_add_point(*pobj, *pchain, fence,
250 sync->timeline_value);
252 drm_syncobj_replace_fence(*pobj, fence);
255 drm_syncobj_put(*pobj);
262 nouveau_job_submit(struct nouveau_job *job)
264 struct nouveau_sched_entity *entity = to_nouveau_sched_entity(job->base.entity);
265 struct dma_fence *done_fence = NULL;
268 ret = nouveau_job_add_deps(job);
272 ret = nouveau_job_fence_attach_prepare(job);
276 /* Make sure the job appears on the sched_entity's queue in the same
277 * order as it was submitted.
279 mutex_lock(&entity->mutex);
281 /* Guarantee we won't fail after the submit() callback returned
284 if (job->ops->submit) {
285 ret = job->ops->submit(job);
290 drm_sched_job_arm(&job->base);
291 job->done_fence = dma_fence_get(&job->base.s_fence->finished);
293 done_fence = dma_fence_get(job->done_fence);
295 /* If a sched job depends on a dma-fence from a job from the same GPU
296 * scheduler instance, but a different scheduler entity, the GPU
297 * scheduler does only wait for the particular job to be scheduled,
298 * rather than for the job to fully complete. This is due to the GPU
299 * scheduler assuming that there is a scheduler instance per ring.
300 * However, the current implementation, in order to avoid arbitrary
301 * amounts of kthreads, has a single scheduler instance while scheduler
302 * entities represent rings.
304 * As a workaround, set the DRM_SCHED_FENCE_DONT_PIPELINE for all
305 * out-fences in order to force the scheduler to wait for full job
306 * completion for dependent jobs from different entities and same
307 * scheduler instance.
309 * There is some work in progress [1] to address the issues of firmware
310 * schedulers; once it is in-tree the scheduler topology in Nouveau
311 * should be re-worked accordingly.
313 * [1] https://lore.kernel.org/dri-devel/20230801205103.627779-1-matthew.brost@intel.com/
315 set_bit(DRM_SCHED_FENCE_DONT_PIPELINE, &job->done_fence->flags);
317 if (job->ops->armed_submit)
318 job->ops->armed_submit(job);
320 nouveau_job_fence_attach(job);
322 /* Set job state before pushing the job to the scheduler,
323 * such that we do not overwrite the job state set in run().
325 job->state = NOUVEAU_JOB_SUBMIT_SUCCESS;
327 drm_sched_entity_push_job(&job->base);
329 mutex_unlock(&entity->mutex);
332 dma_fence_wait(done_fence, true);
333 dma_fence_put(done_fence);
339 mutex_unlock(&entity->mutex);
340 nouveau_job_fence_attach_cleanup(job);
342 job->state = NOUVEAU_JOB_SUBMIT_FAILED;
347 nouveau_sched_entity_qwork(struct nouveau_sched_entity *entity,
348 struct work_struct *work)
350 return queue_work(entity->sched_wq, work);
353 static struct dma_fence *
354 nouveau_job_run(struct nouveau_job *job)
356 struct dma_fence *fence;
358 fence = job->ops->run(job);
360 job->state = NOUVEAU_JOB_RUN_FAILED;
362 job->state = NOUVEAU_JOB_RUN_SUCCESS;
367 static struct dma_fence *
368 nouveau_sched_run_job(struct drm_sched_job *sched_job)
370 struct nouveau_job *job = to_nouveau_job(sched_job);
372 return nouveau_job_run(job);
375 static enum drm_gpu_sched_stat
376 nouveau_sched_timedout_job(struct drm_sched_job *sched_job)
378 struct nouveau_job *job = to_nouveau_job(sched_job);
380 NV_PRINTK(warn, job->cli, "Job timed out.\n");
382 if (job->ops->timeout)
383 return job->ops->timeout(job);
385 return DRM_GPU_SCHED_STAT_ENODEV;
389 nouveau_sched_free_job(struct drm_sched_job *sched_job)
391 struct nouveau_job *job = to_nouveau_job(sched_job);
393 nouveau_job_fini(job);
396 int nouveau_sched_entity_init(struct nouveau_sched_entity *entity,
397 struct drm_gpu_scheduler *sched,
398 struct workqueue_struct *sched_wq)
400 mutex_init(&entity->mutex);
401 spin_lock_init(&entity->job.list.lock);
402 INIT_LIST_HEAD(&entity->job.list.head);
403 init_waitqueue_head(&entity->job.wq);
405 entity->sched_wq = sched_wq;
406 return drm_sched_entity_init(&entity->base,
407 DRM_SCHED_PRIORITY_NORMAL,
412 nouveau_sched_entity_fini(struct nouveau_sched_entity *entity)
414 drm_sched_entity_destroy(&entity->base);
417 static const struct drm_sched_backend_ops nouveau_sched_ops = {
418 .run_job = nouveau_sched_run_job,
419 .timedout_job = nouveau_sched_timedout_job,
420 .free_job = nouveau_sched_free_job,
423 int nouveau_sched_init(struct nouveau_drm *drm)
425 struct drm_gpu_scheduler *sched = &drm->sched;
426 long job_hang_limit = msecs_to_jiffies(NOUVEAU_SCHED_JOB_TIMEOUT_MS);
428 drm->sched_wq = create_singlethread_workqueue("nouveau_sched_wq");
432 return drm_sched_init(sched, &nouveau_sched_ops,
433 NOUVEAU_SCHED_HW_SUBMISSIONS, 0, job_hang_limit,
434 NULL, NULL, "nouveau_sched", drm->dev->dev);
437 void nouveau_sched_fini(struct nouveau_drm *drm)
439 destroy_workqueue(drm->sched_wq);
440 drm_sched_fini(&drm->sched);