cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

msm_gem_submit.c (24280B)


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