blob: a9a001150b9edae73b4a588873003997fdc447f0 [file] [log] [blame]
Rob Clark7198e6b2013-07-19 12:59:32 -04001/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19#include "msm_gpu.h"
20#include "msm_gem.h"
21
22/*
23 * Cmdstream submission:
24 */
25
Rob Clark7198e6b2013-07-19 12:59:32 -040026/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
Rob Clark340faef2016-03-14 13:56:37 -040027#define BO_VALID 0x8000 /* is current addr in cmdstream correct/valid? */
Rob Clark7198e6b2013-07-19 12:59:32 -040028#define BO_LOCKED 0x4000
29#define BO_PINNED 0x2000
30
31static inline void __user *to_user_ptr(u64 address)
32{
33 return (void __user *)(uintptr_t)address;
34}
35
36static struct msm_gem_submit *submit_create(struct drm_device *dev,
37 struct msm_gpu *gpu, int nr)
38{
39 struct msm_gem_submit *submit;
40 int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0]));
41
42 submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
Rob Clark6860b562016-05-03 09:49:40 -040043 if (!submit)
44 return NULL;
Rob Clark7198e6b2013-07-19 12:59:32 -040045
Rob Clark6860b562016-05-03 09:49:40 -040046 submit->dev = dev;
47 submit->gpu = gpu;
Rob Clark4816b622016-05-03 10:10:15 -040048 submit->pid = get_pid(task_pid(current));
Rob Clark7198e6b2013-07-19 12:59:32 -040049
Rob Clark6860b562016-05-03 09:49:40 -040050 /* initially, until copy_from_user() and bo lookup succeeds: */
51 submit->nr_bos = 0;
52 submit->nr_cmds = 0;
53
54 INIT_LIST_HEAD(&submit->bo_list);
55 ww_acquire_init(&submit->ticket, &reservation_ww_class);
Rob Clark7198e6b2013-07-19 12:59:32 -040056
57 return submit;
58}
59
Rob Clark40e68152016-05-03 09:50:26 -040060void msm_gem_submit_free(struct msm_gem_submit *submit)
61{
62 fence_put(submit->fence);
63 list_del(&submit->node);
Rob Clark4816b622016-05-03 10:10:15 -040064 put_pid(submit->pid);
Rob Clark40e68152016-05-03 09:50:26 -040065 kfree(submit);
66}
67
Rob Clark7198e6b2013-07-19 12:59:32 -040068static int submit_lookup_objects(struct msm_gem_submit *submit,
69 struct drm_msm_gem_submit *args, struct drm_file *file)
70{
71 unsigned i;
72 int ret = 0;
73
74 spin_lock(&file->table_lock);
75
76 for (i = 0; i < args->nr_bos; i++) {
77 struct drm_msm_gem_submit_bo submit_bo;
78 struct drm_gem_object *obj;
79 struct msm_gem_object *msm_obj;
80 void __user *userptr =
81 to_user_ptr(args->bos + (i * sizeof(submit_bo)));
82
83 ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
84 if (ret) {
85 ret = -EFAULT;
86 goto out_unlock;
87 }
88
Rob Clark93ddb0d2014-03-03 09:42:33 -050089 if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) {
Rob Clark19872532013-09-06 15:36:40 -040090 DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
Rob Clark7198e6b2013-07-19 12:59:32 -040091 ret = -EINVAL;
92 goto out_unlock;
93 }
94
95 submit->bos[i].flags = submit_bo.flags;
96 /* in validate_objects() we figure out if this is true: */
97 submit->bos[i].iova = submit_bo.presumed;
98
99 /* normally use drm_gem_object_lookup(), but for bulk lookup
100 * all under single table_lock just hit object_idr directly:
101 */
102 obj = idr_find(&file->object_idr, submit_bo.handle);
103 if (!obj) {
Rob Clark19872532013-09-06 15:36:40 -0400104 DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
Rob Clark7198e6b2013-07-19 12:59:32 -0400105 ret = -EINVAL;
106 goto out_unlock;
107 }
108
109 msm_obj = to_msm_bo(obj);
110
111 if (!list_empty(&msm_obj->submit_entry)) {
Rob Clark19872532013-09-06 15:36:40 -0400112 DRM_ERROR("handle %u at index %u already on submit list\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400113 submit_bo.handle, i);
114 ret = -EINVAL;
115 goto out_unlock;
116 }
117
118 drm_gem_object_reference(obj);
119
120 submit->bos[i].obj = msm_obj;
121
122 list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
123 }
124
125out_unlock:
126 submit->nr_bos = i;
127 spin_unlock(&file->table_lock);
128
129 return ret;
130}
131
132static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
133{
134 struct msm_gem_object *msm_obj = submit->bos[i].obj;
135
136 if (submit->bos[i].flags & BO_PINNED)
137 msm_gem_put_iova(&msm_obj->base, submit->gpu->id);
138
139 if (submit->bos[i].flags & BO_LOCKED)
140 ww_mutex_unlock(&msm_obj->resv->lock);
141
142 if (!(submit->bos[i].flags & BO_VALID))
143 submit->bos[i].iova = 0;
144
145 submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
146}
147
148/* This is where we make sure all the bo's are reserved and pin'd: */
Rob Clark340faef2016-03-14 13:56:37 -0400149static int submit_lock_objects(struct msm_gem_submit *submit)
Rob Clark7198e6b2013-07-19 12:59:32 -0400150{
151 int contended, slow_locked = -1, i, ret = 0;
152
153retry:
Rob Clark7198e6b2013-07-19 12:59:32 -0400154 for (i = 0; i < submit->nr_bos; i++) {
155 struct msm_gem_object *msm_obj = submit->bos[i].obj;
Rob Clark7198e6b2013-07-19 12:59:32 -0400156
157 if (slow_locked == i)
158 slow_locked = -1;
159
160 contended = i;
161
162 if (!(submit->bos[i].flags & BO_LOCKED)) {
163 ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
164 &submit->ticket);
165 if (ret)
166 goto fail;
167 submit->bos[i].flags |= BO_LOCKED;
168 }
Rob Clark7198e6b2013-07-19 12:59:32 -0400169 }
170
171 ww_acquire_done(&submit->ticket);
172
173 return 0;
174
175fail:
176 for (; i >= 0; i--)
177 submit_unlock_unpin_bo(submit, i);
178
179 if (slow_locked > 0)
180 submit_unlock_unpin_bo(submit, slow_locked);
181
182 if (ret == -EDEADLK) {
183 struct msm_gem_object *msm_obj = submit->bos[contended].obj;
184 /* we lost out in a seqno race, lock and retry.. */
185 ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
186 &submit->ticket);
187 if (!ret) {
188 submit->bos[contended].flags |= BO_LOCKED;
189 slow_locked = contended;
190 goto retry;
191 }
192 }
193
194 return ret;
195}
196
Rob Clarkb6295f92016-03-15 18:26:28 -0400197static int submit_fence_sync(struct msm_gem_submit *submit)
198{
199 int i, ret = 0;
200
201 for (i = 0; i < submit->nr_bos; i++) {
202 struct msm_gem_object *msm_obj = submit->bos[i].obj;
203 bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
204
205 ret = msm_gem_sync_object(&msm_obj->base, submit->gpu->fctx, write);
206 if (ret)
207 break;
208 }
209
210 return ret;
211}
212
Rob Clark340faef2016-03-14 13:56:37 -0400213static int submit_pin_objects(struct msm_gem_submit *submit)
214{
215 int i, ret = 0;
216
217 submit->valid = true;
218
219 for (i = 0; i < submit->nr_bos; i++) {
220 struct msm_gem_object *msm_obj = submit->bos[i].obj;
221 uint32_t iova;
222
223 /* if locking succeeded, pin bo: */
224 ret = msm_gem_get_iova_locked(&msm_obj->base,
225 submit->gpu->id, &iova);
226
227 if (ret)
228 break;
229
230 submit->bos[i].flags |= BO_PINNED;
231
232 if (iova == submit->bos[i].iova) {
233 submit->bos[i].flags |= BO_VALID;
234 } else {
235 submit->bos[i].iova = iova;
236 /* iova changed, so address in cmdstream is not valid: */
237 submit->bos[i].flags &= ~BO_VALID;
238 submit->valid = false;
239 }
240 }
241
242 return ret;
243}
244
Rob Clark7198e6b2013-07-19 12:59:32 -0400245static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
246 struct msm_gem_object **obj, uint32_t *iova, bool *valid)
247{
248 if (idx >= submit->nr_bos) {
Rob Clark19872532013-09-06 15:36:40 -0400249 DRM_ERROR("invalid buffer index: %u (out of %u)\n",
250 idx, submit->nr_bos);
251 return -EINVAL;
Rob Clark7198e6b2013-07-19 12:59:32 -0400252 }
253
254 if (obj)
255 *obj = submit->bos[idx].obj;
256 if (iova)
257 *iova = submit->bos[idx].iova;
258 if (valid)
259 *valid = !!(submit->bos[idx].flags & BO_VALID);
260
261 return 0;
262}
263
264/* process the reloc's and patch up the cmdstream as needed: */
265static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
266 uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
267{
268 uint32_t i, last_offset = 0;
269 uint32_t *ptr;
270 int ret;
271
272 if (offset % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400273 DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
Rob Clark7198e6b2013-07-19 12:59:32 -0400274 return -EINVAL;
275 }
276
277 /* For now, just map the entire thing. Eventually we probably
278 * to do it page-by-page, w/ kmap() if not vmap()d..
279 */
Rob Clarkc2703b12014-02-06 19:19:20 -0500280 ptr = msm_gem_vaddr_locked(&obj->base);
Rob Clark7198e6b2013-07-19 12:59:32 -0400281
282 if (IS_ERR(ptr)) {
283 ret = PTR_ERR(ptr);
284 DBG("failed to map: %d", ret);
285 return ret;
286 }
287
288 for (i = 0; i < nr_relocs; i++) {
289 struct drm_msm_gem_submit_reloc submit_reloc;
290 void __user *userptr =
291 to_user_ptr(relocs + (i * sizeof(submit_reloc)));
292 uint32_t iova, off;
293 bool valid;
294
295 ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
296 if (ret)
297 return -EFAULT;
298
299 if (submit_reloc.submit_offset % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400300 DRM_ERROR("non-aligned reloc offset: %u\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400301 submit_reloc.submit_offset);
302 return -EINVAL;
303 }
304
305 /* offset in dwords: */
306 off = submit_reloc.submit_offset / 4;
307
308 if ((off >= (obj->base.size / 4)) ||
309 (off < last_offset)) {
Rob Clark19872532013-09-06 15:36:40 -0400310 DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
Rob Clark7198e6b2013-07-19 12:59:32 -0400311 return -EINVAL;
312 }
313
314 ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
315 if (ret)
316 return ret;
317
318 if (valid)
319 continue;
320
321 iova += submit_reloc.reloc_offset;
322
323 if (submit_reloc.shift < 0)
324 iova >>= -submit_reloc.shift;
325 else
326 iova <<= submit_reloc.shift;
327
328 ptr[off] = iova | submit_reloc.or;
329
330 last_offset = off;
331 }
332
333 return 0;
334}
335
Rob Clark40e68152016-05-03 09:50:26 -0400336static void submit_cleanup(struct msm_gem_submit *submit)
Rob Clark7198e6b2013-07-19 12:59:32 -0400337{
338 unsigned i;
339
Rob Clark7198e6b2013-07-19 12:59:32 -0400340 for (i = 0; i < submit->nr_bos; i++) {
341 struct msm_gem_object *msm_obj = submit->bos[i].obj;
342 submit_unlock_unpin_bo(submit, i);
343 list_del_init(&msm_obj->submit_entry);
344 drm_gem_object_unreference(&msm_obj->base);
345 }
Rob Clark7198e6b2013-07-19 12:59:32 -0400346
347 ww_acquire_fini(&submit->ticket);
Rob Clark7198e6b2013-07-19 12:59:32 -0400348}
349
350int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
351 struct drm_file *file)
352{
353 struct msm_drm_private *priv = dev->dev_private;
354 struct drm_msm_gem_submit *args = data;
355 struct msm_file_private *ctx = file->driver_priv;
356 struct msm_gem_submit *submit;
Rob Clarkc01a9582016-02-03 13:12:31 -0500357 struct msm_gpu *gpu = priv->gpu;
Rob Clark7198e6b2013-07-19 12:59:32 -0400358 unsigned i;
359 int ret;
360
Rob Clarkc01a9582016-02-03 13:12:31 -0500361 if (!gpu)
362 return -ENXIO;
363
Rob Clark7198e6b2013-07-19 12:59:32 -0400364 /* for now, we just have 3d pipe.. eventually this would need to
365 * be more clever to dispatch to appropriate gpu module:
366 */
367 if (args->pipe != MSM_PIPE_3D0)
368 return -EINVAL;
369
Rob Clark7198e6b2013-07-19 12:59:32 -0400370 if (args->nr_cmds > MAX_CMDS)
371 return -EINVAL;
372
373 submit = submit_create(dev, gpu, args->nr_bos);
Rob Clark687f0842016-02-03 13:24:35 -0500374 if (!submit)
375 return -ENOMEM;
376
377 mutex_lock(&dev->struct_mutex);
Rob Clark7198e6b2013-07-19 12:59:32 -0400378
379 ret = submit_lookup_objects(submit, args, file);
380 if (ret)
381 goto out;
382
Rob Clark340faef2016-03-14 13:56:37 -0400383 ret = submit_lock_objects(submit);
384 if (ret)
385 goto out;
386
Rob Clarkb6295f92016-03-15 18:26:28 -0400387 ret = submit_fence_sync(submit);
388 if (ret)
389 goto out;
390
Rob Clark340faef2016-03-14 13:56:37 -0400391 ret = submit_pin_objects(submit);
Rob Clark7198e6b2013-07-19 12:59:32 -0400392 if (ret)
393 goto out;
394
395 for (i = 0; i < args->nr_cmds; i++) {
396 struct drm_msm_gem_submit_cmd submit_cmd;
397 void __user *userptr =
398 to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
399 struct msm_gem_object *msm_obj;
400 uint32_t iova;
401
402 ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
403 if (ret) {
404 ret = -EFAULT;
405 goto out;
406 }
407
Rob Clark93ddb0d2014-03-03 09:42:33 -0500408 /* validate input from userspace: */
409 switch (submit_cmd.type) {
410 case MSM_SUBMIT_CMD_BUF:
411 case MSM_SUBMIT_CMD_IB_TARGET_BUF:
412 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
413 break;
414 default:
415 DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
416 ret = -EINVAL;
417 goto out;
418 }
419
Rob Clark7198e6b2013-07-19 12:59:32 -0400420 ret = submit_bo(submit, submit_cmd.submit_idx,
421 &msm_obj, &iova, NULL);
422 if (ret)
423 goto out;
424
425 if (submit_cmd.size % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400426 DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400427 submit_cmd.size);
428 ret = -EINVAL;
429 goto out;
430 }
431
Rob Clark19872532013-09-06 15:36:40 -0400432 if ((submit_cmd.size + submit_cmd.submit_offset) >=
433 msm_obj->base.size) {
434 DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
Rob Clark7198e6b2013-07-19 12:59:32 -0400435 ret = -EINVAL;
436 goto out;
437 }
438
439 submit->cmd[i].type = submit_cmd.type;
440 submit->cmd[i].size = submit_cmd.size / 4;
441 submit->cmd[i].iova = iova + submit_cmd.submit_offset;
Rob Clarka7d3c952014-05-30 14:47:38 -0400442 submit->cmd[i].idx = submit_cmd.submit_idx;
Rob Clark7198e6b2013-07-19 12:59:32 -0400443
444 if (submit->valid)
445 continue;
446
447 ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
448 submit_cmd.nr_relocs, submit_cmd.relocs);
449 if (ret)
450 goto out;
451 }
452
453 submit->nr_cmds = i;
454
455 ret = msm_gpu_submit(gpu, submit, ctx);
456
Rob Clarkb6295f92016-03-15 18:26:28 -0400457 args->fence = submit->fence->seqno;
Rob Clark7198e6b2013-07-19 12:59:32 -0400458
459out:
Rob Clark40e68152016-05-03 09:50:26 -0400460 submit_cleanup(submit);
461 if (ret)
462 msm_gem_submit_free(submit);
Rob Clarkc2703b12014-02-06 19:19:20 -0500463 mutex_unlock(&dev->struct_mutex);
Rob Clark7198e6b2013-07-19 12:59:32 -0400464 return ret;
465}