xref: /linux/drivers/gpu/drm/msm/msm_gem_submit.c (revision 7b1166dee847d5018c1f3cc781218e806078f752)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6 
7 #include <linux/file.h>
8 #include <linux/sync_file.h>
9 #include <linux/uaccess.h>
10 
11 #include <drm/drm_drv.h>
12 #include <drm/drm_file.h>
13 #include <drm/drm_syncobj.h>
14 
15 #include "msm_drv.h"
16 #include "msm_gpu.h"
17 #include "msm_gem.h"
18 #include "msm_gpu_trace.h"
19 
20 /* For userspace errors, use DRM_UT_DRIVER.. so that userspace can enable
21  * error msgs for debugging, but we don't spam dmesg by default
22  */
23 #define SUBMIT_ERROR(err, submit, fmt, ...) \
24 	UERR(err, (submit)->dev, fmt, ##__VA_ARGS__)
25 
26 /*
27  * Cmdstream submission:
28  */
29 
30 static struct msm_gem_submit *submit_create(struct drm_device *dev,
31 		struct msm_gpu *gpu,
32 		struct msm_gpu_submitqueue *queue, uint32_t nr_bos,
33 		uint32_t nr_cmds, u64 drm_client_id)
34 {
35 	static atomic_t ident = ATOMIC_INIT(0);
36 	struct msm_gem_submit *submit;
37 	uint64_t sz;
38 	int ret;
39 
40 	sz = struct_size(submit, bos, nr_bos) +
41 			((u64)nr_cmds * sizeof(submit->cmd[0]));
42 
43 	if (sz > SIZE_MAX)
44 		return ERR_PTR(-ENOMEM);
45 
46 	submit = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN);
47 	if (!submit)
48 		return ERR_PTR(-ENOMEM);
49 
50 	submit->hw_fence = msm_fence_alloc();
51 	if (IS_ERR(submit->hw_fence)) {
52 		ret = PTR_ERR(submit->hw_fence);
53 		kfree(submit);
54 		return ERR_PTR(ret);
55 	}
56 
57 	ret = drm_sched_job_init(&submit->base, queue->entity, 1, queue,
58 				 drm_client_id);
59 	if (ret) {
60 		kfree(submit->hw_fence);
61 		kfree(submit);
62 		return ERR_PTR(ret);
63 	}
64 
65 	kref_init(&submit->ref);
66 	submit->dev = dev;
67 	submit->aspace = queue->ctx->aspace;
68 	submit->gpu = gpu;
69 	submit->cmd = (void *)&submit->bos[nr_bos];
70 	submit->queue = queue;
71 	submit->pid = get_pid(task_pid(current));
72 	submit->ring = gpu->rb[queue->ring_nr];
73 	submit->fault_dumped = false;
74 
75 	/* Get a unique identifier for the submission for logging purposes */
76 	submit->ident = atomic_inc_return(&ident) - 1;
77 
78 	INIT_LIST_HEAD(&submit->node);
79 
80 	return submit;
81 }
82 
83 void __msm_gem_submit_destroy(struct kref *kref)
84 {
85 	struct msm_gem_submit *submit =
86 			container_of(kref, struct msm_gem_submit, ref);
87 	unsigned i;
88 
89 	if (submit->fence_id) {
90 		spin_lock(&submit->queue->idr_lock);
91 		idr_remove(&submit->queue->fence_idr, submit->fence_id);
92 		spin_unlock(&submit->queue->idr_lock);
93 	}
94 
95 	dma_fence_put(submit->user_fence);
96 
97 	/*
98 	 * If the submit is freed before msm_job_run(), then hw_fence is
99 	 * just some pre-allocated memory, not a reference counted fence.
100 	 * Once the job runs and the hw_fence is initialized, it will
101 	 * have a refcount of at least one, since the submit holds a ref
102 	 * to the hw_fence.
103 	 */
104 	if (kref_read(&submit->hw_fence->refcount) == 0) {
105 		kfree(submit->hw_fence);
106 	} else {
107 		dma_fence_put(submit->hw_fence);
108 	}
109 
110 	put_pid(submit->pid);
111 	msm_submitqueue_put(submit->queue);
112 
113 	for (i = 0; i < submit->nr_cmds; i++)
114 		kfree(submit->cmd[i].relocs);
115 
116 	kfree(submit);
117 }
118 
119 static int submit_lookup_objects(struct msm_gem_submit *submit,
120 		struct drm_msm_gem_submit *args, struct drm_file *file)
121 {
122 	unsigned i;
123 	int ret = 0;
124 
125 	for (i = 0; i < args->nr_bos; i++) {
126 		struct drm_msm_gem_submit_bo submit_bo;
127 		void __user *userptr =
128 			u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
129 
130 		/* make sure we don't have garbage flags, in case we hit
131 		 * error path before flags is initialized:
132 		 */
133 		submit->bos[i].flags = 0;
134 
135 		if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
136 			ret = -EFAULT;
137 			i = 0;
138 			goto out;
139 		}
140 
141 /* at least one of READ and/or WRITE flags should be set: */
142 #define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
143 
144 		if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
145 			!(submit_bo.flags & MANDATORY_FLAGS)) {
146 			ret = SUBMIT_ERROR(EINVAL, submit, "invalid flags: %x\n", submit_bo.flags);
147 			i = 0;
148 			goto out;
149 		}
150 
151 		submit->bos[i].handle = submit_bo.handle;
152 		submit->bos[i].flags = submit_bo.flags;
153 	}
154 
155 	spin_lock(&file->table_lock);
156 
157 	for (i = 0; i < args->nr_bos; i++) {
158 		struct drm_gem_object *obj;
159 
160 		/* normally use drm_gem_object_lookup(), but for bulk lookup
161 		 * all under single table_lock just hit object_idr directly:
162 		 */
163 		obj = idr_find(&file->object_idr, submit->bos[i].handle);
164 		if (!obj) {
165 			ret = SUBMIT_ERROR(EINVAL, submit, "invalid handle %u at index %u\n", submit->bos[i].handle, i);
166 			goto out_unlock;
167 		}
168 
169 		drm_gem_object_get(obj);
170 
171 		submit->bos[i].obj = obj;
172 	}
173 
174 out_unlock:
175 	spin_unlock(&file->table_lock);
176 
177 out:
178 	submit->nr_bos = i;
179 
180 	return ret;
181 }
182 
183 static int submit_lookup_cmds(struct msm_gem_submit *submit,
184 		struct drm_msm_gem_submit *args, struct drm_file *file)
185 {
186 	unsigned i;
187 	size_t sz;
188 	int ret = 0;
189 
190 	for (i = 0; i < args->nr_cmds; i++) {
191 		struct drm_msm_gem_submit_cmd submit_cmd;
192 		void __user *userptr =
193 			u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
194 
195 		ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
196 		if (ret) {
197 			ret = -EFAULT;
198 			goto out;
199 		}
200 
201 		/* validate input from userspace: */
202 		switch (submit_cmd.type) {
203 		case MSM_SUBMIT_CMD_BUF:
204 		case MSM_SUBMIT_CMD_IB_TARGET_BUF:
205 		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
206 			break;
207 		default:
208 			return SUBMIT_ERROR(EINVAL, submit, "invalid type: %08x\n", submit_cmd.type);
209 		}
210 
211 		if (submit_cmd.size % 4) {
212 			ret = SUBMIT_ERROR(EINVAL, submit, "non-aligned cmdstream buffer size: %u\n",
213 					   submit_cmd.size);
214 			goto out;
215 		}
216 
217 		submit->cmd[i].type = submit_cmd.type;
218 		submit->cmd[i].size = submit_cmd.size / 4;
219 		submit->cmd[i].offset = submit_cmd.submit_offset / 4;
220 		submit->cmd[i].idx  = submit_cmd.submit_idx;
221 		submit->cmd[i].nr_relocs = submit_cmd.nr_relocs;
222 
223 		userptr = u64_to_user_ptr(submit_cmd.relocs);
224 
225 		sz = array_size(submit_cmd.nr_relocs,
226 				sizeof(struct drm_msm_gem_submit_reloc));
227 		/* check for overflow: */
228 		if (sz == SIZE_MAX) {
229 			ret = -ENOMEM;
230 			goto out;
231 		}
232 		submit->cmd[i].relocs = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN);
233 		if (!submit->cmd[i].relocs) {
234 			ret = -ENOMEM;
235 			goto out;
236 		}
237 		ret = copy_from_user(submit->cmd[i].relocs, userptr, sz);
238 		if (ret) {
239 			ret = -EFAULT;
240 			goto out;
241 		}
242 	}
243 
244 out:
245 	return ret;
246 }
247 
248 /* This is where we make sure all the bo's are reserved and pin'd: */
249 static int submit_lock_objects(struct msm_gem_submit *submit)
250 {
251 	int ret;
252 
253 	drm_exec_init(&submit->exec, DRM_EXEC_INTERRUPTIBLE_WAIT, submit->nr_bos);
254 
255 	drm_exec_until_all_locked (&submit->exec) {
256 		for (unsigned i = 0; i < submit->nr_bos; i++) {
257 			struct drm_gem_object *obj = submit->bos[i].obj;
258 			ret = drm_exec_prepare_obj(&submit->exec, obj, 1);
259 			drm_exec_retry_on_contention(&submit->exec);
260 			if (ret)
261 				goto error;
262 		}
263 	}
264 
265 	return 0;
266 
267 error:
268 	return ret;
269 }
270 
271 static int submit_fence_sync(struct msm_gem_submit *submit)
272 {
273 	int i, ret = 0;
274 
275 	for (i = 0; i < submit->nr_bos; i++) {
276 		struct drm_gem_object *obj = submit->bos[i].obj;
277 		bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
278 
279 		/* Otherwise userspace can ask for implicit sync to be
280 		 * disabled on specific buffers.  This is useful for internal
281 		 * usermode driver managed buffers, suballocation, etc.
282 		 */
283 		if (submit->bos[i].flags & MSM_SUBMIT_BO_NO_IMPLICIT)
284 			continue;
285 
286 		ret = drm_sched_job_add_implicit_dependencies(&submit->base,
287 							      obj,
288 							      write);
289 		if (ret)
290 			break;
291 	}
292 
293 	return ret;
294 }
295 
296 static int submit_pin_objects(struct msm_gem_submit *submit)
297 {
298 	struct msm_drm_private *priv = submit->dev->dev_private;
299 	int i, ret = 0;
300 
301 	for (i = 0; i < submit->nr_bos; i++) {
302 		struct drm_gem_object *obj = submit->bos[i].obj;
303 		struct msm_gem_vma *vma;
304 
305 		/* if locking succeeded, pin bo: */
306 		vma = msm_gem_get_vma_locked(obj, submit->aspace);
307 		if (IS_ERR(vma)) {
308 			ret = PTR_ERR(vma);
309 			break;
310 		}
311 
312 		ret = msm_gem_pin_vma_locked(obj, vma);
313 		if (ret)
314 			break;
315 
316 		submit->bos[i].iova = vma->iova;
317 	}
318 
319 	/*
320 	 * A second loop while holding the LRU lock (a) avoids acquiring/dropping
321 	 * the LRU lock for each individual bo, while (b) avoiding holding the
322 	 * LRU lock while calling msm_gem_pin_vma_locked() (which could trigger
323 	 * get_pages() which could trigger reclaim.. and if we held the LRU lock
324 	 * could trigger deadlock with the shrinker).
325 	 */
326 	mutex_lock(&priv->lru.lock);
327 	for (i = 0; i < submit->nr_bos; i++) {
328 		msm_gem_pin_obj_locked(submit->bos[i].obj);
329 	}
330 	mutex_unlock(&priv->lru.lock);
331 
332 	submit->bos_pinned = true;
333 
334 	return ret;
335 }
336 
337 static void submit_unpin_objects(struct msm_gem_submit *submit)
338 {
339 	if (!submit->bos_pinned)
340 		return;
341 
342 	for (int i = 0; i < submit->nr_bos; i++) {
343 		struct drm_gem_object *obj = submit->bos[i].obj;
344 
345 		msm_gem_unpin_locked(obj);
346 	}
347 
348 	submit->bos_pinned = false;
349 }
350 
351 static void submit_attach_object_fences(struct msm_gem_submit *submit)
352 {
353 	int i;
354 
355 	for (i = 0; i < submit->nr_bos; i++) {
356 		struct drm_gem_object *obj = submit->bos[i].obj;
357 
358 		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
359 			dma_resv_add_fence(obj->resv, submit->user_fence,
360 					   DMA_RESV_USAGE_WRITE);
361 		else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
362 			dma_resv_add_fence(obj->resv, submit->user_fence,
363 					   DMA_RESV_USAGE_READ);
364 	}
365 }
366 
367 static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
368 		struct drm_gem_object **obj, uint64_t *iova)
369 {
370 	if (idx >= submit->nr_bos) {
371 		return SUBMIT_ERROR(EINVAL, submit, "invalid buffer index: %u (out of %u)\n",
372 				    idx, submit->nr_bos);
373 	}
374 
375 	if (obj)
376 		*obj = submit->bos[idx].obj;
377 	if (iova)
378 		*iova = submit->bos[idx].iova;
379 
380 	return 0;
381 }
382 
383 /* process the reloc's and patch up the cmdstream as needed: */
384 static int submit_reloc(struct msm_gem_submit *submit, struct drm_gem_object *obj,
385 		uint32_t offset, uint32_t nr_relocs, struct drm_msm_gem_submit_reloc *relocs)
386 {
387 	uint32_t i, last_offset = 0;
388 	uint32_t *ptr;
389 	int ret = 0;
390 
391 	if (offset % 4)
392 		return SUBMIT_ERROR(EINVAL, submit, "non-aligned cmdstream buffer: %u\n", offset);
393 
394 	/* For now, just map the entire thing.  Eventually we probably
395 	 * to do it page-by-page, w/ kmap() if not vmap()d..
396 	 */
397 	ptr = msm_gem_get_vaddr_locked(obj);
398 
399 	if (IS_ERR(ptr)) {
400 		ret = PTR_ERR(ptr);
401 		DBG("failed to map: %d", ret);
402 		return ret;
403 	}
404 
405 	for (i = 0; i < nr_relocs; i++) {
406 		struct drm_msm_gem_submit_reloc submit_reloc = relocs[i];
407 		uint32_t off;
408 		uint64_t iova;
409 
410 		if (submit_reloc.submit_offset % 4) {
411 			ret = SUBMIT_ERROR(EINVAL, submit, "non-aligned reloc offset: %u\n",
412 					   submit_reloc.submit_offset);
413 			goto out;
414 		}
415 
416 		/* offset in dwords: */
417 		off = submit_reloc.submit_offset / 4;
418 
419 		if ((off >= (obj->size / 4)) ||
420 				(off < last_offset)) {
421 			ret = SUBMIT_ERROR(EINVAL, submit, "invalid offset %u at reloc %u\n", off, i);
422 			goto out;
423 		}
424 
425 		ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova);
426 		if (ret)
427 			goto out;
428 
429 		iova += submit_reloc.reloc_offset;
430 
431 		if (submit_reloc.shift < 0)
432 			iova >>= -submit_reloc.shift;
433 		else
434 			iova <<= submit_reloc.shift;
435 
436 		ptr[off] = iova | submit_reloc.or;
437 
438 		last_offset = off;
439 	}
440 
441 out:
442 	msm_gem_put_vaddr_locked(obj);
443 
444 	return ret;
445 }
446 
447 /* Cleanup submit at end of ioctl.  In the error case, this also drops
448  * references, unpins, and drops active refcnt.  In the non-error case,
449  * this is done when the submit is retired.
450  */
451 static void submit_cleanup(struct msm_gem_submit *submit, bool error)
452 {
453 	if (error) {
454 		submit_unpin_objects(submit);
455 		/* job wasn't enqueued to scheduler, so early retirement: */
456 		msm_submit_retire(submit);
457 	}
458 
459 	if (submit->exec.objects)
460 		drm_exec_fini(&submit->exec);
461 }
462 
463 void msm_submit_retire(struct msm_gem_submit *submit)
464 {
465 	int i;
466 
467 	for (i = 0; i < submit->nr_bos; i++) {
468 		struct drm_gem_object *obj = submit->bos[i].obj;
469 
470 		drm_gem_object_put(obj);
471 	}
472 }
473 
474 struct msm_submit_post_dep {
475 	struct drm_syncobj *syncobj;
476 	uint64_t point;
477 	struct dma_fence_chain *chain;
478 };
479 
480 static struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit,
481                                            struct drm_file *file,
482                                            uint64_t in_syncobjs_addr,
483                                            uint32_t nr_in_syncobjs,
484                                            size_t syncobj_stride)
485 {
486 	struct drm_syncobj **syncobjs = NULL;
487 	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
488 	int ret = 0;
489 	uint32_t i, j;
490 
491 	syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
492 	                   GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
493 	if (!syncobjs)
494 		return ERR_PTR(-ENOMEM);
495 
496 	for (i = 0; i < nr_in_syncobjs; ++i) {
497 		uint64_t address = in_syncobjs_addr + i * syncobj_stride;
498 
499 		if (copy_from_user(&syncobj_desc,
500 			           u64_to_user_ptr(address),
501 			           min(syncobj_stride, sizeof(syncobj_desc)))) {
502 			ret = -EFAULT;
503 			break;
504 		}
505 
506 		if (syncobj_desc.point &&
507 		    !drm_core_check_feature(submit->dev, DRIVER_SYNCOBJ_TIMELINE)) {
508 			ret = SUBMIT_ERROR(EOPNOTSUPP, submit, "syncobj timeline unsupported");
509 			break;
510 		}
511 
512 		if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) {
513 			ret = SUBMIT_ERROR(EINVAL, submit, "invalid syncobj flags: %x", syncobj_desc.flags);
514 			break;
515 		}
516 
517 		ret = drm_sched_job_add_syncobj_dependency(&submit->base, file,
518 							   syncobj_desc.handle, syncobj_desc.point);
519 		if (ret)
520 			break;
521 
522 		if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) {
523 			syncobjs[i] =
524 				drm_syncobj_find(file, syncobj_desc.handle);
525 			if (!syncobjs[i]) {
526 				ret = SUBMIT_ERROR(EINVAL, submit, "invalid syncobj handle: %u", i);
527 				break;
528 			}
529 		}
530 	}
531 
532 	if (ret) {
533 		for (j = 0; j <= i; ++j) {
534 			if (syncobjs[j])
535 				drm_syncobj_put(syncobjs[j]);
536 		}
537 		kfree(syncobjs);
538 		return ERR_PTR(ret);
539 	}
540 	return syncobjs;
541 }
542 
543 static void msm_reset_syncobjs(struct drm_syncobj **syncobjs,
544                                uint32_t nr_syncobjs)
545 {
546 	uint32_t i;
547 
548 	for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
549 		if (syncobjs[i])
550 			drm_syncobj_replace_fence(syncobjs[i], NULL);
551 	}
552 }
553 
554 static struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev,
555                                                        struct drm_file *file,
556                                                        uint64_t syncobjs_addr,
557                                                        uint32_t nr_syncobjs,
558                                                        size_t syncobj_stride)
559 {
560 	struct msm_submit_post_dep *post_deps;
561 	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
562 	int ret = 0;
563 	uint32_t i, j;
564 
565 	post_deps = kcalloc(nr_syncobjs, sizeof(*post_deps),
566 			    GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
567 	if (!post_deps)
568 		return ERR_PTR(-ENOMEM);
569 
570 	for (i = 0; i < nr_syncobjs; ++i) {
571 		uint64_t address = syncobjs_addr + i * syncobj_stride;
572 
573 		if (copy_from_user(&syncobj_desc,
574 			           u64_to_user_ptr(address),
575 			           min(syncobj_stride, sizeof(syncobj_desc)))) {
576 			ret = -EFAULT;
577 			break;
578 		}
579 
580 		post_deps[i].point = syncobj_desc.point;
581 
582 		if (syncobj_desc.flags) {
583 			ret = UERR(EINVAL, dev, "invalid syncobj flags");
584 			break;
585 		}
586 
587 		if (syncobj_desc.point) {
588 			if (!drm_core_check_feature(dev,
589 			                            DRIVER_SYNCOBJ_TIMELINE)) {
590 				ret = UERR(EOPNOTSUPP, dev, "syncobj timeline unsupported");
591 				break;
592 			}
593 
594 			post_deps[i].chain = dma_fence_chain_alloc();
595 			if (!post_deps[i].chain) {
596 				ret = -ENOMEM;
597 				break;
598 			}
599 		}
600 
601 		post_deps[i].syncobj =
602 			drm_syncobj_find(file, syncobj_desc.handle);
603 		if (!post_deps[i].syncobj) {
604 			ret = UERR(EINVAL, dev, "invalid syncobj handle");
605 			break;
606 		}
607 	}
608 
609 	if (ret) {
610 		for (j = 0; j <= i; ++j) {
611 			dma_fence_chain_free(post_deps[j].chain);
612 			if (post_deps[j].syncobj)
613 				drm_syncobj_put(post_deps[j].syncobj);
614 		}
615 
616 		kfree(post_deps);
617 		return ERR_PTR(ret);
618 	}
619 
620 	return post_deps;
621 }
622 
623 static void msm_process_post_deps(struct msm_submit_post_dep *post_deps,
624                                   uint32_t count, struct dma_fence *fence)
625 {
626 	uint32_t i;
627 
628 	for (i = 0; post_deps && i < count; ++i) {
629 		if (post_deps[i].chain) {
630 			drm_syncobj_add_point(post_deps[i].syncobj,
631 			                      post_deps[i].chain,
632 			                      fence, post_deps[i].point);
633 			post_deps[i].chain = NULL;
634 		} else {
635 			drm_syncobj_replace_fence(post_deps[i].syncobj,
636 			                          fence);
637 		}
638 	}
639 }
640 
641 int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
642 		struct drm_file *file)
643 {
644 	struct msm_drm_private *priv = dev->dev_private;
645 	struct drm_msm_gem_submit *args = data;
646 	struct msm_file_private *ctx = file->driver_priv;
647 	struct msm_gem_submit *submit = NULL;
648 	struct msm_gpu *gpu = priv->gpu;
649 	struct msm_gpu_submitqueue *queue;
650 	struct msm_ringbuffer *ring;
651 	struct msm_submit_post_dep *post_deps = NULL;
652 	struct drm_syncobj **syncobjs_to_reset = NULL;
653 	int out_fence_fd = -1;
654 	unsigned i;
655 	int ret;
656 
657 	if (!gpu)
658 		return -ENXIO;
659 
660 	if (args->pad)
661 		return -EINVAL;
662 
663 	if (unlikely(!ctx->aspace) && !capable(CAP_SYS_RAWIO)) {
664 		DRM_ERROR_RATELIMITED("IOMMU support or CAP_SYS_RAWIO required!\n");
665 		return -EPERM;
666 	}
667 
668 	/* for now, we just have 3d pipe.. eventually this would need to
669 	 * be more clever to dispatch to appropriate gpu module:
670 	 */
671 	if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
672 		return UERR(EINVAL, dev, "invalid pipe");
673 
674 	if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
675 		return UERR(EINVAL, dev, "invalid flags");
676 
677 	if (args->flags & MSM_SUBMIT_SUDO) {
678 		if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
679 		    !capable(CAP_SYS_RAWIO))
680 			return -EINVAL;
681 	}
682 
683 	queue = msm_submitqueue_get(ctx, args->queueid);
684 	if (!queue)
685 		return -ENOENT;
686 
687 	ring = gpu->rb[queue->ring_nr];
688 
689 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
690 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
691 		if (out_fence_fd < 0) {
692 			ret = out_fence_fd;
693 			goto out_post_unlock;
694 		}
695 	}
696 
697 	submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds,
698 			       file->client_id);
699 	if (IS_ERR(submit)) {
700 		ret = PTR_ERR(submit);
701 		goto out_post_unlock;
702 	}
703 
704 	trace_msm_gpu_submit(pid_nr(submit->pid), ring->id, submit->ident,
705 		args->nr_bos, args->nr_cmds);
706 
707 	ret = mutex_lock_interruptible(&queue->lock);
708 	if (ret)
709 		goto out_post_unlock;
710 
711 	if (args->flags & MSM_SUBMIT_SUDO)
712 		submit->in_rb = true;
713 
714 	if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
715 		struct dma_fence *in_fence;
716 
717 		in_fence = sync_file_get_fence(args->fence_fd);
718 
719 		if (!in_fence) {
720 			ret = UERR(EINVAL, dev, "invalid in-fence");
721 			goto out_unlock;
722 		}
723 
724 		ret = drm_sched_job_add_dependency(&submit->base, in_fence);
725 		if (ret)
726 			goto out_unlock;
727 	}
728 
729 	if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) {
730 		syncobjs_to_reset = msm_parse_deps(submit, file,
731 		                                   args->in_syncobjs,
732 		                                   args->nr_in_syncobjs,
733 		                                   args->syncobj_stride);
734 		if (IS_ERR(syncobjs_to_reset)) {
735 			ret = PTR_ERR(syncobjs_to_reset);
736 			goto out_unlock;
737 		}
738 	}
739 
740 	if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) {
741 		post_deps = msm_parse_post_deps(dev, file,
742 		                                args->out_syncobjs,
743 		                                args->nr_out_syncobjs,
744 		                                args->syncobj_stride);
745 		if (IS_ERR(post_deps)) {
746 			ret = PTR_ERR(post_deps);
747 			goto out_unlock;
748 		}
749 	}
750 
751 	ret = submit_lookup_objects(submit, args, file);
752 	if (ret)
753 		goto out;
754 
755 	ret = submit_lookup_cmds(submit, args, file);
756 	if (ret)
757 		goto out;
758 
759 	/* copy_*_user while holding a ww ticket upsets lockdep */
760 	ret = submit_lock_objects(submit);
761 	if (ret)
762 		goto out;
763 
764 	if (!(args->flags & MSM_SUBMIT_NO_IMPLICIT)) {
765 		ret = submit_fence_sync(submit);
766 		if (ret)
767 			goto out;
768 	}
769 
770 	ret = submit_pin_objects(submit);
771 	if (ret)
772 		goto out;
773 
774 	for (i = 0; i < args->nr_cmds; i++) {
775 		struct drm_gem_object *obj;
776 		uint64_t iova;
777 
778 		ret = submit_bo(submit, submit->cmd[i].idx, &obj, &iova);
779 		if (ret)
780 			goto out;
781 
782 		if (!submit->cmd[i].size ||
783 		    (size_add(submit->cmd[i].size, submit->cmd[i].offset) > obj->size / 4)) {
784 			ret = UERR(EINVAL, dev, "invalid cmdstream size: %u\n",
785 				   submit->cmd[i].size * 4);
786 			goto out;
787 		}
788 
789 		submit->cmd[i].iova = iova + (submit->cmd[i].offset * 4);
790 
791 		if (likely(!submit->cmd[i].nr_relocs))
792 			continue;
793 
794 		if (!gpu->allow_relocs) {
795 			ret = UERR(EINVAL, dev, "relocs not allowed\n");
796 			goto out;
797 		}
798 
799 		ret = submit_reloc(submit, obj, submit->cmd[i].offset * 4,
800 				submit->cmd[i].nr_relocs, submit->cmd[i].relocs);
801 		if (ret)
802 			goto out;
803 	}
804 
805 	submit->nr_cmds = i;
806 
807 	idr_preload(GFP_KERNEL);
808 
809 	spin_lock(&queue->idr_lock);
810 
811 	/*
812 	 * If using userspace provided seqno fence, validate that the id
813 	 * is available before arming sched job.  Since access to fence_idr
814 	 * is serialized on the queue lock, the slot should be still avail
815 	 * after the job is armed
816 	 */
817 	if ((args->flags & MSM_SUBMIT_FENCE_SN_IN) &&
818 			(!args->fence || idr_find(&queue->fence_idr, args->fence))) {
819 		spin_unlock(&queue->idr_lock);
820 		idr_preload_end();
821 		ret = UERR(EINVAL, dev, "invalid in-fence-sn");
822 		goto out;
823 	}
824 
825 	drm_sched_job_arm(&submit->base);
826 
827 	submit->user_fence = dma_fence_get(&submit->base.s_fence->finished);
828 
829 	if (args->flags & MSM_SUBMIT_FENCE_SN_IN) {
830 		/*
831 		 * Userspace has assigned the seqno fence that it wants
832 		 * us to use.  It is an error to pick a fence sequence
833 		 * number that is not available.
834 		 */
835 		submit->fence_id = args->fence;
836 		ret = idr_alloc_u32(&queue->fence_idr, submit->user_fence,
837 				    &submit->fence_id, submit->fence_id,
838 				    GFP_NOWAIT);
839 		/*
840 		 * We've already validated that the fence_id slot is valid,
841 		 * so if idr_alloc_u32 failed, it is a kernel bug
842 		 */
843 		WARN_ON(ret);
844 	} else {
845 		/*
846 		 * Allocate an id which can be used by WAIT_FENCE ioctl to map
847 		 * back to the underlying fence.
848 		 */
849 		submit->fence_id = idr_alloc_cyclic(&queue->fence_idr,
850 						    submit->user_fence, 1,
851 						    INT_MAX, GFP_NOWAIT);
852 	}
853 
854 	spin_unlock(&queue->idr_lock);
855 	idr_preload_end();
856 
857 	if (submit->fence_id < 0) {
858 		ret = submit->fence_id;
859 		submit->fence_id = 0;
860 	}
861 
862 	if (ret == 0 && args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
863 		struct sync_file *sync_file = sync_file_create(submit->user_fence);
864 		if (!sync_file) {
865 			ret = -ENOMEM;
866 		} else {
867 			fd_install(out_fence_fd, sync_file->file);
868 			args->fence_fd = out_fence_fd;
869 		}
870 	}
871 
872 	if (ret)
873 		goto out;
874 
875 	submit_attach_object_fences(submit);
876 
877 	/* The scheduler owns a ref now: */
878 	msm_gem_submit_get(submit);
879 
880 	msm_rd_dump_submit(priv->rd, submit, NULL);
881 
882 	drm_sched_entity_push_job(&submit->base);
883 
884 	args->fence = submit->fence_id;
885 	queue->last_fence = submit->fence_id;
886 
887 	msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs);
888 	msm_process_post_deps(post_deps, args->nr_out_syncobjs,
889 	                      submit->user_fence);
890 
891 
892 out:
893 	submit_cleanup(submit, !!ret);
894 out_unlock:
895 	mutex_unlock(&queue->lock);
896 out_post_unlock:
897 	if (ret && (out_fence_fd >= 0))
898 		put_unused_fd(out_fence_fd);
899 
900 	if (!IS_ERR_OR_NULL(submit)) {
901 		msm_gem_submit_put(submit);
902 	} else {
903 		/*
904 		 * If the submit hasn't yet taken ownership of the queue
905 		 * then we need to drop the reference ourself:
906 		 */
907 		msm_submitqueue_put(queue);
908 	}
909 	if (!IS_ERR_OR_NULL(post_deps)) {
910 		for (i = 0; i < args->nr_out_syncobjs; ++i) {
911 			kfree(post_deps[i].chain);
912 			drm_syncobj_put(post_deps[i].syncobj);
913 		}
914 		kfree(post_deps);
915 	}
916 
917 	if (!IS_ERR_OR_NULL(syncobjs_to_reset)) {
918 		for (i = 0; i < args->nr_in_syncobjs; ++i) {
919 			if (syncobjs_to_reset[i])
920 				drm_syncobj_put(syncobjs_to_reset[i]);
921 		}
922 		kfree(syncobjs_to_reset);
923 	}
924 
925 	return ret;
926 }
927