blob: 65284febb2f93749197d84c1f2ab7609603d6c79 [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
Rob Clarkf0a42bb2016-06-16 16:08:19 -040018#include <linux/sync_file.h>
19
Rob Clark7198e6b2013-07-19 12:59:32 -040020#include "msm_drv.h"
21#include "msm_gpu.h"
22#include "msm_gem.h"
23
24/*
25 * Cmdstream submission:
26 */
27
Rob Clark7198e6b2013-07-19 12:59:32 -040028/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
Rob Clark340faef2016-03-14 13:56:37 -040029#define BO_VALID 0x8000 /* is current addr in cmdstream correct/valid? */
Rob Clark7198e6b2013-07-19 12:59:32 -040030#define BO_LOCKED 0x4000
31#define BO_PINNED 0x2000
32
Rob Clark7198e6b2013-07-19 12:59:32 -040033static struct msm_gem_submit *submit_create(struct drm_device *dev,
Rob Clark6b597ce2016-06-01 14:17:40 -040034 struct msm_gpu *gpu, int nr_bos, int nr_cmds)
Rob Clark7198e6b2013-07-19 12:59:32 -040035{
36 struct msm_gem_submit *submit;
Rob Clark6b597ce2016-06-01 14:17:40 -040037 int sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) +
38 (nr_cmds * sizeof(*submit->cmd));
Rob Clark7198e6b2013-07-19 12:59:32 -040039
40 submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
Rob Clark6860b562016-05-03 09:49:40 -040041 if (!submit)
42 return NULL;
Rob Clark7198e6b2013-07-19 12:59:32 -040043
Rob Clark6860b562016-05-03 09:49:40 -040044 submit->dev = dev;
45 submit->gpu = gpu;
Rob Clarkba344af2016-05-24 18:43:26 -040046 submit->fence = NULL;
Rob Clark4816b622016-05-03 10:10:15 -040047 submit->pid = get_pid(task_pid(current));
Rob Clark6b597ce2016-06-01 14:17:40 -040048 submit->cmd = (void *)&submit->bos[nr_bos];
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
Rob Clarkba344af2016-05-24 18:43:26 -040054 INIT_LIST_HEAD(&submit->node);
Rob Clark6860b562016-05-03 09:49:40 -040055 INIT_LIST_HEAD(&submit->bo_list);
56 ww_acquire_init(&submit->ticket, &reservation_ww_class);
Rob Clark7198e6b2013-07-19 12:59:32 -040057
58 return submit;
59}
60
Rob Clark40e68152016-05-03 09:50:26 -040061void msm_gem_submit_free(struct msm_gem_submit *submit)
62{
63 fence_put(submit->fence);
64 list_del(&submit->node);
Rob Clark4816b622016-05-03 10:10:15 -040065 put_pid(submit->pid);
Rob Clark40e68152016-05-03 09:50:26 -040066 kfree(submit);
67}
68
Rob Clark7198e6b2013-07-19 12:59:32 -040069static int submit_lookup_objects(struct msm_gem_submit *submit,
70 struct drm_msm_gem_submit *args, struct drm_file *file)
71{
72 unsigned i;
73 int ret = 0;
74
75 spin_lock(&file->table_lock);
76
77 for (i = 0; i < args->nr_bos; i++) {
78 struct drm_msm_gem_submit_bo submit_bo;
79 struct drm_gem_object *obj;
80 struct msm_gem_object *msm_obj;
81 void __user *userptr =
Gustavo Padovan3ed605b2016-04-26 12:32:27 -030082 u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
Rob Clark7198e6b2013-07-19 12:59:32 -040083
Rob Clarka9e26ca2016-06-01 14:02:51 -040084 /* make sure we don't have garbage flags, in case we hit
85 * error path before flags is initialized:
86 */
87 submit->bos[i].flags = 0;
88
Rob Clark7198e6b2013-07-19 12:59:32 -040089 ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
90 if (ret) {
91 ret = -EFAULT;
92 goto out_unlock;
93 }
94
Rob Clark93ddb0d2014-03-03 09:42:33 -050095 if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) {
Rob Clark19872532013-09-06 15:36:40 -040096 DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
Rob Clark7198e6b2013-07-19 12:59:32 -040097 ret = -EINVAL;
98 goto out_unlock;
99 }
100
101 submit->bos[i].flags = submit_bo.flags;
102 /* in validate_objects() we figure out if this is true: */
103 submit->bos[i].iova = submit_bo.presumed;
104
105 /* normally use drm_gem_object_lookup(), but for bulk lookup
106 * all under single table_lock just hit object_idr directly:
107 */
108 obj = idr_find(&file->object_idr, submit_bo.handle);
109 if (!obj) {
Rob Clark19872532013-09-06 15:36:40 -0400110 DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
Rob Clark7198e6b2013-07-19 12:59:32 -0400111 ret = -EINVAL;
112 goto out_unlock;
113 }
114
115 msm_obj = to_msm_bo(obj);
116
117 if (!list_empty(&msm_obj->submit_entry)) {
Rob Clark19872532013-09-06 15:36:40 -0400118 DRM_ERROR("handle %u at index %u already on submit list\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400119 submit_bo.handle, i);
120 ret = -EINVAL;
121 goto out_unlock;
122 }
123
124 drm_gem_object_reference(obj);
125
126 submit->bos[i].obj = msm_obj;
127
128 list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
129 }
130
131out_unlock:
132 submit->nr_bos = i;
133 spin_unlock(&file->table_lock);
134
135 return ret;
136}
137
138static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
139{
140 struct msm_gem_object *msm_obj = submit->bos[i].obj;
141
142 if (submit->bos[i].flags & BO_PINNED)
143 msm_gem_put_iova(&msm_obj->base, submit->gpu->id);
144
145 if (submit->bos[i].flags & BO_LOCKED)
146 ww_mutex_unlock(&msm_obj->resv->lock);
147
148 if (!(submit->bos[i].flags & BO_VALID))
149 submit->bos[i].iova = 0;
150
151 submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
152}
153
154/* This is where we make sure all the bo's are reserved and pin'd: */
Rob Clark340faef2016-03-14 13:56:37 -0400155static int submit_lock_objects(struct msm_gem_submit *submit)
Rob Clark7198e6b2013-07-19 12:59:32 -0400156{
157 int contended, slow_locked = -1, i, ret = 0;
158
159retry:
Rob Clark7198e6b2013-07-19 12:59:32 -0400160 for (i = 0; i < submit->nr_bos; i++) {
161 struct msm_gem_object *msm_obj = submit->bos[i].obj;
Rob Clark7198e6b2013-07-19 12:59:32 -0400162
163 if (slow_locked == i)
164 slow_locked = -1;
165
166 contended = i;
167
168 if (!(submit->bos[i].flags & BO_LOCKED)) {
169 ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
170 &submit->ticket);
171 if (ret)
172 goto fail;
173 submit->bos[i].flags |= BO_LOCKED;
174 }
Rob Clark7198e6b2013-07-19 12:59:32 -0400175 }
176
177 ww_acquire_done(&submit->ticket);
178
179 return 0;
180
181fail:
182 for (; i >= 0; i--)
183 submit_unlock_unpin_bo(submit, i);
184
185 if (slow_locked > 0)
186 submit_unlock_unpin_bo(submit, slow_locked);
187
188 if (ret == -EDEADLK) {
189 struct msm_gem_object *msm_obj = submit->bos[contended].obj;
190 /* we lost out in a seqno race, lock and retry.. */
191 ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
192 &submit->ticket);
193 if (!ret) {
194 submit->bos[contended].flags |= BO_LOCKED;
195 slow_locked = contended;
196 goto retry;
197 }
198 }
199
200 return ret;
201}
202
Rob Clarkb6295f92016-03-15 18:26:28 -0400203static int submit_fence_sync(struct msm_gem_submit *submit)
204{
205 int i, ret = 0;
206
207 for (i = 0; i < submit->nr_bos; i++) {
208 struct msm_gem_object *msm_obj = submit->bos[i].obj;
209 bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
210
211 ret = msm_gem_sync_object(&msm_obj->base, submit->gpu->fctx, write);
212 if (ret)
213 break;
214 }
215
216 return ret;
217}
218
Rob Clark340faef2016-03-14 13:56:37 -0400219static int submit_pin_objects(struct msm_gem_submit *submit)
220{
221 int i, ret = 0;
222
223 submit->valid = true;
224
225 for (i = 0; i < submit->nr_bos; i++) {
226 struct msm_gem_object *msm_obj = submit->bos[i].obj;
227 uint32_t iova;
228
229 /* if locking succeeded, pin bo: */
230 ret = msm_gem_get_iova_locked(&msm_obj->base,
231 submit->gpu->id, &iova);
232
233 if (ret)
234 break;
235
236 submit->bos[i].flags |= BO_PINNED;
237
238 if (iova == submit->bos[i].iova) {
239 submit->bos[i].flags |= BO_VALID;
240 } else {
241 submit->bos[i].iova = iova;
242 /* iova changed, so address in cmdstream is not valid: */
243 submit->bos[i].flags &= ~BO_VALID;
244 submit->valid = false;
245 }
246 }
247
248 return ret;
249}
250
Rob Clark7198e6b2013-07-19 12:59:32 -0400251static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
252 struct msm_gem_object **obj, uint32_t *iova, bool *valid)
253{
254 if (idx >= submit->nr_bos) {
Rob Clark19872532013-09-06 15:36:40 -0400255 DRM_ERROR("invalid buffer index: %u (out of %u)\n",
256 idx, submit->nr_bos);
257 return -EINVAL;
Rob Clark7198e6b2013-07-19 12:59:32 -0400258 }
259
260 if (obj)
261 *obj = submit->bos[idx].obj;
262 if (iova)
263 *iova = submit->bos[idx].iova;
264 if (valid)
265 *valid = !!(submit->bos[idx].flags & BO_VALID);
266
267 return 0;
268}
269
270/* process the reloc's and patch up the cmdstream as needed: */
271static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
272 uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
273{
274 uint32_t i, last_offset = 0;
275 uint32_t *ptr;
276 int ret;
277
278 if (offset % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400279 DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
Rob Clark7198e6b2013-07-19 12:59:32 -0400280 return -EINVAL;
281 }
282
283 /* For now, just map the entire thing. Eventually we probably
284 * to do it page-by-page, w/ kmap() if not vmap()d..
285 */
Rob Clark18f23042016-05-26 16:24:35 -0400286 ptr = msm_gem_get_vaddr_locked(&obj->base);
Rob Clark7198e6b2013-07-19 12:59:32 -0400287
288 if (IS_ERR(ptr)) {
289 ret = PTR_ERR(ptr);
290 DBG("failed to map: %d", ret);
291 return ret;
292 }
293
294 for (i = 0; i < nr_relocs; i++) {
295 struct drm_msm_gem_submit_reloc submit_reloc;
296 void __user *userptr =
Gustavo Padovan3ed605b2016-04-26 12:32:27 -0300297 u64_to_user_ptr(relocs + (i * sizeof(submit_reloc)));
Rob Clark7198e6b2013-07-19 12:59:32 -0400298 uint32_t iova, off;
299 bool valid;
300
301 ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
302 if (ret)
303 return -EFAULT;
304
305 if (submit_reloc.submit_offset % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400306 DRM_ERROR("non-aligned reloc offset: %u\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400307 submit_reloc.submit_offset);
308 return -EINVAL;
309 }
310
311 /* offset in dwords: */
312 off = submit_reloc.submit_offset / 4;
313
314 if ((off >= (obj->base.size / 4)) ||
315 (off < last_offset)) {
Rob Clark19872532013-09-06 15:36:40 -0400316 DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
Rob Clark7198e6b2013-07-19 12:59:32 -0400317 return -EINVAL;
318 }
319
320 ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
321 if (ret)
322 return ret;
323
324 if (valid)
325 continue;
326
327 iova += submit_reloc.reloc_offset;
328
329 if (submit_reloc.shift < 0)
330 iova >>= -submit_reloc.shift;
331 else
332 iova <<= submit_reloc.shift;
333
334 ptr[off] = iova | submit_reloc.or;
335
336 last_offset = off;
337 }
338
Rob Clark18f23042016-05-26 16:24:35 -0400339 msm_gem_put_vaddr_locked(&obj->base);
340
Rob Clark7198e6b2013-07-19 12:59:32 -0400341 return 0;
342}
343
Rob Clark40e68152016-05-03 09:50:26 -0400344static void submit_cleanup(struct msm_gem_submit *submit)
Rob Clark7198e6b2013-07-19 12:59:32 -0400345{
346 unsigned i;
347
Rob Clark7198e6b2013-07-19 12:59:32 -0400348 for (i = 0; i < submit->nr_bos; i++) {
349 struct msm_gem_object *msm_obj = submit->bos[i].obj;
350 submit_unlock_unpin_bo(submit, i);
351 list_del_init(&msm_obj->submit_entry);
352 drm_gem_object_unreference(&msm_obj->base);
353 }
Rob Clark7198e6b2013-07-19 12:59:32 -0400354
355 ww_acquire_fini(&submit->ticket);
Rob Clark7198e6b2013-07-19 12:59:32 -0400356}
357
358int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
359 struct drm_file *file)
360{
361 struct msm_drm_private *priv = dev->dev_private;
362 struct drm_msm_gem_submit *args = data;
363 struct msm_file_private *ctx = file->driver_priv;
364 struct msm_gem_submit *submit;
Rob Clarkc01a9582016-02-03 13:12:31 -0500365 struct msm_gpu *gpu = priv->gpu;
Rob Clarkf0a42bb2016-06-16 16:08:19 -0400366 struct fence *in_fence = NULL;
Rob Clark7198e6b2013-07-19 12:59:32 -0400367 unsigned i;
368 int ret;
369
Rob Clarkc01a9582016-02-03 13:12:31 -0500370 if (!gpu)
371 return -ENXIO;
372
Rob Clark7198e6b2013-07-19 12:59:32 -0400373 /* for now, we just have 3d pipe.. eventually this would need to
374 * be more clever to dispatch to appropriate gpu module:
375 */
Rob Clarkd9c181e2016-04-23 10:08:59 -0400376 if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
377 return -EINVAL;
378
379 if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
Rob Clark7198e6b2013-07-19 12:59:32 -0400380 return -EINVAL;
381
Rob Clarkb5b4c262016-05-17 15:43:35 -0400382 ret = mutex_lock_interruptible(&dev->struct_mutex);
383 if (ret)
384 return ret;
Rob Clark687f0842016-02-03 13:24:35 -0500385
Rob Clark6b597ce2016-06-01 14:17:40 -0400386 submit = submit_create(dev, gpu, args->nr_bos, args->nr_cmds);
Rob Clarkb5b4c262016-05-17 15:43:35 -0400387 if (!submit) {
388 ret = -ENOMEM;
389 goto out_unlock;
390 }
Rob Clark7198e6b2013-07-19 12:59:32 -0400391
392 ret = submit_lookup_objects(submit, args, file);
393 if (ret)
394 goto out;
395
Rob Clark340faef2016-03-14 13:56:37 -0400396 ret = submit_lock_objects(submit);
397 if (ret)
398 goto out;
399
Rob Clarkf0a42bb2016-06-16 16:08:19 -0400400 if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
401 in_fence = sync_file_get_fence(args->fence_fd);
402
403 if (!in_fence) {
404 ret = -EINVAL;
405 goto out;
406 }
407
408 /* TODO if we get an array-fence due to userspace merging multiple
409 * fences, we need a way to determine if all the backing fences
410 * are from our own context..
411 */
412
413 if (in_fence->context != gpu->fctx->context) {
414 ret = fence_wait(in_fence, true);
415 if (ret)
416 goto out;
417 }
418
419 }
420
421 if (!(args->fence & MSM_SUBMIT_NO_IMPLICIT)) {
422 ret = submit_fence_sync(submit);
423 if (ret)
424 goto out;
425 }
Rob Clarkb6295f92016-03-15 18:26:28 -0400426
Rob Clark340faef2016-03-14 13:56:37 -0400427 ret = submit_pin_objects(submit);
Rob Clark7198e6b2013-07-19 12:59:32 -0400428 if (ret)
429 goto out;
430
431 for (i = 0; i < args->nr_cmds; i++) {
432 struct drm_msm_gem_submit_cmd submit_cmd;
433 void __user *userptr =
Gustavo Padovan3ed605b2016-04-26 12:32:27 -0300434 u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
Rob Clark7198e6b2013-07-19 12:59:32 -0400435 struct msm_gem_object *msm_obj;
436 uint32_t iova;
437
438 ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
439 if (ret) {
440 ret = -EFAULT;
441 goto out;
442 }
443
Rob Clark93ddb0d2014-03-03 09:42:33 -0500444 /* validate input from userspace: */
445 switch (submit_cmd.type) {
446 case MSM_SUBMIT_CMD_BUF:
447 case MSM_SUBMIT_CMD_IB_TARGET_BUF:
448 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
449 break;
450 default:
451 DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
452 ret = -EINVAL;
453 goto out;
454 }
455
Rob Clark7198e6b2013-07-19 12:59:32 -0400456 ret = submit_bo(submit, submit_cmd.submit_idx,
457 &msm_obj, &iova, NULL);
458 if (ret)
459 goto out;
460
461 if (submit_cmd.size % 4) {
Rob Clark19872532013-09-06 15:36:40 -0400462 DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
Rob Clark7198e6b2013-07-19 12:59:32 -0400463 submit_cmd.size);
464 ret = -EINVAL;
465 goto out;
466 }
467
Rob Clark19872532013-09-06 15:36:40 -0400468 if ((submit_cmd.size + submit_cmd.submit_offset) >=
469 msm_obj->base.size) {
470 DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
Rob Clark7198e6b2013-07-19 12:59:32 -0400471 ret = -EINVAL;
472 goto out;
473 }
474
475 submit->cmd[i].type = submit_cmd.type;
476 submit->cmd[i].size = submit_cmd.size / 4;
477 submit->cmd[i].iova = iova + submit_cmd.submit_offset;
Rob Clarka7d3c952014-05-30 14:47:38 -0400478 submit->cmd[i].idx = submit_cmd.submit_idx;
Rob Clark7198e6b2013-07-19 12:59:32 -0400479
480 if (submit->valid)
481 continue;
482
483 ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
484 submit_cmd.nr_relocs, submit_cmd.relocs);
485 if (ret)
486 goto out;
487 }
488
489 submit->nr_cmds = i;
490
Rob Clarkf44d32c2016-06-16 16:37:38 -0400491 submit->fence = msm_fence_alloc(gpu->fctx);
492 if (IS_ERR(submit->fence)) {
493 ret = PTR_ERR(submit->fence);
494 submit->fence = NULL;
495 goto out;
496 }
497
498 msm_gpu_submit(gpu, submit, ctx);
Rob Clark7198e6b2013-07-19 12:59:32 -0400499
Rob Clarkb6295f92016-03-15 18:26:28 -0400500 args->fence = submit->fence->seqno;
Rob Clark7198e6b2013-07-19 12:59:32 -0400501
502out:
Rob Clarkf0a42bb2016-06-16 16:08:19 -0400503 if (in_fence)
504 fence_put(in_fence);
Rob Clark40e68152016-05-03 09:50:26 -0400505 submit_cleanup(submit);
506 if (ret)
507 msm_gem_submit_free(submit);
Rob Clarkb5b4c262016-05-17 15:43:35 -0400508out_unlock:
Rob Clarkc2703b12014-02-06 19:19:20 -0500509 mutex_unlock(&dev->struct_mutex);
Rob Clark7198e6b2013-07-19 12:59:32 -0400510 return ret;
511}