162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/kref.h>
662306a36Sopenharmony_ci#include <linux/uaccess.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "msm_gpu.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ciint msm_file_private_set_sysprof(struct msm_file_private *ctx,
1162306a36Sopenharmony_ci				 struct msm_gpu *gpu, int sysprof)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	/*
1462306a36Sopenharmony_ci	 * Since pm_runtime and sysprof_active are both refcounts, we
1562306a36Sopenharmony_ci	 * call apply the new value first, and then unwind the previous
1662306a36Sopenharmony_ci	 * value
1762306a36Sopenharmony_ci	 */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	switch (sysprof) {
2062306a36Sopenharmony_ci	default:
2162306a36Sopenharmony_ci		return -EINVAL;
2262306a36Sopenharmony_ci	case 2:
2362306a36Sopenharmony_ci		pm_runtime_get_sync(&gpu->pdev->dev);
2462306a36Sopenharmony_ci		fallthrough;
2562306a36Sopenharmony_ci	case 1:
2662306a36Sopenharmony_ci		refcount_inc(&gpu->sysprof_active);
2762306a36Sopenharmony_ci		fallthrough;
2862306a36Sopenharmony_ci	case 0:
2962306a36Sopenharmony_ci		break;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* unwind old value: */
3362306a36Sopenharmony_ci	switch (ctx->sysprof) {
3462306a36Sopenharmony_ci	case 2:
3562306a36Sopenharmony_ci		pm_runtime_put_autosuspend(&gpu->pdev->dev);
3662306a36Sopenharmony_ci		fallthrough;
3762306a36Sopenharmony_ci	case 1:
3862306a36Sopenharmony_ci		refcount_dec(&gpu->sysprof_active);
3962306a36Sopenharmony_ci		fallthrough;
4062306a36Sopenharmony_ci	case 0:
4162306a36Sopenharmony_ci		break;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	ctx->sysprof = sysprof;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid __msm_file_private_destroy(struct kref *kref)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct msm_file_private *ctx = container_of(kref,
5262306a36Sopenharmony_ci		struct msm_file_private, ref);
5362306a36Sopenharmony_ci	int i;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctx->entities); i++) {
5662306a36Sopenharmony_ci		if (!ctx->entities[i])
5762306a36Sopenharmony_ci			continue;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		drm_sched_entity_destroy(ctx->entities[i]);
6062306a36Sopenharmony_ci		kfree(ctx->entities[i]);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	msm_gem_address_space_put(ctx->aspace);
6462306a36Sopenharmony_ci	kfree(ctx->comm);
6562306a36Sopenharmony_ci	kfree(ctx->cmdline);
6662306a36Sopenharmony_ci	kfree(ctx);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid msm_submitqueue_destroy(struct kref *kref)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct msm_gpu_submitqueue *queue = container_of(kref,
7262306a36Sopenharmony_ci		struct msm_gpu_submitqueue, ref);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	idr_destroy(&queue->fence_idr);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	msm_file_private_put(queue->ctx);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	kfree(queue);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
8262306a36Sopenharmony_ci		u32 id)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct msm_gpu_submitqueue *entry;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (!ctx)
8762306a36Sopenharmony_ci		return NULL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	read_lock(&ctx->queuelock);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	list_for_each_entry(entry, &ctx->submitqueues, node) {
9262306a36Sopenharmony_ci		if (entry->id == id) {
9362306a36Sopenharmony_ci			kref_get(&entry->ref);
9462306a36Sopenharmony_ci			read_unlock(&ctx->queuelock);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci			return entry;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	read_unlock(&ctx->queuelock);
10162306a36Sopenharmony_ci	return NULL;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_civoid msm_submitqueue_close(struct msm_file_private *ctx)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct msm_gpu_submitqueue *entry, *tmp;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (!ctx)
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * No lock needed in close and there won't
11362306a36Sopenharmony_ci	 * be any more user ioctls coming our way
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) {
11662306a36Sopenharmony_ci		list_del(&entry->node);
11762306a36Sopenharmony_ci		msm_submitqueue_put(entry);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic struct drm_sched_entity *
12262306a36Sopenharmony_ciget_sched_entity(struct msm_file_private *ctx, struct msm_ringbuffer *ring,
12362306a36Sopenharmony_ci		 unsigned ring_nr, enum drm_sched_priority sched_prio)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	static DEFINE_MUTEX(entity_lock);
12662306a36Sopenharmony_ci	unsigned idx = (ring_nr * NR_SCHED_PRIORITIES) + sched_prio;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* We should have already validated that the requested priority is
12962306a36Sopenharmony_ci	 * valid by the time we get here.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	if (WARN_ON(idx >= ARRAY_SIZE(ctx->entities)))
13262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	mutex_lock(&entity_lock);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!ctx->entities[idx]) {
13762306a36Sopenharmony_ci		struct drm_sched_entity *entity;
13862306a36Sopenharmony_ci		struct drm_gpu_scheduler *sched = &ring->sched;
13962306a36Sopenharmony_ci		int ret;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		entity = kzalloc(sizeof(*ctx->entities[idx]), GFP_KERNEL);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		ret = drm_sched_entity_init(entity, sched_prio, &sched, 1, NULL);
14462306a36Sopenharmony_ci		if (ret) {
14562306a36Sopenharmony_ci			mutex_unlock(&entity_lock);
14662306a36Sopenharmony_ci			kfree(entity);
14762306a36Sopenharmony_ci			return ERR_PTR(ret);
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		ctx->entities[idx] = entity;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	mutex_unlock(&entity_lock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return ctx->entities[idx];
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciint msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
15962306a36Sopenharmony_ci		u32 prio, u32 flags, u32 *id)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct msm_drm_private *priv = drm->dev_private;
16262306a36Sopenharmony_ci	struct msm_gpu_submitqueue *queue;
16362306a36Sopenharmony_ci	enum drm_sched_priority sched_prio;
16462306a36Sopenharmony_ci	unsigned ring_nr;
16562306a36Sopenharmony_ci	int ret;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!ctx)
16862306a36Sopenharmony_ci		return -ENODEV;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!priv->gpu)
17162306a36Sopenharmony_ci		return -ENODEV;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = msm_gpu_convert_priority(priv->gpu, prio, &ring_nr, &sched_prio);
17462306a36Sopenharmony_ci	if (ret)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	queue = kzalloc(sizeof(*queue), GFP_KERNEL);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (!queue)
18062306a36Sopenharmony_ci		return -ENOMEM;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	kref_init(&queue->ref);
18362306a36Sopenharmony_ci	queue->flags = flags;
18462306a36Sopenharmony_ci	queue->ring_nr = ring_nr;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	queue->entity = get_sched_entity(ctx, priv->gpu->rb[ring_nr],
18762306a36Sopenharmony_ci					 ring_nr, sched_prio);
18862306a36Sopenharmony_ci	if (IS_ERR(queue->entity)) {
18962306a36Sopenharmony_ci		ret = PTR_ERR(queue->entity);
19062306a36Sopenharmony_ci		kfree(queue);
19162306a36Sopenharmony_ci		return ret;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	write_lock(&ctx->queuelock);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	queue->ctx = msm_file_private_get(ctx);
19762306a36Sopenharmony_ci	queue->id = ctx->queueid++;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (id)
20062306a36Sopenharmony_ci		*id = queue->id;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	idr_init(&queue->fence_idr);
20362306a36Sopenharmony_ci	spin_lock_init(&queue->idr_lock);
20462306a36Sopenharmony_ci	mutex_init(&queue->lock);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	list_add_tail(&queue->node, &ctx->submitqueues);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	write_unlock(&ctx->queuelock);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * Create the default submit-queue (id==0), used for backwards compatibility
21562306a36Sopenharmony_ci * for userspace that pre-dates the introduction of submitqueues.
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_ciint msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct msm_drm_private *priv = drm->dev_private;
22062306a36Sopenharmony_ci	int default_prio, max_priority;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!priv->gpu)
22362306a36Sopenharmony_ci		return -ENODEV;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	max_priority = (priv->gpu->nr_rings * NR_SCHED_PRIORITIES) - 1;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/*
22862306a36Sopenharmony_ci	 * Pick a medium priority level as default.  Lower numeric value is
22962306a36Sopenharmony_ci	 * higher priority, so round-up to pick a priority that is not higher
23062306a36Sopenharmony_ci	 * than the middle priority level.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	default_prio = DIV_ROUND_UP(max_priority, 2);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue,
23862306a36Sopenharmony_ci		struct drm_msm_submitqueue_query *args)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	size_t size = min_t(size_t, args->len, sizeof(queue->faults));
24162306a36Sopenharmony_ci	int ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* If a zero length was passed in, return the data size we expect */
24462306a36Sopenharmony_ci	if (!args->len) {
24562306a36Sopenharmony_ci		args->len = sizeof(queue->faults);
24662306a36Sopenharmony_ci		return 0;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Set the length to the actual size of the data */
25062306a36Sopenharmony_ci	args->len = size;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return ret ? -EFAULT : 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
25862306a36Sopenharmony_ci		struct drm_msm_submitqueue_query *args)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct msm_gpu_submitqueue *queue;
26162306a36Sopenharmony_ci	int ret = -EINVAL;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (args->pad)
26462306a36Sopenharmony_ci		return -EINVAL;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	queue = msm_submitqueue_get(ctx, args->id);
26762306a36Sopenharmony_ci	if (!queue)
26862306a36Sopenharmony_ci		return -ENOENT;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS)
27162306a36Sopenharmony_ci		ret = msm_submitqueue_query_faults(queue, args);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	msm_submitqueue_put(queue);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return ret;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciint msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct msm_gpu_submitqueue *entry;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!ctx)
28362306a36Sopenharmony_ci		return 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/*
28662306a36Sopenharmony_ci	 * id 0 is the "default" queue and can't be destroyed
28762306a36Sopenharmony_ci	 * by the user
28862306a36Sopenharmony_ci	 */
28962306a36Sopenharmony_ci	if (!id)
29062306a36Sopenharmony_ci		return -ENOENT;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	write_lock(&ctx->queuelock);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	list_for_each_entry(entry, &ctx->submitqueues, node) {
29562306a36Sopenharmony_ci		if (entry->id == id) {
29662306a36Sopenharmony_ci			list_del(&entry->node);
29762306a36Sopenharmony_ci			write_unlock(&ctx->queuelock);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			msm_submitqueue_put(entry);
30062306a36Sopenharmony_ci			return 0;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	write_unlock(&ctx->queuelock);
30562306a36Sopenharmony_ci	return -ENOENT;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
308