xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/msm/msm_gem_submit.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2013 Red Hat
4*4882a593Smuzhiyun  * Author: Rob Clark <robdclark@gmail.com>
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/file.h>
8*4882a593Smuzhiyun #include <linux/sync_file.h>
9*4882a593Smuzhiyun #include <linux/uaccess.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <drm/drm_drv.h>
12*4882a593Smuzhiyun #include <drm/drm_file.h>
13*4882a593Smuzhiyun #include <drm/drm_syncobj.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include "msm_drv.h"
16*4882a593Smuzhiyun #include "msm_gpu.h"
17*4882a593Smuzhiyun #include "msm_gem.h"
18*4882a593Smuzhiyun #include "msm_gpu_trace.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun  * Cmdstream submission:
22*4882a593Smuzhiyun  */
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun /* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
25*4882a593Smuzhiyun #define BO_VALID    0x8000   /* is current addr in cmdstream correct/valid? */
26*4882a593Smuzhiyun #define BO_LOCKED   0x4000
27*4882a593Smuzhiyun #define BO_PINNED   0x2000
28*4882a593Smuzhiyun 
submit_create(struct drm_device * dev,struct msm_gpu * gpu,struct msm_gpu_submitqueue * queue,uint32_t nr_bos,uint32_t nr_cmds)29*4882a593Smuzhiyun static struct msm_gem_submit *submit_create(struct drm_device *dev,
30*4882a593Smuzhiyun 		struct msm_gpu *gpu,
31*4882a593Smuzhiyun 		struct msm_gpu_submitqueue *queue, uint32_t nr_bos,
32*4882a593Smuzhiyun 		uint32_t nr_cmds)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	struct msm_gem_submit *submit;
35*4882a593Smuzhiyun 	uint64_t sz = struct_size(submit, bos, nr_bos) +
36*4882a593Smuzhiyun 				  ((u64)nr_cmds * sizeof(submit->cmd[0]));
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (sz > SIZE_MAX)
39*4882a593Smuzhiyun 		return NULL;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	submit = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
42*4882a593Smuzhiyun 	if (!submit)
43*4882a593Smuzhiyun 		return NULL;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	submit->dev = dev;
46*4882a593Smuzhiyun 	submit->aspace = queue->ctx->aspace;
47*4882a593Smuzhiyun 	submit->gpu = gpu;
48*4882a593Smuzhiyun 	submit->fence = NULL;
49*4882a593Smuzhiyun 	submit->cmd = (void *)&submit->bos[nr_bos];
50*4882a593Smuzhiyun 	submit->queue = queue;
51*4882a593Smuzhiyun 	submit->ring = gpu->rb[queue->prio];
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	/* initially, until copy_from_user() and bo lookup succeeds: */
54*4882a593Smuzhiyun 	submit->nr_bos = 0;
55*4882a593Smuzhiyun 	submit->nr_cmds = 0;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	INIT_LIST_HEAD(&submit->node);
58*4882a593Smuzhiyun 	INIT_LIST_HEAD(&submit->bo_list);
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	return submit;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
msm_gem_submit_free(struct msm_gem_submit * submit)63*4882a593Smuzhiyun void msm_gem_submit_free(struct msm_gem_submit *submit)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	dma_fence_put(submit->fence);
66*4882a593Smuzhiyun 	list_del(&submit->node);
67*4882a593Smuzhiyun 	put_pid(submit->pid);
68*4882a593Smuzhiyun 	msm_submitqueue_put(submit->queue);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	kfree(submit);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
submit_lookup_objects(struct msm_gem_submit * submit,struct drm_msm_gem_submit * args,struct drm_file * file)73*4882a593Smuzhiyun static int submit_lookup_objects(struct msm_gem_submit *submit,
74*4882a593Smuzhiyun 		struct drm_msm_gem_submit *args, struct drm_file *file)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	unsigned i;
77*4882a593Smuzhiyun 	int ret = 0;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	for (i = 0; i < args->nr_bos; i++) {
80*4882a593Smuzhiyun 		struct drm_msm_gem_submit_bo submit_bo;
81*4882a593Smuzhiyun 		void __user *userptr =
82*4882a593Smuzhiyun 			u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 		/* make sure we don't have garbage flags, in case we hit
85*4882a593Smuzhiyun 		 * error path before flags is initialized:
86*4882a593Smuzhiyun 		 */
87*4882a593Smuzhiyun 		submit->bos[i].flags = 0;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 		if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
90*4882a593Smuzhiyun 			ret = -EFAULT;
91*4882a593Smuzhiyun 			i = 0;
92*4882a593Smuzhiyun 			goto out;
93*4882a593Smuzhiyun 		}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /* at least one of READ and/or WRITE flags should be set: */
96*4882a593Smuzhiyun #define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 		if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
99*4882a593Smuzhiyun 			!(submit_bo.flags & MANDATORY_FLAGS)) {
100*4882a593Smuzhiyun 			DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
101*4882a593Smuzhiyun 			ret = -EINVAL;
102*4882a593Smuzhiyun 			i = 0;
103*4882a593Smuzhiyun 			goto out;
104*4882a593Smuzhiyun 		}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 		submit->bos[i].handle = submit_bo.handle;
107*4882a593Smuzhiyun 		submit->bos[i].flags = submit_bo.flags;
108*4882a593Smuzhiyun 		/* in validate_objects() we figure out if this is true: */
109*4882a593Smuzhiyun 		submit->bos[i].iova  = submit_bo.presumed;
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	spin_lock(&file->table_lock);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	for (i = 0; i < args->nr_bos; i++) {
115*4882a593Smuzhiyun 		struct drm_gem_object *obj;
116*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 		/* normally use drm_gem_object_lookup(), but for bulk lookup
119*4882a593Smuzhiyun 		 * all under single table_lock just hit object_idr directly:
120*4882a593Smuzhiyun 		 */
121*4882a593Smuzhiyun 		obj = idr_find(&file->object_idr, submit->bos[i].handle);
122*4882a593Smuzhiyun 		if (!obj) {
123*4882a593Smuzhiyun 			DRM_ERROR("invalid handle %u at index %u\n", submit->bos[i].handle, i);
124*4882a593Smuzhiyun 			ret = -EINVAL;
125*4882a593Smuzhiyun 			goto out_unlock;
126*4882a593Smuzhiyun 		}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 		msm_obj = to_msm_bo(obj);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 		if (!list_empty(&msm_obj->submit_entry)) {
131*4882a593Smuzhiyun 			DRM_ERROR("handle %u at index %u already on submit list\n",
132*4882a593Smuzhiyun 					submit->bos[i].handle, i);
133*4882a593Smuzhiyun 			ret = -EINVAL;
134*4882a593Smuzhiyun 			goto out_unlock;
135*4882a593Smuzhiyun 		}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		drm_gem_object_get(obj);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 		submit->bos[i].obj = msm_obj;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 		list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun out_unlock:
145*4882a593Smuzhiyun 	spin_unlock(&file->table_lock);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun out:
148*4882a593Smuzhiyun 	submit->nr_bos = i;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return ret;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
submit_unlock_unpin_bo(struct msm_gem_submit * submit,int i,bool backoff)153*4882a593Smuzhiyun static void submit_unlock_unpin_bo(struct msm_gem_submit *submit,
154*4882a593Smuzhiyun 		int i, bool backoff)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	struct msm_gem_object *msm_obj = submit->bos[i].obj;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	if (submit->bos[i].flags & BO_PINNED)
159*4882a593Smuzhiyun 		msm_gem_unpin_iova(&msm_obj->base, submit->aspace);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	if (submit->bos[i].flags & BO_LOCKED)
162*4882a593Smuzhiyun 		dma_resv_unlock(msm_obj->base.resv);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	if (backoff && !(submit->bos[i].flags & BO_VALID))
165*4882a593Smuzhiyun 		submit->bos[i].iova = 0;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun /* This is where we make sure all the bo's are reserved and pin'd: */
submit_lock_objects(struct msm_gem_submit * submit)171*4882a593Smuzhiyun static int submit_lock_objects(struct msm_gem_submit *submit)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	int contended, slow_locked = -1, i, ret = 0;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun retry:
176*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
177*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 		if (slow_locked == i)
180*4882a593Smuzhiyun 			slow_locked = -1;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		contended = i;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 		if (!(submit->bos[i].flags & BO_LOCKED)) {
185*4882a593Smuzhiyun 			ret = dma_resv_lock_interruptible(msm_obj->base.resv,
186*4882a593Smuzhiyun 							  &submit->ticket);
187*4882a593Smuzhiyun 			if (ret)
188*4882a593Smuzhiyun 				goto fail;
189*4882a593Smuzhiyun 			submit->bos[i].flags |= BO_LOCKED;
190*4882a593Smuzhiyun 		}
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	ww_acquire_done(&submit->ticket);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	return 0;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun fail:
198*4882a593Smuzhiyun 	for (; i >= 0; i--)
199*4882a593Smuzhiyun 		submit_unlock_unpin_bo(submit, i, true);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	if (slow_locked > 0)
202*4882a593Smuzhiyun 		submit_unlock_unpin_bo(submit, slow_locked, true);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (ret == -EDEADLK) {
205*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj = submit->bos[contended].obj;
206*4882a593Smuzhiyun 		/* we lost out in a seqno race, lock and retry.. */
207*4882a593Smuzhiyun 		ret = dma_resv_lock_slow_interruptible(msm_obj->base.resv,
208*4882a593Smuzhiyun 						       &submit->ticket);
209*4882a593Smuzhiyun 		if (!ret) {
210*4882a593Smuzhiyun 			submit->bos[contended].flags |= BO_LOCKED;
211*4882a593Smuzhiyun 			slow_locked = contended;
212*4882a593Smuzhiyun 			goto retry;
213*4882a593Smuzhiyun 		}
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	return ret;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
submit_fence_sync(struct msm_gem_submit * submit,bool no_implicit)219*4882a593Smuzhiyun static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	int i, ret = 0;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
224*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
225*4882a593Smuzhiyun 		bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		if (!write) {
228*4882a593Smuzhiyun 			/* NOTE: _reserve_shared() must happen before
229*4882a593Smuzhiyun 			 * _add_shared_fence(), which makes this a slightly
230*4882a593Smuzhiyun 			 * strange place to call it.  OTOH this is a
231*4882a593Smuzhiyun 			 * convenient can-fail point to hook it in.
232*4882a593Smuzhiyun 			 */
233*4882a593Smuzhiyun 			ret = dma_resv_reserve_shared(msm_obj->base.resv,
234*4882a593Smuzhiyun 								1);
235*4882a593Smuzhiyun 			if (ret)
236*4882a593Smuzhiyun 				return ret;
237*4882a593Smuzhiyun 		}
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		if (no_implicit)
240*4882a593Smuzhiyun 			continue;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 		ret = msm_gem_sync_object(&msm_obj->base, submit->ring->fctx,
243*4882a593Smuzhiyun 			write);
244*4882a593Smuzhiyun 		if (ret)
245*4882a593Smuzhiyun 			break;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	return ret;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
submit_pin_objects(struct msm_gem_submit * submit)251*4882a593Smuzhiyun static int submit_pin_objects(struct msm_gem_submit *submit)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	int i, ret = 0;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	submit->valid = true;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
258*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
259*4882a593Smuzhiyun 		uint64_t iova;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 		/* if locking succeeded, pin bo: */
262*4882a593Smuzhiyun 		ret = msm_gem_get_and_pin_iova(&msm_obj->base,
263*4882a593Smuzhiyun 				submit->aspace, &iova);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 		if (ret)
266*4882a593Smuzhiyun 			break;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		submit->bos[i].flags |= BO_PINNED;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 		if (iova == submit->bos[i].iova) {
271*4882a593Smuzhiyun 			submit->bos[i].flags |= BO_VALID;
272*4882a593Smuzhiyun 		} else {
273*4882a593Smuzhiyun 			submit->bos[i].iova = iova;
274*4882a593Smuzhiyun 			/* iova changed, so address in cmdstream is not valid: */
275*4882a593Smuzhiyun 			submit->bos[i].flags &= ~BO_VALID;
276*4882a593Smuzhiyun 			submit->valid = false;
277*4882a593Smuzhiyun 		}
278*4882a593Smuzhiyun 	}
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	return ret;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun 
submit_bo(struct msm_gem_submit * submit,uint32_t idx,struct msm_gem_object ** obj,uint64_t * iova,bool * valid)283*4882a593Smuzhiyun static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
284*4882a593Smuzhiyun 		struct msm_gem_object **obj, uint64_t *iova, bool *valid)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	if (idx >= submit->nr_bos) {
287*4882a593Smuzhiyun 		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
288*4882a593Smuzhiyun 				idx, submit->nr_bos);
289*4882a593Smuzhiyun 		return -EINVAL;
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (obj)
293*4882a593Smuzhiyun 		*obj = submit->bos[idx].obj;
294*4882a593Smuzhiyun 	if (iova)
295*4882a593Smuzhiyun 		*iova = submit->bos[idx].iova;
296*4882a593Smuzhiyun 	if (valid)
297*4882a593Smuzhiyun 		*valid = !!(submit->bos[idx].flags & BO_VALID);
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun /* process the reloc's and patch up the cmdstream as needed: */
submit_reloc(struct msm_gem_submit * submit,struct msm_gem_object * obj,uint32_t offset,uint32_t nr_relocs,uint64_t relocs)303*4882a593Smuzhiyun static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
304*4882a593Smuzhiyun 		uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	uint32_t i, last_offset = 0;
307*4882a593Smuzhiyun 	uint32_t *ptr;
308*4882a593Smuzhiyun 	int ret = 0;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (!nr_relocs)
311*4882a593Smuzhiyun 		return 0;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	if (offset % 4) {
314*4882a593Smuzhiyun 		DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
315*4882a593Smuzhiyun 		return -EINVAL;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	/* For now, just map the entire thing.  Eventually we probably
319*4882a593Smuzhiyun 	 * to do it page-by-page, w/ kmap() if not vmap()d..
320*4882a593Smuzhiyun 	 */
321*4882a593Smuzhiyun 	ptr = msm_gem_get_vaddr(&obj->base);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (IS_ERR(ptr)) {
324*4882a593Smuzhiyun 		ret = PTR_ERR(ptr);
325*4882a593Smuzhiyun 		DBG("failed to map: %d", ret);
326*4882a593Smuzhiyun 		return ret;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	for (i = 0; i < nr_relocs; i++) {
330*4882a593Smuzhiyun 		struct drm_msm_gem_submit_reloc submit_reloc;
331*4882a593Smuzhiyun 		void __user *userptr =
332*4882a593Smuzhiyun 			u64_to_user_ptr(relocs + (i * sizeof(submit_reloc)));
333*4882a593Smuzhiyun 		uint32_t off;
334*4882a593Smuzhiyun 		uint64_t iova;
335*4882a593Smuzhiyun 		bool valid;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 		if (copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc))) {
338*4882a593Smuzhiyun 			ret = -EFAULT;
339*4882a593Smuzhiyun 			goto out;
340*4882a593Smuzhiyun 		}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 		if (submit_reloc.submit_offset % 4) {
343*4882a593Smuzhiyun 			DRM_ERROR("non-aligned reloc offset: %u\n",
344*4882a593Smuzhiyun 					submit_reloc.submit_offset);
345*4882a593Smuzhiyun 			ret = -EINVAL;
346*4882a593Smuzhiyun 			goto out;
347*4882a593Smuzhiyun 		}
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 		/* offset in dwords: */
350*4882a593Smuzhiyun 		off = submit_reloc.submit_offset / 4;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 		if ((off >= (obj->base.size / 4)) ||
353*4882a593Smuzhiyun 				(off < last_offset)) {
354*4882a593Smuzhiyun 			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
355*4882a593Smuzhiyun 			ret = -EINVAL;
356*4882a593Smuzhiyun 			goto out;
357*4882a593Smuzhiyun 		}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 		ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
360*4882a593Smuzhiyun 		if (ret)
361*4882a593Smuzhiyun 			goto out;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 		if (valid)
364*4882a593Smuzhiyun 			continue;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 		iova += submit_reloc.reloc_offset;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 		if (submit_reloc.shift < 0)
369*4882a593Smuzhiyun 			iova >>= -submit_reloc.shift;
370*4882a593Smuzhiyun 		else
371*4882a593Smuzhiyun 			iova <<= submit_reloc.shift;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 		ptr[off] = iova | submit_reloc.or;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 		last_offset = off;
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun out:
379*4882a593Smuzhiyun 	msm_gem_put_vaddr(&obj->base);
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	return ret;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
submit_cleanup(struct msm_gem_submit * submit)384*4882a593Smuzhiyun static void submit_cleanup(struct msm_gem_submit *submit)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun 	unsigned i;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
389*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj = submit->bos[i].obj;
390*4882a593Smuzhiyun 		submit_unlock_unpin_bo(submit, i, false);
391*4882a593Smuzhiyun 		list_del_init(&msm_obj->submit_entry);
392*4882a593Smuzhiyun 		drm_gem_object_put_locked(&msm_obj->base);
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun struct msm_submit_post_dep {
398*4882a593Smuzhiyun 	struct drm_syncobj *syncobj;
399*4882a593Smuzhiyun 	uint64_t point;
400*4882a593Smuzhiyun 	struct dma_fence_chain *chain;
401*4882a593Smuzhiyun };
402*4882a593Smuzhiyun 
msm_wait_deps(struct drm_device * dev,struct drm_file * file,uint64_t in_syncobjs_addr,uint32_t nr_in_syncobjs,size_t syncobj_stride,struct msm_ringbuffer * ring)403*4882a593Smuzhiyun static struct drm_syncobj **msm_wait_deps(struct drm_device *dev,
404*4882a593Smuzhiyun                                           struct drm_file *file,
405*4882a593Smuzhiyun                                           uint64_t in_syncobjs_addr,
406*4882a593Smuzhiyun                                           uint32_t nr_in_syncobjs,
407*4882a593Smuzhiyun                                           size_t syncobj_stride,
408*4882a593Smuzhiyun                                           struct msm_ringbuffer *ring)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	struct drm_syncobj **syncobjs = NULL;
411*4882a593Smuzhiyun 	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
412*4882a593Smuzhiyun 	int ret = 0;
413*4882a593Smuzhiyun 	uint32_t i, j;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
416*4882a593Smuzhiyun 	                   GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
417*4882a593Smuzhiyun 	if (!syncobjs)
418*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	for (i = 0; i < nr_in_syncobjs; ++i) {
421*4882a593Smuzhiyun 		uint64_t address = in_syncobjs_addr + i * syncobj_stride;
422*4882a593Smuzhiyun 		struct dma_fence *fence;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 		if (copy_from_user(&syncobj_desc,
425*4882a593Smuzhiyun 			           u64_to_user_ptr(address),
426*4882a593Smuzhiyun 			           min(syncobj_stride, sizeof(syncobj_desc)))) {
427*4882a593Smuzhiyun 			ret = -EFAULT;
428*4882a593Smuzhiyun 			break;
429*4882a593Smuzhiyun 		}
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 		if (syncobj_desc.point &&
432*4882a593Smuzhiyun 		    !drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) {
433*4882a593Smuzhiyun 			ret = -EOPNOTSUPP;
434*4882a593Smuzhiyun 			break;
435*4882a593Smuzhiyun 		}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 		if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) {
438*4882a593Smuzhiyun 			ret = -EINVAL;
439*4882a593Smuzhiyun 			break;
440*4882a593Smuzhiyun 		}
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 		ret = drm_syncobj_find_fence(file, syncobj_desc.handle,
443*4882a593Smuzhiyun 		                             syncobj_desc.point, 0, &fence);
444*4882a593Smuzhiyun 		if (ret)
445*4882a593Smuzhiyun 			break;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 		if (!dma_fence_match_context(fence, ring->fctx->context))
448*4882a593Smuzhiyun 			ret = dma_fence_wait(fence, true);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 		dma_fence_put(fence);
451*4882a593Smuzhiyun 		if (ret)
452*4882a593Smuzhiyun 			break;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 		if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) {
455*4882a593Smuzhiyun 			syncobjs[i] =
456*4882a593Smuzhiyun 				drm_syncobj_find(file, syncobj_desc.handle);
457*4882a593Smuzhiyun 			if (!syncobjs[i]) {
458*4882a593Smuzhiyun 				ret = -EINVAL;
459*4882a593Smuzhiyun 				break;
460*4882a593Smuzhiyun 			}
461*4882a593Smuzhiyun 		}
462*4882a593Smuzhiyun 	}
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	if (ret) {
465*4882a593Smuzhiyun 		for (j = 0; j <= i; ++j) {
466*4882a593Smuzhiyun 			if (syncobjs[j])
467*4882a593Smuzhiyun 				drm_syncobj_put(syncobjs[j]);
468*4882a593Smuzhiyun 		}
469*4882a593Smuzhiyun 		kfree(syncobjs);
470*4882a593Smuzhiyun 		return ERR_PTR(ret);
471*4882a593Smuzhiyun 	}
472*4882a593Smuzhiyun 	return syncobjs;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun 
msm_reset_syncobjs(struct drm_syncobj ** syncobjs,uint32_t nr_syncobjs)475*4882a593Smuzhiyun static void msm_reset_syncobjs(struct drm_syncobj **syncobjs,
476*4882a593Smuzhiyun                                uint32_t nr_syncobjs)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun 	uint32_t i;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
481*4882a593Smuzhiyun 		if (syncobjs[i])
482*4882a593Smuzhiyun 			drm_syncobj_replace_fence(syncobjs[i], NULL);
483*4882a593Smuzhiyun 	}
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun 
msm_parse_post_deps(struct drm_device * dev,struct drm_file * file,uint64_t syncobjs_addr,uint32_t nr_syncobjs,size_t syncobj_stride)486*4882a593Smuzhiyun static struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev,
487*4882a593Smuzhiyun                                                        struct drm_file *file,
488*4882a593Smuzhiyun                                                        uint64_t syncobjs_addr,
489*4882a593Smuzhiyun                                                        uint32_t nr_syncobjs,
490*4882a593Smuzhiyun                                                        size_t syncobj_stride)
491*4882a593Smuzhiyun {
492*4882a593Smuzhiyun 	struct msm_submit_post_dep *post_deps;
493*4882a593Smuzhiyun 	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
494*4882a593Smuzhiyun 	int ret = 0;
495*4882a593Smuzhiyun 	uint32_t i, j;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	post_deps = kmalloc_array(nr_syncobjs, sizeof(*post_deps),
498*4882a593Smuzhiyun 	                          GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
499*4882a593Smuzhiyun 	if (!post_deps)
500*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	for (i = 0; i < nr_syncobjs; ++i) {
503*4882a593Smuzhiyun 		uint64_t address = syncobjs_addr + i * syncobj_stride;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 		if (copy_from_user(&syncobj_desc,
506*4882a593Smuzhiyun 			           u64_to_user_ptr(address),
507*4882a593Smuzhiyun 			           min(syncobj_stride, sizeof(syncobj_desc)))) {
508*4882a593Smuzhiyun 			ret = -EFAULT;
509*4882a593Smuzhiyun 			break;
510*4882a593Smuzhiyun 		}
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 		post_deps[i].point = syncobj_desc.point;
513*4882a593Smuzhiyun 		post_deps[i].chain = NULL;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 		if (syncobj_desc.flags) {
516*4882a593Smuzhiyun 			ret = -EINVAL;
517*4882a593Smuzhiyun 			break;
518*4882a593Smuzhiyun 		}
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 		if (syncobj_desc.point) {
521*4882a593Smuzhiyun 			if (!drm_core_check_feature(dev,
522*4882a593Smuzhiyun 			                            DRIVER_SYNCOBJ_TIMELINE)) {
523*4882a593Smuzhiyun 				ret = -EOPNOTSUPP;
524*4882a593Smuzhiyun 				break;
525*4882a593Smuzhiyun 			}
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 			post_deps[i].chain =
528*4882a593Smuzhiyun 				kmalloc(sizeof(*post_deps[i].chain),
529*4882a593Smuzhiyun 				        GFP_KERNEL);
530*4882a593Smuzhiyun 			if (!post_deps[i].chain) {
531*4882a593Smuzhiyun 				ret = -ENOMEM;
532*4882a593Smuzhiyun 				break;
533*4882a593Smuzhiyun 			}
534*4882a593Smuzhiyun 		}
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 		post_deps[i].syncobj =
537*4882a593Smuzhiyun 			drm_syncobj_find(file, syncobj_desc.handle);
538*4882a593Smuzhiyun 		if (!post_deps[i].syncobj) {
539*4882a593Smuzhiyun 			ret = -EINVAL;
540*4882a593Smuzhiyun 			break;
541*4882a593Smuzhiyun 		}
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	if (ret) {
545*4882a593Smuzhiyun 		for (j = 0; j <= i; ++j) {
546*4882a593Smuzhiyun 			kfree(post_deps[j].chain);
547*4882a593Smuzhiyun 			if (post_deps[j].syncobj)
548*4882a593Smuzhiyun 				drm_syncobj_put(post_deps[j].syncobj);
549*4882a593Smuzhiyun 		}
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 		kfree(post_deps);
552*4882a593Smuzhiyun 		return ERR_PTR(ret);
553*4882a593Smuzhiyun 	}
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	return post_deps;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun 
msm_process_post_deps(struct msm_submit_post_dep * post_deps,uint32_t count,struct dma_fence * fence)558*4882a593Smuzhiyun static void msm_process_post_deps(struct msm_submit_post_dep *post_deps,
559*4882a593Smuzhiyun                                   uint32_t count, struct dma_fence *fence)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	uint32_t i;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	for (i = 0; post_deps && i < count; ++i) {
564*4882a593Smuzhiyun 		if (post_deps[i].chain) {
565*4882a593Smuzhiyun 			drm_syncobj_add_point(post_deps[i].syncobj,
566*4882a593Smuzhiyun 			                      post_deps[i].chain,
567*4882a593Smuzhiyun 			                      fence, post_deps[i].point);
568*4882a593Smuzhiyun 			post_deps[i].chain = NULL;
569*4882a593Smuzhiyun 		} else {
570*4882a593Smuzhiyun 			drm_syncobj_replace_fence(post_deps[i].syncobj,
571*4882a593Smuzhiyun 			                          fence);
572*4882a593Smuzhiyun 		}
573*4882a593Smuzhiyun 	}
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun 
msm_ioctl_gem_submit(struct drm_device * dev,void * data,struct drm_file * file)576*4882a593Smuzhiyun int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
577*4882a593Smuzhiyun 		struct drm_file *file)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun 	static atomic_t ident = ATOMIC_INIT(0);
580*4882a593Smuzhiyun 	struct msm_drm_private *priv = dev->dev_private;
581*4882a593Smuzhiyun 	struct drm_msm_gem_submit *args = data;
582*4882a593Smuzhiyun 	struct msm_file_private *ctx = file->driver_priv;
583*4882a593Smuzhiyun 	struct msm_gem_submit *submit;
584*4882a593Smuzhiyun 	struct msm_gpu *gpu = priv->gpu;
585*4882a593Smuzhiyun 	struct sync_file *sync_file = NULL;
586*4882a593Smuzhiyun 	struct msm_gpu_submitqueue *queue;
587*4882a593Smuzhiyun 	struct msm_ringbuffer *ring;
588*4882a593Smuzhiyun 	struct msm_submit_post_dep *post_deps = NULL;
589*4882a593Smuzhiyun 	struct drm_syncobj **syncobjs_to_reset = NULL;
590*4882a593Smuzhiyun 	int out_fence_fd = -1;
591*4882a593Smuzhiyun 	struct pid *pid = get_pid(task_pid(current));
592*4882a593Smuzhiyun 	bool has_ww_ticket = false;
593*4882a593Smuzhiyun 	unsigned i;
594*4882a593Smuzhiyun 	int ret, submitid;
595*4882a593Smuzhiyun 	if (!gpu)
596*4882a593Smuzhiyun 		return -ENXIO;
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	if (args->pad)
599*4882a593Smuzhiyun 		return -EINVAL;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	/* for now, we just have 3d pipe.. eventually this would need to
602*4882a593Smuzhiyun 	 * be more clever to dispatch to appropriate gpu module:
603*4882a593Smuzhiyun 	 */
604*4882a593Smuzhiyun 	if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
605*4882a593Smuzhiyun 		return -EINVAL;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
608*4882a593Smuzhiyun 		return -EINVAL;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_SUDO) {
611*4882a593Smuzhiyun 		if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
612*4882a593Smuzhiyun 		    !capable(CAP_SYS_RAWIO))
613*4882a593Smuzhiyun 			return -EINVAL;
614*4882a593Smuzhiyun 	}
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	queue = msm_submitqueue_get(ctx, args->queueid);
617*4882a593Smuzhiyun 	if (!queue)
618*4882a593Smuzhiyun 		return -ENOENT;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	/* Get a unique identifier for the submission for logging purposes */
621*4882a593Smuzhiyun 	submitid = atomic_inc_return(&ident) - 1;
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	ring = gpu->rb[queue->prio];
624*4882a593Smuzhiyun 	trace_msm_gpu_submit(pid_nr(pid), ring->id, submitid,
625*4882a593Smuzhiyun 		args->nr_bos, args->nr_cmds);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
628*4882a593Smuzhiyun 		struct dma_fence *in_fence;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 		in_fence = sync_file_get_fence(args->fence_fd);
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 		if (!in_fence)
633*4882a593Smuzhiyun 			return -EINVAL;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 		/*
636*4882a593Smuzhiyun 		 * Wait if the fence is from a foreign context, or if the fence
637*4882a593Smuzhiyun 		 * array contains any fence from a foreign context.
638*4882a593Smuzhiyun 		 */
639*4882a593Smuzhiyun 		ret = 0;
640*4882a593Smuzhiyun 		if (!dma_fence_match_context(in_fence, ring->fctx->context))
641*4882a593Smuzhiyun 			ret = dma_fence_wait(in_fence, true);
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 		dma_fence_put(in_fence);
644*4882a593Smuzhiyun 		if (ret)
645*4882a593Smuzhiyun 			return ret;
646*4882a593Smuzhiyun 	}
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) {
649*4882a593Smuzhiyun 		syncobjs_to_reset = msm_wait_deps(dev, file,
650*4882a593Smuzhiyun 		                                  args->in_syncobjs,
651*4882a593Smuzhiyun 		                                  args->nr_in_syncobjs,
652*4882a593Smuzhiyun 		                                  args->syncobj_stride, ring);
653*4882a593Smuzhiyun 		if (IS_ERR(syncobjs_to_reset))
654*4882a593Smuzhiyun 			return PTR_ERR(syncobjs_to_reset);
655*4882a593Smuzhiyun 	}
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) {
658*4882a593Smuzhiyun 		post_deps = msm_parse_post_deps(dev, file,
659*4882a593Smuzhiyun 		                                args->out_syncobjs,
660*4882a593Smuzhiyun 		                                args->nr_out_syncobjs,
661*4882a593Smuzhiyun 		                                args->syncobj_stride);
662*4882a593Smuzhiyun 		if (IS_ERR(post_deps)) {
663*4882a593Smuzhiyun 			ret = PTR_ERR(post_deps);
664*4882a593Smuzhiyun 			goto out_post_unlock;
665*4882a593Smuzhiyun 		}
666*4882a593Smuzhiyun 	}
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	ret = mutex_lock_interruptible(&dev->struct_mutex);
669*4882a593Smuzhiyun 	if (ret)
670*4882a593Smuzhiyun 		goto out_post_unlock;
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
673*4882a593Smuzhiyun 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
674*4882a593Smuzhiyun 		if (out_fence_fd < 0) {
675*4882a593Smuzhiyun 			ret = out_fence_fd;
676*4882a593Smuzhiyun 			goto out_unlock;
677*4882a593Smuzhiyun 		}
678*4882a593Smuzhiyun 	}
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	submit = submit_create(dev, gpu, queue, args->nr_bos,
681*4882a593Smuzhiyun 		args->nr_cmds);
682*4882a593Smuzhiyun 	if (!submit) {
683*4882a593Smuzhiyun 		ret = -ENOMEM;
684*4882a593Smuzhiyun 		goto out_unlock;
685*4882a593Smuzhiyun 	}
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	submit->pid = pid;
688*4882a593Smuzhiyun 	submit->ident = submitid;
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_SUDO)
691*4882a593Smuzhiyun 		submit->in_rb = true;
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	ret = submit_lookup_objects(submit, args, file);
694*4882a593Smuzhiyun 	if (ret)
695*4882a593Smuzhiyun 		goto out;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	/* copy_*_user while holding a ww ticket upsets lockdep */
698*4882a593Smuzhiyun 	ww_acquire_init(&submit->ticket, &reservation_ww_class);
699*4882a593Smuzhiyun 	has_ww_ticket = true;
700*4882a593Smuzhiyun 	ret = submit_lock_objects(submit);
701*4882a593Smuzhiyun 	if (ret)
702*4882a593Smuzhiyun 		goto out;
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	ret = submit_fence_sync(submit, !!(args->flags & MSM_SUBMIT_NO_IMPLICIT));
705*4882a593Smuzhiyun 	if (ret)
706*4882a593Smuzhiyun 		goto out;
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 	ret = submit_pin_objects(submit);
709*4882a593Smuzhiyun 	if (ret)
710*4882a593Smuzhiyun 		goto out;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	for (i = 0; i < args->nr_cmds; i++) {
713*4882a593Smuzhiyun 		struct drm_msm_gem_submit_cmd submit_cmd;
714*4882a593Smuzhiyun 		void __user *userptr =
715*4882a593Smuzhiyun 			u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
716*4882a593Smuzhiyun 		struct msm_gem_object *msm_obj;
717*4882a593Smuzhiyun 		uint64_t iova;
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 		ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
720*4882a593Smuzhiyun 		if (ret) {
721*4882a593Smuzhiyun 			ret = -EFAULT;
722*4882a593Smuzhiyun 			goto out;
723*4882a593Smuzhiyun 		}
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 		/* validate input from userspace: */
726*4882a593Smuzhiyun 		switch (submit_cmd.type) {
727*4882a593Smuzhiyun 		case MSM_SUBMIT_CMD_BUF:
728*4882a593Smuzhiyun 		case MSM_SUBMIT_CMD_IB_TARGET_BUF:
729*4882a593Smuzhiyun 		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
730*4882a593Smuzhiyun 			break;
731*4882a593Smuzhiyun 		default:
732*4882a593Smuzhiyun 			DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
733*4882a593Smuzhiyun 			ret = -EINVAL;
734*4882a593Smuzhiyun 			goto out;
735*4882a593Smuzhiyun 		}
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 		ret = submit_bo(submit, submit_cmd.submit_idx,
738*4882a593Smuzhiyun 				&msm_obj, &iova, NULL);
739*4882a593Smuzhiyun 		if (ret)
740*4882a593Smuzhiyun 			goto out;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 		if (submit_cmd.size % 4) {
743*4882a593Smuzhiyun 			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
744*4882a593Smuzhiyun 					submit_cmd.size);
745*4882a593Smuzhiyun 			ret = -EINVAL;
746*4882a593Smuzhiyun 			goto out;
747*4882a593Smuzhiyun 		}
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun 		if (!submit_cmd.size ||
750*4882a593Smuzhiyun 			((submit_cmd.size + submit_cmd.submit_offset) >
751*4882a593Smuzhiyun 				msm_obj->base.size)) {
752*4882a593Smuzhiyun 			DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
753*4882a593Smuzhiyun 			ret = -EINVAL;
754*4882a593Smuzhiyun 			goto out;
755*4882a593Smuzhiyun 		}
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 		submit->cmd[i].type = submit_cmd.type;
758*4882a593Smuzhiyun 		submit->cmd[i].size = submit_cmd.size / 4;
759*4882a593Smuzhiyun 		submit->cmd[i].iova = iova + submit_cmd.submit_offset;
760*4882a593Smuzhiyun 		submit->cmd[i].idx  = submit_cmd.submit_idx;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 		if (submit->valid)
763*4882a593Smuzhiyun 			continue;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 		ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
766*4882a593Smuzhiyun 				submit_cmd.nr_relocs, submit_cmd.relocs);
767*4882a593Smuzhiyun 		if (ret)
768*4882a593Smuzhiyun 			goto out;
769*4882a593Smuzhiyun 	}
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	submit->nr_cmds = i;
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 	submit->fence = msm_fence_alloc(ring->fctx);
774*4882a593Smuzhiyun 	if (IS_ERR(submit->fence)) {
775*4882a593Smuzhiyun 		ret = PTR_ERR(submit->fence);
776*4882a593Smuzhiyun 		submit->fence = NULL;
777*4882a593Smuzhiyun 		goto out;
778*4882a593Smuzhiyun 	}
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
781*4882a593Smuzhiyun 		sync_file = sync_file_create(submit->fence);
782*4882a593Smuzhiyun 		if (!sync_file) {
783*4882a593Smuzhiyun 			ret = -ENOMEM;
784*4882a593Smuzhiyun 			goto out;
785*4882a593Smuzhiyun 		}
786*4882a593Smuzhiyun 	}
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	msm_gpu_submit(gpu, submit);
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	args->fence = submit->fence->seqno;
791*4882a593Smuzhiyun 
792*4882a593Smuzhiyun 	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
793*4882a593Smuzhiyun 		fd_install(out_fence_fd, sync_file->file);
794*4882a593Smuzhiyun 		args->fence_fd = out_fence_fd;
795*4882a593Smuzhiyun 	}
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 	msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs);
798*4882a593Smuzhiyun 	msm_process_post_deps(post_deps, args->nr_out_syncobjs,
799*4882a593Smuzhiyun 	                      submit->fence);
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun out:
803*4882a593Smuzhiyun 	submit_cleanup(submit);
804*4882a593Smuzhiyun 	if (has_ww_ticket)
805*4882a593Smuzhiyun 		ww_acquire_fini(&submit->ticket);
806*4882a593Smuzhiyun 	if (ret)
807*4882a593Smuzhiyun 		msm_gem_submit_free(submit);
808*4882a593Smuzhiyun out_unlock:
809*4882a593Smuzhiyun 	if (ret && (out_fence_fd >= 0))
810*4882a593Smuzhiyun 		put_unused_fd(out_fence_fd);
811*4882a593Smuzhiyun 	mutex_unlock(&dev->struct_mutex);
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun out_post_unlock:
814*4882a593Smuzhiyun 	if (!IS_ERR_OR_NULL(post_deps)) {
815*4882a593Smuzhiyun 		for (i = 0; i < args->nr_out_syncobjs; ++i) {
816*4882a593Smuzhiyun 			kfree(post_deps[i].chain);
817*4882a593Smuzhiyun 			drm_syncobj_put(post_deps[i].syncobj);
818*4882a593Smuzhiyun 		}
819*4882a593Smuzhiyun 		kfree(post_deps);
820*4882a593Smuzhiyun 	}
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	if (!IS_ERR_OR_NULL(syncobjs_to_reset)) {
823*4882a593Smuzhiyun 		for (i = 0; i < args->nr_in_syncobjs; ++i) {
824*4882a593Smuzhiyun 			if (syncobjs_to_reset[i])
825*4882a593Smuzhiyun 				drm_syncobj_put(syncobjs_to_reset[i]);
826*4882a593Smuzhiyun 		}
827*4882a593Smuzhiyun 		kfree(syncobjs_to_reset);
828*4882a593Smuzhiyun 	}
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	return ret;
831*4882a593Smuzhiyun }
832