Commit 5f03a507 authored by Danilo Krummrich's avatar Danilo Krummrich
Browse files

drm/nouveau: implement 1:1 scheduler - entity relationship

Recent patches to the DRM scheduler [1][2] allow for a variable number
of run-queues and add support for (shared) workqueues rather than
dedicated kthreads per scheduler. This allows us to create a 1:1
relationship between a GPU scheduler and a scheduler entity, in order to
properly support firmware schedulers being able to handle an arbitrary
amount of dynamically allocated command ring buffers. This perfectly
matches Nouveau's needs, hence make use of it.

Topology wise we create one scheduler instance per client (handling
VM_BIND jobs) and one scheduler instance per channel (handling EXEC
jobs).

All channel scheduler instances share a workqueue, but every client
scheduler instance has a dedicated workqueue. The latter is required to
ensure that for VM_BIND job's free_job() work and run_job() work can
always run concurrently and hence, free_job() work can never stall
run_job() work. For EXEC jobs we don't have this requirement, since EXEC
job's free_job() does not require to take any locks which indirectly or
directly are held for allocations elsewhere.

[1] https://lore.kernel.org/all/8f53f7ef-7621-4f0b-bdef-d8d20bc497ff@redhat.com/T/
[2] https://lore.kernel.org/all/20231031032439.1558703-1-matthew.brost@intel.com/T/



Signed-off-by: default avatarDanilo Krummrich <dakr@redhat.com>
Reviewed-by: default avatarDave Airlie <airlied@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20231114002728.3491-1-dakr@redhat.com
parent 014f831a
Loading
Loading
Loading
Loading
+5 −13
Original line number Diff line number Diff line
@@ -127,21 +127,14 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
{
	struct nouveau_abi16_ntfy *ntfy, *temp;

	/* When a client exits without waiting for it's queued up jobs to
	 * finish it might happen that we fault the channel. This is due to
	 * drm_file_free() calling drm_gem_release() before the postclose()
	 * callback. Hence, we can't tear down this scheduler entity before
	 * uvmm mappings are unmapped. Currently, we can't detect this case.
	 *
	 * However, this should be rare and harmless, since the channel isn't
	 * needed anymore.
	 */
	nouveau_sched_entity_fini(&chan->sched_entity);
	/* Cancel all jobs from the entity's queue. */
	drm_sched_entity_fini(&chan->sched.entity);

	/* wait for all activity to stop before cleaning up */
	if (chan->chan)
		nouveau_channel_idle(chan->chan);

	nouveau_sched_fini(&chan->sched);

	/* cleanup notifier state */
	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
		nouveau_abi16_ntfy_fini(chan, ntfy);
@@ -344,8 +337,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
	if (ret)
		goto done;

	ret = nouveau_sched_entity_init(&chan->sched_entity, &drm->sched,
					drm->sched_wq);
	ret = nouveau_sched_init(&chan->sched, drm, drm->sched_wq);
	if (ret)
		goto done;

+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ struct nouveau_abi16_chan {
	struct nouveau_bo *ntfy;
	struct nouveau_vma *ntfy_vma;
	struct nvkm_mm  heap;
	struct nouveau_sched_entity sched_entity;
	struct nouveau_sched sched;
};

struct nouveau_abi16 {
+21 −10
Original line number Diff line number Diff line
@@ -201,9 +201,9 @@ nouveau_cli_fini(struct nouveau_cli *cli)
	WARN_ON(!list_empty(&cli->worker));

	usif_client_fini(cli);
	nouveau_sched_fini(&cli->sched);
	if (uvmm)
		nouveau_uvmm_fini(uvmm);
	nouveau_sched_entity_fini(&cli->sched_entity);
	nouveau_vmm_fini(&cli->svm);
	nouveau_vmm_fini(&cli->vmm);
	nvif_mmu_dtor(&cli->mmu);
@@ -310,8 +310,17 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname,

	cli->mem = &mems[ret];

	ret = nouveau_sched_entity_init(&cli->sched_entity, &drm->sched,
					drm->sched_wq);
	/* Don't pass in the (shared) sched_wq in order to let
	 * nouveau_sched_init() create a dedicated one for VM_BIND jobs.
	 *
	 * This is required to ensure that for VM_BIND jobs free_job() work and
	 * run_job() work can always run concurrently and hence, free_job() work
	 * can never stall run_job() work. For EXEC jobs we don't have this
	 * requirement, since EXEC job's free_job() does not require to take any
	 * locks which indirectly or directly are held for allocations
	 * elsewhere.
	 */
	ret = nouveau_sched_init(&cli->sched, drm, NULL);
	if (ret)
		goto done;

@@ -582,13 +591,16 @@ nouveau_drm_device_init(struct drm_device *dev)
	nvif_parent_ctor(&nouveau_parent, &drm->parent);
	drm->master.base.object.parent = &drm->parent;

	ret = nouveau_sched_init(drm);
	if (ret)
	drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", 0,
					WQ_MAX_ACTIVE);
	if (!drm->sched_wq) {
		ret = -ENOMEM;
		goto fail_alloc;
	}

	ret = nouveau_cli_init(drm, "DRM-master", &drm->master);
	if (ret)
		goto fail_sched;
		goto fail_wq;

	ret = nouveau_cli_init(drm, "DRM", &drm->client);
	if (ret)
@@ -658,8 +670,8 @@ nouveau_drm_device_init(struct drm_device *dev)
	nouveau_cli_fini(&drm->client);
fail_master:
	nouveau_cli_fini(&drm->master);
fail_sched:
	nouveau_sched_fini(drm);
fail_wq:
	destroy_workqueue(drm->sched_wq);
fail_alloc:
	nvif_parent_dtor(&drm->parent);
	kfree(drm);
@@ -711,10 +723,9 @@ nouveau_drm_device_fini(struct drm_device *dev)
	}
	mutex_unlock(&drm->clients_lock);

	nouveau_sched_fini(drm);

	nouveau_cli_fini(&drm->client);
	nouveau_cli_fini(&drm->master);
	destroy_workqueue(drm->sched_wq);
	nvif_parent_dtor(&drm->parent);
	mutex_destroy(&drm->clients_lock);
	kfree(drm);
+4 −5
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ struct nouveau_cli {
		bool disabled;
	} uvmm;

	struct nouveau_sched_entity sched_entity;
	struct nouveau_sched sched;

	const struct nvif_mclass *mem;

@@ -258,6 +258,9 @@ struct nouveau_drm {
		u64 context_base;
	} *runl;

	/* Workqueue used for channel schedulers. */
	struct workqueue_struct *sched_wq;

	/* context for accelerated drm-internal operations */
	struct nouveau_channel *cechan;
	struct nouveau_channel *channel;
@@ -298,10 +301,6 @@ struct nouveau_drm {
		struct mutex lock;
		bool component_registered;
	} audio;

	struct drm_gpu_scheduler sched;
	struct workqueue_struct *sched_wq;

};

static inline struct nouveau_drm *
+3 −4
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ nouveau_exec_job_free(struct nouveau_job *job)
{
	struct nouveau_exec_job *exec_job = to_nouveau_exec_job(job);

	nouveau_job_done(job);
	nouveau_job_free(job);

	kfree(exec_job->fence);
@@ -184,8 +185,6 @@ nouveau_exec_job_timeout(struct nouveau_job *job)
	NV_PRINTK(warn, job->cli, "job timeout, channel %d killed!\n",
		  chan->chid);

	nouveau_sched_entity_fini(job->entity);

	return DRM_GPU_SCHED_STAT_NOMINAL;
}

@@ -234,7 +233,7 @@ nouveau_exec_job_init(struct nouveau_exec_job **pjob,

	job->chan = __args->chan;

	args.sched_entity = __args->sched_entity;
	args.sched = __args->sched;
	args.file_priv = __args->file_priv;

	args.in_sync.count = __args->in_sync.count;
@@ -388,7 +387,7 @@ nouveau_exec_ioctl_exec(struct drm_device *dev,
	if (ret)
		goto out;

	args.sched_entity = &chan16->sched_entity;
	args.sched = &chan16->sched;
	args.file_priv = file_priv;
	args.chan = chan;

Loading