1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2017 The Linux Foundation. All rights reserved.
3  */
4 
5 #include <linux/kref.h>
6 #include <linux/uaccess.h>
7 
8 #include "msm_gpu.h"
9 
10 void msm_submitqueue_destroy(struct kref *kref)
11 {
12 	struct msm_gpu_submitqueue *queue = container_of(kref,
13 		struct msm_gpu_submitqueue, ref);
14 
15 	idr_destroy(&queue->fence_idr);
16 
17 	msm_file_private_put(queue->ctx);
18 
19 	kfree(queue);
20 }
21 
22 struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
23 		u32 id)
24 {
25 	struct msm_gpu_submitqueue *entry;
26 
27 	if (!ctx)
28 		return NULL;
29 
30 	read_lock(&ctx->queuelock);
31 
32 	list_for_each_entry(entry, &ctx->submitqueues, node) {
33 		if (entry->id == id) {
34 			kref_get(&entry->ref);
35 			read_unlock(&ctx->queuelock);
36 
37 			return entry;
38 		}
39 	}
40 
41 	read_unlock(&ctx->queuelock);
42 	return NULL;
43 }
44 
45 void msm_submitqueue_close(struct msm_file_private *ctx)
46 {
47 	struct msm_gpu_submitqueue *entry, *tmp;
48 
49 	if (!ctx)
50 		return;
51 
52 	/*
53 	 * No lock needed in close and there won't
54 	 * be any more user ioctls coming our way
55 	 */
56 	list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) {
57 		list_del(&entry->node);
58 		msm_submitqueue_put(entry);
59 	}
60 }
61 
62 int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
63 		u32 prio, u32 flags, u32 *id)
64 {
65 	struct msm_drm_private *priv = drm->dev_private;
66 	struct msm_gpu_submitqueue *queue;
67 
68 	if (!ctx)
69 		return -ENODEV;
70 
71 	if (!priv->gpu)
72 		return -ENODEV;
73 
74 	if (prio >= priv->gpu->nr_rings)
75 		return -EINVAL;
76 
77 	queue = kzalloc(sizeof(*queue), GFP_KERNEL);
78 
79 	if (!queue)
80 		return -ENOMEM;
81 
82 	kref_init(&queue->ref);
83 	queue->flags = flags;
84 	queue->prio = prio;
85 
86 	write_lock(&ctx->queuelock);
87 
88 	queue->ctx = msm_file_private_get(ctx);
89 	queue->id = ctx->queueid++;
90 
91 	if (id)
92 		*id = queue->id;
93 
94 	idr_init(&queue->fence_idr);
95 	mutex_init(&queue->lock);
96 
97 	list_add_tail(&queue->node, &ctx->submitqueues);
98 
99 	write_unlock(&ctx->queuelock);
100 
101 	return 0;
102 }
103 
104 /*
105  * Create the default submit-queue (id==0), used for backwards compatibility
106  * for userspace that pre-dates the introduction of submitqueues.
107  */
108 int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
109 {
110 	struct msm_drm_private *priv = drm->dev_private;
111 	int default_prio;
112 
113 	if (!priv->gpu)
114 		return -ENODEV;
115 
116 	/*
117 	 * Select priority 2 as the "default priority" unless nr_rings is less
118 	 * than 2 and then pick the lowest priority
119 	 */
120 	default_prio = clamp_t(uint32_t, 2, 0, priv->gpu->nr_rings - 1);
121 
122 	INIT_LIST_HEAD(&ctx->submitqueues);
123 
124 	rwlock_init(&ctx->queuelock);
125 
126 	return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
127 }
128 
129 static int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue,
130 		struct drm_msm_submitqueue_query *args)
131 {
132 	size_t size = min_t(size_t, args->len, sizeof(queue->faults));
133 	int ret;
134 
135 	/* If a zero length was passed in, return the data size we expect */
136 	if (!args->len) {
137 		args->len = sizeof(queue->faults);
138 		return 0;
139 	}
140 
141 	/* Set the length to the actual size of the data */
142 	args->len = size;
143 
144 	ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size);
145 
146 	return ret ? -EFAULT : 0;
147 }
148 
149 int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
150 		struct drm_msm_submitqueue_query *args)
151 {
152 	struct msm_gpu_submitqueue *queue;
153 	int ret = -EINVAL;
154 
155 	if (args->pad)
156 		return -EINVAL;
157 
158 	queue = msm_submitqueue_get(ctx, args->id);
159 	if (!queue)
160 		return -ENOENT;
161 
162 	if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS)
163 		ret = msm_submitqueue_query_faults(queue, args);
164 
165 	msm_submitqueue_put(queue);
166 
167 	return ret;
168 }
169 
170 int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
171 {
172 	struct msm_gpu_submitqueue *entry;
173 
174 	if (!ctx)
175 		return 0;
176 
177 	/*
178 	 * id 0 is the "default" queue and can't be destroyed
179 	 * by the user
180 	 */
181 	if (!id)
182 		return -ENOENT;
183 
184 	write_lock(&ctx->queuelock);
185 
186 	list_for_each_entry(entry, &ctx->submitqueues, node) {
187 		if (entry->id == id) {
188 			list_del(&entry->node);
189 			write_unlock(&ctx->queuelock);
190 
191 			msm_submitqueue_put(entry);
192 			return 0;
193 		}
194 	}
195 
196 	write_unlock(&ctx->queuelock);
197 	return -ENOENT;
198 }
199 
200