xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2015 Etnaviv Project
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <drm/drm_file.h>
7*4882a593Smuzhiyun #include <linux/dma-fence-array.h>
8*4882a593Smuzhiyun #include <linux/file.h>
9*4882a593Smuzhiyun #include <linux/pm_runtime.h>
10*4882a593Smuzhiyun #include <linux/dma-resv.h>
11*4882a593Smuzhiyun #include <linux/sync_file.h>
12*4882a593Smuzhiyun #include <linux/uaccess.h>
13*4882a593Smuzhiyun #include <linux/vmalloc.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include "etnaviv_cmdbuf.h"
16*4882a593Smuzhiyun #include "etnaviv_drv.h"
17*4882a593Smuzhiyun #include "etnaviv_gpu.h"
18*4882a593Smuzhiyun #include "etnaviv_gem.h"
19*4882a593Smuzhiyun #include "etnaviv_perfmon.h"
20*4882a593Smuzhiyun #include "etnaviv_sched.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /*
23*4882a593Smuzhiyun  * Cmdstream submission:
24*4882a593Smuzhiyun  */
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
27*4882a593Smuzhiyun /* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */
28*4882a593Smuzhiyun #define BO_LOCKED   0x4000
29*4882a593Smuzhiyun #define BO_PINNED   0x2000
30*4882a593Smuzhiyun 
submit_create(struct drm_device * dev,struct etnaviv_gpu * gpu,size_t nr_bos,size_t nr_pmrs)31*4882a593Smuzhiyun static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
32*4882a593Smuzhiyun 		struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	struct etnaviv_gem_submit *submit;
35*4882a593Smuzhiyun 	size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit));
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	submit = kzalloc(sz, GFP_KERNEL);
38*4882a593Smuzhiyun 	if (!submit)
39*4882a593Smuzhiyun 		return NULL;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	submit->pmrs = kcalloc(nr_pmrs, sizeof(struct etnaviv_perfmon_request),
42*4882a593Smuzhiyun 			       GFP_KERNEL);
43*4882a593Smuzhiyun 	if (!submit->pmrs) {
44*4882a593Smuzhiyun 		kfree(submit);
45*4882a593Smuzhiyun 		return NULL;
46*4882a593Smuzhiyun 	}
47*4882a593Smuzhiyun 	submit->nr_pmrs = nr_pmrs;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	submit->gpu = gpu;
50*4882a593Smuzhiyun 	kref_init(&submit->refcount);
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	return submit;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
submit_lookup_objects(struct etnaviv_gem_submit * submit,struct drm_file * file,struct drm_etnaviv_gem_submit_bo * submit_bos,unsigned nr_bos)55*4882a593Smuzhiyun static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
56*4882a593Smuzhiyun 	struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos,
57*4882a593Smuzhiyun 	unsigned nr_bos)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	struct drm_etnaviv_gem_submit_bo *bo;
60*4882a593Smuzhiyun 	unsigned i;
61*4882a593Smuzhiyun 	int ret = 0;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	spin_lock(&file->table_lock);
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) {
66*4882a593Smuzhiyun 		struct drm_gem_object *obj;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 		if (bo->flags & BO_INVALID_FLAGS) {
69*4882a593Smuzhiyun 			DRM_ERROR("invalid flags: %x\n", bo->flags);
70*4882a593Smuzhiyun 			ret = -EINVAL;
71*4882a593Smuzhiyun 			goto out_unlock;
72*4882a593Smuzhiyun 		}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 		submit->bos[i].flags = bo->flags;
75*4882a593Smuzhiyun 		if (submit->flags & ETNA_SUBMIT_SOFTPIN) {
76*4882a593Smuzhiyun 			if (bo->presumed < ETNAVIV_SOFTPIN_START_ADDRESS) {
77*4882a593Smuzhiyun 				DRM_ERROR("invalid softpin address\n");
78*4882a593Smuzhiyun 				ret = -EINVAL;
79*4882a593Smuzhiyun 				goto out_unlock;
80*4882a593Smuzhiyun 			}
81*4882a593Smuzhiyun 			submit->bos[i].va = bo->presumed;
82*4882a593Smuzhiyun 		}
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 		/* normally use drm_gem_object_lookup(), but for bulk lookup
85*4882a593Smuzhiyun 		 * all under single table_lock just hit object_idr directly:
86*4882a593Smuzhiyun 		 */
87*4882a593Smuzhiyun 		obj = idr_find(&file->object_idr, bo->handle);
88*4882a593Smuzhiyun 		if (!obj) {
89*4882a593Smuzhiyun 			DRM_ERROR("invalid handle %u at index %u\n",
90*4882a593Smuzhiyun 				  bo->handle, i);
91*4882a593Smuzhiyun 			ret = -EINVAL;
92*4882a593Smuzhiyun 			goto out_unlock;
93*4882a593Smuzhiyun 		}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		/*
96*4882a593Smuzhiyun 		 * Take a refcount on the object. The file table lock
97*4882a593Smuzhiyun 		 * prevents the object_idr's refcount on this being dropped.
98*4882a593Smuzhiyun 		 */
99*4882a593Smuzhiyun 		drm_gem_object_get(obj);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 		submit->bos[i].obj = to_etnaviv_bo(obj);
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun out_unlock:
105*4882a593Smuzhiyun 	submit->nr_bos = i;
106*4882a593Smuzhiyun 	spin_unlock(&file->table_lock);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return ret;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
submit_unlock_object(struct etnaviv_gem_submit * submit,int i)111*4882a593Smuzhiyun static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	if (submit->bos[i].flags & BO_LOCKED) {
114*4882a593Smuzhiyun 		struct drm_gem_object *obj = &submit->bos[i].obj->base;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 		dma_resv_unlock(obj->resv);
117*4882a593Smuzhiyun 		submit->bos[i].flags &= ~BO_LOCKED;
118*4882a593Smuzhiyun 	}
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
submit_lock_objects(struct etnaviv_gem_submit * submit,struct ww_acquire_ctx * ticket)121*4882a593Smuzhiyun static int submit_lock_objects(struct etnaviv_gem_submit *submit,
122*4882a593Smuzhiyun 		struct ww_acquire_ctx *ticket)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	int contended, slow_locked = -1, i, ret = 0;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun retry:
127*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
128*4882a593Smuzhiyun 		struct drm_gem_object *obj = &submit->bos[i].obj->base;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 		if (slow_locked == i)
131*4882a593Smuzhiyun 			slow_locked = -1;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 		contended = i;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 		if (!(submit->bos[i].flags & BO_LOCKED)) {
136*4882a593Smuzhiyun 			ret = dma_resv_lock_interruptible(obj->resv, ticket);
137*4882a593Smuzhiyun 			if (ret == -EALREADY)
138*4882a593Smuzhiyun 				DRM_ERROR("BO at index %u already on submit list\n",
139*4882a593Smuzhiyun 					  i);
140*4882a593Smuzhiyun 			if (ret)
141*4882a593Smuzhiyun 				goto fail;
142*4882a593Smuzhiyun 			submit->bos[i].flags |= BO_LOCKED;
143*4882a593Smuzhiyun 		}
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	ww_acquire_done(ticket);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return 0;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun fail:
151*4882a593Smuzhiyun 	for (; i >= 0; i--)
152*4882a593Smuzhiyun 		submit_unlock_object(submit, i);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	if (slow_locked > 0)
155*4882a593Smuzhiyun 		submit_unlock_object(submit, slow_locked);
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	if (ret == -EDEADLK) {
158*4882a593Smuzhiyun 		struct drm_gem_object *obj;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 		obj = &submit->bos[contended].obj->base;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 		/* we lost out in a seqno race, lock and retry.. */
163*4882a593Smuzhiyun 		ret = dma_resv_lock_slow_interruptible(obj->resv, ticket);
164*4882a593Smuzhiyun 		if (!ret) {
165*4882a593Smuzhiyun 			submit->bos[contended].flags |= BO_LOCKED;
166*4882a593Smuzhiyun 			slow_locked = contended;
167*4882a593Smuzhiyun 			goto retry;
168*4882a593Smuzhiyun 		}
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	return ret;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
submit_fence_sync(struct etnaviv_gem_submit * submit)174*4882a593Smuzhiyun static int submit_fence_sync(struct etnaviv_gem_submit *submit)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	int i, ret = 0;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
179*4882a593Smuzhiyun 		struct etnaviv_gem_submit_bo *bo = &submit->bos[i];
180*4882a593Smuzhiyun 		struct dma_resv *robj = bo->obj->base.resv;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) {
183*4882a593Smuzhiyun 			ret = dma_resv_reserve_shared(robj, 1);
184*4882a593Smuzhiyun 			if (ret)
185*4882a593Smuzhiyun 				return ret;
186*4882a593Smuzhiyun 		}
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		if (submit->flags & ETNA_SUBMIT_NO_IMPLICIT)
189*4882a593Smuzhiyun 			continue;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 		if (bo->flags & ETNA_SUBMIT_BO_WRITE) {
192*4882a593Smuzhiyun 			ret = dma_resv_get_fences_rcu(robj, &bo->excl,
193*4882a593Smuzhiyun 								&bo->nr_shared,
194*4882a593Smuzhiyun 								&bo->shared);
195*4882a593Smuzhiyun 			if (ret)
196*4882a593Smuzhiyun 				return ret;
197*4882a593Smuzhiyun 		} else {
198*4882a593Smuzhiyun 			bo->excl = dma_resv_get_excl_rcu(robj);
199*4882a593Smuzhiyun 		}
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	return ret;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
submit_attach_object_fences(struct etnaviv_gem_submit * submit)206*4882a593Smuzhiyun static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	int i;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
211*4882a593Smuzhiyun 		struct drm_gem_object *obj = &submit->bos[i].obj->base;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 		if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
214*4882a593Smuzhiyun 			dma_resv_add_excl_fence(obj->resv,
215*4882a593Smuzhiyun 							  submit->out_fence);
216*4882a593Smuzhiyun 		else
217*4882a593Smuzhiyun 			dma_resv_add_shared_fence(obj->resv,
218*4882a593Smuzhiyun 							    submit->out_fence);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 		submit_unlock_object(submit, i);
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
submit_pin_objects(struct etnaviv_gem_submit * submit)224*4882a593Smuzhiyun static int submit_pin_objects(struct etnaviv_gem_submit *submit)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	int i, ret = 0;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
229*4882a593Smuzhiyun 		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
230*4882a593Smuzhiyun 		struct etnaviv_vram_mapping *mapping;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 		mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base,
233*4882a593Smuzhiyun 						  submit->mmu_context,
234*4882a593Smuzhiyun 						  submit->bos[i].va);
235*4882a593Smuzhiyun 		if (IS_ERR(mapping)) {
236*4882a593Smuzhiyun 			ret = PTR_ERR(mapping);
237*4882a593Smuzhiyun 			break;
238*4882a593Smuzhiyun 		}
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 		if ((submit->flags & ETNA_SUBMIT_SOFTPIN) &&
241*4882a593Smuzhiyun 		     submit->bos[i].va != mapping->iova) {
242*4882a593Smuzhiyun 			etnaviv_gem_mapping_unreference(mapping);
243*4882a593Smuzhiyun 			return -EINVAL;
244*4882a593Smuzhiyun 		}
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 		atomic_inc(&etnaviv_obj->gpu_active);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		submit->bos[i].flags |= BO_PINNED;
249*4882a593Smuzhiyun 		submit->bos[i].mapping = mapping;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	return ret;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
submit_bo(struct etnaviv_gem_submit * submit,u32 idx,struct etnaviv_gem_submit_bo ** bo)255*4882a593Smuzhiyun static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx,
256*4882a593Smuzhiyun 	struct etnaviv_gem_submit_bo **bo)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	if (idx >= submit->nr_bos) {
259*4882a593Smuzhiyun 		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
260*4882a593Smuzhiyun 				idx, submit->nr_bos);
261*4882a593Smuzhiyun 		return -EINVAL;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	*bo = &submit->bos[idx];
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun /* process the reloc's and patch up the cmdstream as needed: */
submit_reloc(struct etnaviv_gem_submit * submit,void * stream,u32 size,const struct drm_etnaviv_gem_submit_reloc * relocs,u32 nr_relocs)270*4882a593Smuzhiyun static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
271*4882a593Smuzhiyun 		u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs,
272*4882a593Smuzhiyun 		u32 nr_relocs)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun 	u32 i, last_offset = 0;
275*4882a593Smuzhiyun 	u32 *ptr = stream;
276*4882a593Smuzhiyun 	int ret;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	/* Submits using softpin don't blend with relocs */
279*4882a593Smuzhiyun 	if ((submit->flags & ETNA_SUBMIT_SOFTPIN) && nr_relocs != 0)
280*4882a593Smuzhiyun 		return -EINVAL;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	for (i = 0; i < nr_relocs; i++) {
283*4882a593Smuzhiyun 		const struct drm_etnaviv_gem_submit_reloc *r = relocs + i;
284*4882a593Smuzhiyun 		struct etnaviv_gem_submit_bo *bo;
285*4882a593Smuzhiyun 		u32 off;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		if (unlikely(r->flags)) {
288*4882a593Smuzhiyun 			DRM_ERROR("invalid reloc flags\n");
289*4882a593Smuzhiyun 			return -EINVAL;
290*4882a593Smuzhiyun 		}
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 		if (r->submit_offset % 4) {
293*4882a593Smuzhiyun 			DRM_ERROR("non-aligned reloc offset: %u\n",
294*4882a593Smuzhiyun 				  r->submit_offset);
295*4882a593Smuzhiyun 			return -EINVAL;
296*4882a593Smuzhiyun 		}
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 		/* offset in dwords: */
299*4882a593Smuzhiyun 		off = r->submit_offset / 4;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 		if ((off >= size ) ||
302*4882a593Smuzhiyun 				(off < last_offset)) {
303*4882a593Smuzhiyun 			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
304*4882a593Smuzhiyun 			return -EINVAL;
305*4882a593Smuzhiyun 		}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 		ret = submit_bo(submit, r->reloc_idx, &bo);
308*4882a593Smuzhiyun 		if (ret)
309*4882a593Smuzhiyun 			return ret;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 		if (r->reloc_offset > bo->obj->base.size - sizeof(*ptr)) {
312*4882a593Smuzhiyun 			DRM_ERROR("relocation %u outside object\n", i);
313*4882a593Smuzhiyun 			return -EINVAL;
314*4882a593Smuzhiyun 		}
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 		ptr[off] = bo->mapping->iova + r->reloc_offset;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 		last_offset = off;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	return 0;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
submit_perfmon_validate(struct etnaviv_gem_submit * submit,u32 exec_state,const struct drm_etnaviv_gem_submit_pmr * pmrs)324*4882a593Smuzhiyun static int submit_perfmon_validate(struct etnaviv_gem_submit *submit,
325*4882a593Smuzhiyun 		u32 exec_state, const struct drm_etnaviv_gem_submit_pmr *pmrs)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	u32 i;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_pmrs; i++) {
330*4882a593Smuzhiyun 		const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i;
331*4882a593Smuzhiyun 		struct etnaviv_gem_submit_bo *bo;
332*4882a593Smuzhiyun 		int ret;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 		ret = submit_bo(submit, r->read_idx, &bo);
335*4882a593Smuzhiyun 		if (ret)
336*4882a593Smuzhiyun 			return ret;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 		/* at offset 0 a sequence number gets stored used for userspace sync */
339*4882a593Smuzhiyun 		if (r->read_offset == 0) {
340*4882a593Smuzhiyun 			DRM_ERROR("perfmon request: offset is 0");
341*4882a593Smuzhiyun 			return -EINVAL;
342*4882a593Smuzhiyun 		}
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 		if (r->read_offset >= bo->obj->base.size - sizeof(u32)) {
345*4882a593Smuzhiyun 			DRM_ERROR("perfmon request: offset %u outside object", i);
346*4882a593Smuzhiyun 			return -EINVAL;
347*4882a593Smuzhiyun 		}
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 		if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) {
350*4882a593Smuzhiyun 			DRM_ERROR("perfmon request: flags are not valid");
351*4882a593Smuzhiyun 			return -EINVAL;
352*4882a593Smuzhiyun 		}
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 		if (etnaviv_pm_req_validate(r, exec_state)) {
355*4882a593Smuzhiyun 			DRM_ERROR("perfmon request: domain or signal not valid");
356*4882a593Smuzhiyun 			return -EINVAL;
357*4882a593Smuzhiyun 		}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 		submit->pmrs[i].flags = r->flags;
360*4882a593Smuzhiyun 		submit->pmrs[i].domain = r->domain;
361*4882a593Smuzhiyun 		submit->pmrs[i].signal = r->signal;
362*4882a593Smuzhiyun 		submit->pmrs[i].sequence = r->sequence;
363*4882a593Smuzhiyun 		submit->pmrs[i].offset = r->read_offset;
364*4882a593Smuzhiyun 		submit->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base);
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	return 0;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
submit_cleanup(struct kref * kref)370*4882a593Smuzhiyun static void submit_cleanup(struct kref *kref)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct etnaviv_gem_submit *submit =
373*4882a593Smuzhiyun 			container_of(kref, struct etnaviv_gem_submit, refcount);
374*4882a593Smuzhiyun 	unsigned i;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	if (submit->runtime_resumed)
377*4882a593Smuzhiyun 		pm_runtime_put_autosuspend(submit->gpu->dev);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (submit->cmdbuf.suballoc)
380*4882a593Smuzhiyun 		etnaviv_cmdbuf_free(&submit->cmdbuf);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	if (submit->mmu_context)
383*4882a593Smuzhiyun 		etnaviv_iommu_context_put(submit->mmu_context);
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (submit->prev_mmu_context)
386*4882a593Smuzhiyun 		etnaviv_iommu_context_put(submit->prev_mmu_context);
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	for (i = 0; i < submit->nr_bos; i++) {
389*4882a593Smuzhiyun 		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 		/* unpin all objects */
392*4882a593Smuzhiyun 		if (submit->bos[i].flags & BO_PINNED) {
393*4882a593Smuzhiyun 			etnaviv_gem_mapping_unreference(submit->bos[i].mapping);
394*4882a593Smuzhiyun 			atomic_dec(&etnaviv_obj->gpu_active);
395*4882a593Smuzhiyun 			submit->bos[i].mapping = NULL;
396*4882a593Smuzhiyun 			submit->bos[i].flags &= ~BO_PINNED;
397*4882a593Smuzhiyun 		}
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 		/* if the GPU submit failed, objects might still be locked */
400*4882a593Smuzhiyun 		submit_unlock_object(submit, i);
401*4882a593Smuzhiyun 		drm_gem_object_put(&etnaviv_obj->base);
402*4882a593Smuzhiyun 	}
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	wake_up_all(&submit->gpu->fence_event);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	if (submit->in_fence)
407*4882a593Smuzhiyun 		dma_fence_put(submit->in_fence);
408*4882a593Smuzhiyun 	if (submit->out_fence) {
409*4882a593Smuzhiyun 		/* first remove from IDR, so fence can not be found anymore */
410*4882a593Smuzhiyun 		mutex_lock(&submit->gpu->fence_lock);
411*4882a593Smuzhiyun 		idr_remove(&submit->gpu->fence_idr, submit->out_fence_id);
412*4882a593Smuzhiyun 		mutex_unlock(&submit->gpu->fence_lock);
413*4882a593Smuzhiyun 		dma_fence_put(submit->out_fence);
414*4882a593Smuzhiyun 	}
415*4882a593Smuzhiyun 	kfree(submit->pmrs);
416*4882a593Smuzhiyun 	kfree(submit);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun 
etnaviv_submit_put(struct etnaviv_gem_submit * submit)419*4882a593Smuzhiyun void etnaviv_submit_put(struct etnaviv_gem_submit *submit)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	kref_put(&submit->refcount, submit_cleanup);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
etnaviv_ioctl_gem_submit(struct drm_device * dev,void * data,struct drm_file * file)424*4882a593Smuzhiyun int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
425*4882a593Smuzhiyun 		struct drm_file *file)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	struct etnaviv_file_private *ctx = file->driver_priv;
428*4882a593Smuzhiyun 	struct etnaviv_drm_private *priv = dev->dev_private;
429*4882a593Smuzhiyun 	struct drm_etnaviv_gem_submit *args = data;
430*4882a593Smuzhiyun 	struct drm_etnaviv_gem_submit_reloc *relocs;
431*4882a593Smuzhiyun 	struct drm_etnaviv_gem_submit_pmr *pmrs;
432*4882a593Smuzhiyun 	struct drm_etnaviv_gem_submit_bo *bos;
433*4882a593Smuzhiyun 	struct etnaviv_gem_submit *submit;
434*4882a593Smuzhiyun 	struct etnaviv_gpu *gpu;
435*4882a593Smuzhiyun 	struct sync_file *sync_file = NULL;
436*4882a593Smuzhiyun 	struct ww_acquire_ctx ticket;
437*4882a593Smuzhiyun 	int out_fence_fd = -1;
438*4882a593Smuzhiyun 	void *stream;
439*4882a593Smuzhiyun 	int ret;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	if (args->pipe >= ETNA_MAX_PIPES)
442*4882a593Smuzhiyun 		return -EINVAL;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	gpu = priv->gpu[args->pipe];
445*4882a593Smuzhiyun 	if (!gpu)
446*4882a593Smuzhiyun 		return -ENXIO;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	if (args->stream_size % 4) {
449*4882a593Smuzhiyun 		DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
450*4882a593Smuzhiyun 			  args->stream_size);
451*4882a593Smuzhiyun 		return -EINVAL;
452*4882a593Smuzhiyun 	}
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	if (args->exec_state != ETNA_PIPE_3D &&
455*4882a593Smuzhiyun 	    args->exec_state != ETNA_PIPE_2D &&
456*4882a593Smuzhiyun 	    args->exec_state != ETNA_PIPE_VG) {
457*4882a593Smuzhiyun 		DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state);
458*4882a593Smuzhiyun 		return -EINVAL;
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	if (args->flags & ~ETNA_SUBMIT_FLAGS) {
462*4882a593Smuzhiyun 		DRM_ERROR("invalid flags: 0x%x\n", args->flags);
463*4882a593Smuzhiyun 		return -EINVAL;
464*4882a593Smuzhiyun 	}
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	if ((args->flags & ETNA_SUBMIT_SOFTPIN) &&
467*4882a593Smuzhiyun 	    priv->mmu_global->version != ETNAVIV_IOMMU_V2) {
468*4882a593Smuzhiyun 		DRM_ERROR("softpin requested on incompatible MMU\n");
469*4882a593Smuzhiyun 		return -EINVAL;
470*4882a593Smuzhiyun 	}
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	if (args->stream_size > SZ_128K || args->nr_relocs > SZ_128K ||
473*4882a593Smuzhiyun 	    args->nr_bos > SZ_128K || args->nr_pmrs > 128) {
474*4882a593Smuzhiyun 		DRM_ERROR("submit arguments out of size limits\n");
475*4882a593Smuzhiyun 		return -EINVAL;
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	/*
479*4882a593Smuzhiyun 	 * Copy the command submission and bo array to kernel space in
480*4882a593Smuzhiyun 	 * one go, and do this outside of any locks.
481*4882a593Smuzhiyun 	 */
482*4882a593Smuzhiyun 	bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
483*4882a593Smuzhiyun 	relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
484*4882a593Smuzhiyun 	pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL);
485*4882a593Smuzhiyun 	stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
486*4882a593Smuzhiyun 	if (!bos || !relocs || !pmrs || !stream) {
487*4882a593Smuzhiyun 		ret = -ENOMEM;
488*4882a593Smuzhiyun 		goto err_submit_cmds;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	ret = copy_from_user(bos, u64_to_user_ptr(args->bos),
492*4882a593Smuzhiyun 			     args->nr_bos * sizeof(*bos));
493*4882a593Smuzhiyun 	if (ret) {
494*4882a593Smuzhiyun 		ret = -EFAULT;
495*4882a593Smuzhiyun 		goto err_submit_cmds;
496*4882a593Smuzhiyun 	}
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs),
499*4882a593Smuzhiyun 			     args->nr_relocs * sizeof(*relocs));
500*4882a593Smuzhiyun 	if (ret) {
501*4882a593Smuzhiyun 		ret = -EFAULT;
502*4882a593Smuzhiyun 		goto err_submit_cmds;
503*4882a593Smuzhiyun 	}
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs),
506*4882a593Smuzhiyun 			     args->nr_pmrs * sizeof(*pmrs));
507*4882a593Smuzhiyun 	if (ret) {
508*4882a593Smuzhiyun 		ret = -EFAULT;
509*4882a593Smuzhiyun 		goto err_submit_cmds;
510*4882a593Smuzhiyun 	}
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
513*4882a593Smuzhiyun 			     args->stream_size);
514*4882a593Smuzhiyun 	if (ret) {
515*4882a593Smuzhiyun 		ret = -EFAULT;
516*4882a593Smuzhiyun 		goto err_submit_cmds;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
520*4882a593Smuzhiyun 		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
521*4882a593Smuzhiyun 		if (out_fence_fd < 0) {
522*4882a593Smuzhiyun 			ret = out_fence_fd;
523*4882a593Smuzhiyun 			goto err_submit_cmds;
524*4882a593Smuzhiyun 		}
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	ww_acquire_init(&ticket, &reservation_ww_class);
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	submit = submit_create(dev, gpu, args->nr_bos, args->nr_pmrs);
530*4882a593Smuzhiyun 	if (!submit) {
531*4882a593Smuzhiyun 		ret = -ENOMEM;
532*4882a593Smuzhiyun 		goto err_submit_ww_acquire;
533*4882a593Smuzhiyun 	}
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &submit->cmdbuf,
536*4882a593Smuzhiyun 				  ALIGN(args->stream_size, 8) + 8);
537*4882a593Smuzhiyun 	if (ret)
538*4882a593Smuzhiyun 		goto err_submit_objects;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	submit->ctx = file->driver_priv;
541*4882a593Smuzhiyun 	submit->mmu_context = etnaviv_iommu_context_get(submit->ctx->mmu);
542*4882a593Smuzhiyun 	submit->exec_state = args->exec_state;
543*4882a593Smuzhiyun 	submit->flags = args->flags;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	ret = submit_lookup_objects(submit, file, bos, args->nr_bos);
546*4882a593Smuzhiyun 	if (ret)
547*4882a593Smuzhiyun 		goto err_submit_objects;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	if ((priv->mmu_global->version != ETNAVIV_IOMMU_V2) &&
550*4882a593Smuzhiyun 	    !etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4,
551*4882a593Smuzhiyun 				      relocs, args->nr_relocs)) {
552*4882a593Smuzhiyun 		ret = -EINVAL;
553*4882a593Smuzhiyun 		goto err_submit_objects;
554*4882a593Smuzhiyun 	}
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	if (args->flags & ETNA_SUBMIT_FENCE_FD_IN) {
557*4882a593Smuzhiyun 		submit->in_fence = sync_file_get_fence(args->fence_fd);
558*4882a593Smuzhiyun 		if (!submit->in_fence) {
559*4882a593Smuzhiyun 			ret = -EINVAL;
560*4882a593Smuzhiyun 			goto err_submit_objects;
561*4882a593Smuzhiyun 		}
562*4882a593Smuzhiyun 	}
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	ret = submit_pin_objects(submit);
565*4882a593Smuzhiyun 	if (ret)
566*4882a593Smuzhiyun 		goto err_submit_objects;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	ret = submit_reloc(submit, stream, args->stream_size / 4,
569*4882a593Smuzhiyun 			   relocs, args->nr_relocs);
570*4882a593Smuzhiyun 	if (ret)
571*4882a593Smuzhiyun 		goto err_submit_objects;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	ret = submit_perfmon_validate(submit, args->exec_state, pmrs);
574*4882a593Smuzhiyun 	if (ret)
575*4882a593Smuzhiyun 		goto err_submit_objects;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	memcpy(submit->cmdbuf.vaddr, stream, args->stream_size);
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	ret = submit_lock_objects(submit, &ticket);
580*4882a593Smuzhiyun 	if (ret)
581*4882a593Smuzhiyun 		goto err_submit_objects;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	ret = submit_fence_sync(submit);
584*4882a593Smuzhiyun 	if (ret)
585*4882a593Smuzhiyun 		goto err_submit_objects;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	ret = etnaviv_sched_push_job(&ctx->sched_entity[args->pipe], submit);
588*4882a593Smuzhiyun 	if (ret)
589*4882a593Smuzhiyun 		goto err_submit_objects;
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	submit_attach_object_fences(submit);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
594*4882a593Smuzhiyun 		/*
595*4882a593Smuzhiyun 		 * This can be improved: ideally we want to allocate the sync
596*4882a593Smuzhiyun 		 * file before kicking off the GPU job and just attach the
597*4882a593Smuzhiyun 		 * fence to the sync file here, eliminating the ENOMEM
598*4882a593Smuzhiyun 		 * possibility at this stage.
599*4882a593Smuzhiyun 		 */
600*4882a593Smuzhiyun 		sync_file = sync_file_create(submit->out_fence);
601*4882a593Smuzhiyun 		if (!sync_file) {
602*4882a593Smuzhiyun 			ret = -ENOMEM;
603*4882a593Smuzhiyun 			goto err_submit_objects;
604*4882a593Smuzhiyun 		}
605*4882a593Smuzhiyun 		fd_install(out_fence_fd, sync_file->file);
606*4882a593Smuzhiyun 	}
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	args->fence_fd = out_fence_fd;
609*4882a593Smuzhiyun 	args->fence = submit->out_fence_id;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun err_submit_objects:
612*4882a593Smuzhiyun 	etnaviv_submit_put(submit);
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun err_submit_ww_acquire:
615*4882a593Smuzhiyun 	ww_acquire_fini(&ticket);
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun err_submit_cmds:
618*4882a593Smuzhiyun 	if (ret && (out_fence_fd >= 0))
619*4882a593Smuzhiyun 		put_unused_fd(out_fence_fd);
620*4882a593Smuzhiyun 	if (stream)
621*4882a593Smuzhiyun 		kvfree(stream);
622*4882a593Smuzhiyun 	if (bos)
623*4882a593Smuzhiyun 		kvfree(bos);
624*4882a593Smuzhiyun 	if (relocs)
625*4882a593Smuzhiyun 		kvfree(relocs);
626*4882a593Smuzhiyun 	if (pmrs)
627*4882a593Smuzhiyun 		kvfree(pmrs);
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	return ret;
630*4882a593Smuzhiyun }
631