162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat
462306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/file.h>
862306a36Sopenharmony_ci#include <linux/sync_file.h>
962306a36Sopenharmony_ci#include <linux/uaccess.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <drm/drm_drv.h>
1262306a36Sopenharmony_ci#include <drm/drm_file.h>
1362306a36Sopenharmony_ci#include <drm/drm_syncobj.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "msm_drv.h"
1662306a36Sopenharmony_ci#include "msm_gpu.h"
1762306a36Sopenharmony_ci#include "msm_gem.h"
1862306a36Sopenharmony_ci#include "msm_gpu_trace.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * Cmdstream submission:
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct msm_gem_submit *submit_create(struct drm_device *dev,
2562306a36Sopenharmony_ci		struct msm_gpu *gpu,
2662306a36Sopenharmony_ci		struct msm_gpu_submitqueue *queue, uint32_t nr_bos,
2762306a36Sopenharmony_ci		uint32_t nr_cmds)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	static atomic_t ident = ATOMIC_INIT(0);
3062306a36Sopenharmony_ci	struct msm_gem_submit *submit;
3162306a36Sopenharmony_ci	uint64_t sz;
3262306a36Sopenharmony_ci	int ret;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	sz = struct_size(submit, bos, nr_bos) +
3562306a36Sopenharmony_ci			((u64)nr_cmds * sizeof(submit->cmd[0]));
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (sz > SIZE_MAX)
3862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	submit = kzalloc(sz, GFP_KERNEL);
4162306a36Sopenharmony_ci	if (!submit)
4262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	submit->hw_fence = msm_fence_alloc();
4562306a36Sopenharmony_ci	if (IS_ERR(submit->hw_fence)) {
4662306a36Sopenharmony_ci		ret = PTR_ERR(submit->hw_fence);
4762306a36Sopenharmony_ci		kfree(submit);
4862306a36Sopenharmony_ci		return ERR_PTR(ret);
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	ret = drm_sched_job_init(&submit->base, queue->entity, queue);
5262306a36Sopenharmony_ci	if (ret) {
5362306a36Sopenharmony_ci		kfree(submit->hw_fence);
5462306a36Sopenharmony_ci		kfree(submit);
5562306a36Sopenharmony_ci		return ERR_PTR(ret);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	kref_init(&submit->ref);
5962306a36Sopenharmony_ci	submit->dev = dev;
6062306a36Sopenharmony_ci	submit->aspace = queue->ctx->aspace;
6162306a36Sopenharmony_ci	submit->gpu = gpu;
6262306a36Sopenharmony_ci	submit->cmd = (void *)&submit->bos[nr_bos];
6362306a36Sopenharmony_ci	submit->queue = queue;
6462306a36Sopenharmony_ci	submit->pid = get_pid(task_pid(current));
6562306a36Sopenharmony_ci	submit->ring = gpu->rb[queue->ring_nr];
6662306a36Sopenharmony_ci	submit->fault_dumped = false;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Get a unique identifier for the submission for logging purposes */
6962306a36Sopenharmony_ci	submit->ident = atomic_inc_return(&ident) - 1;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	INIT_LIST_HEAD(&submit->node);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return submit;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid __msm_gem_submit_destroy(struct kref *kref)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct msm_gem_submit *submit =
7962306a36Sopenharmony_ci			container_of(kref, struct msm_gem_submit, ref);
8062306a36Sopenharmony_ci	unsigned i;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (submit->fence_id) {
8362306a36Sopenharmony_ci		spin_lock(&submit->queue->idr_lock);
8462306a36Sopenharmony_ci		idr_remove(&submit->queue->fence_idr, submit->fence_id);
8562306a36Sopenharmony_ci		spin_unlock(&submit->queue->idr_lock);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	dma_fence_put(submit->user_fence);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/*
9162306a36Sopenharmony_ci	 * If the submit is freed before msm_job_run(), then hw_fence is
9262306a36Sopenharmony_ci	 * just some pre-allocated memory, not a reference counted fence.
9362306a36Sopenharmony_ci	 * Once the job runs and the hw_fence is initialized, it will
9462306a36Sopenharmony_ci	 * have a refcount of at least one, since the submit holds a ref
9562306a36Sopenharmony_ci	 * to the hw_fence.
9662306a36Sopenharmony_ci	 */
9762306a36Sopenharmony_ci	if (kref_read(&submit->hw_fence->refcount) == 0) {
9862306a36Sopenharmony_ci		kfree(submit->hw_fence);
9962306a36Sopenharmony_ci	} else {
10062306a36Sopenharmony_ci		dma_fence_put(submit->hw_fence);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	put_pid(submit->pid);
10462306a36Sopenharmony_ci	msm_submitqueue_put(submit->queue);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (i = 0; i < submit->nr_cmds; i++)
10762306a36Sopenharmony_ci		kfree(submit->cmd[i].relocs);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	kfree(submit);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int submit_lookup_objects(struct msm_gem_submit *submit,
11362306a36Sopenharmony_ci		struct drm_msm_gem_submit *args, struct drm_file *file)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned i;
11662306a36Sopenharmony_ci	int ret = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	for (i = 0; i < args->nr_bos; i++) {
11962306a36Sopenharmony_ci		struct drm_msm_gem_submit_bo submit_bo;
12062306a36Sopenharmony_ci		void __user *userptr =
12162306a36Sopenharmony_ci			u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/* make sure we don't have garbage flags, in case we hit
12462306a36Sopenharmony_ci		 * error path before flags is initialized:
12562306a36Sopenharmony_ci		 */
12662306a36Sopenharmony_ci		submit->bos[i].flags = 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
12962306a36Sopenharmony_ci			ret = -EFAULT;
13062306a36Sopenharmony_ci			i = 0;
13162306a36Sopenharmony_ci			goto out;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* at least one of READ and/or WRITE flags should be set: */
13562306a36Sopenharmony_ci#define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
13862306a36Sopenharmony_ci			!(submit_bo.flags & MANDATORY_FLAGS)) {
13962306a36Sopenharmony_ci			DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
14062306a36Sopenharmony_ci			ret = -EINVAL;
14162306a36Sopenharmony_ci			i = 0;
14262306a36Sopenharmony_ci			goto out;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		submit->bos[i].handle = submit_bo.handle;
14662306a36Sopenharmony_ci		submit->bos[i].flags = submit_bo.flags;
14762306a36Sopenharmony_ci		/* in validate_objects() we figure out if this is true: */
14862306a36Sopenharmony_ci		submit->bos[i].iova  = submit_bo.presumed;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	spin_lock(&file->table_lock);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	for (i = 0; i < args->nr_bos; i++) {
15462306a36Sopenharmony_ci		struct drm_gem_object *obj;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/* normally use drm_gem_object_lookup(), but for bulk lookup
15762306a36Sopenharmony_ci		 * all under single table_lock just hit object_idr directly:
15862306a36Sopenharmony_ci		 */
15962306a36Sopenharmony_ci		obj = idr_find(&file->object_idr, submit->bos[i].handle);
16062306a36Sopenharmony_ci		if (!obj) {
16162306a36Sopenharmony_ci			DRM_ERROR("invalid handle %u at index %u\n", submit->bos[i].handle, i);
16262306a36Sopenharmony_ci			ret = -EINVAL;
16362306a36Sopenharmony_ci			goto out_unlock;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		drm_gem_object_get(obj);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		submit->bos[i].obj = obj;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciout_unlock:
17262306a36Sopenharmony_ci	spin_unlock(&file->table_lock);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciout:
17562306a36Sopenharmony_ci	submit->nr_bos = i;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return ret;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int submit_lookup_cmds(struct msm_gem_submit *submit,
18162306a36Sopenharmony_ci		struct drm_msm_gem_submit *args, struct drm_file *file)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	unsigned i;
18462306a36Sopenharmony_ci	size_t sz;
18562306a36Sopenharmony_ci	int ret = 0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	for (i = 0; i < args->nr_cmds; i++) {
18862306a36Sopenharmony_ci		struct drm_msm_gem_submit_cmd submit_cmd;
18962306a36Sopenharmony_ci		void __user *userptr =
19062306a36Sopenharmony_ci			u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
19362306a36Sopenharmony_ci		if (ret) {
19462306a36Sopenharmony_ci			ret = -EFAULT;
19562306a36Sopenharmony_ci			goto out;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		/* validate input from userspace: */
19962306a36Sopenharmony_ci		switch (submit_cmd.type) {
20062306a36Sopenharmony_ci		case MSM_SUBMIT_CMD_BUF:
20162306a36Sopenharmony_ci		case MSM_SUBMIT_CMD_IB_TARGET_BUF:
20262306a36Sopenharmony_ci		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
20362306a36Sopenharmony_ci			break;
20462306a36Sopenharmony_ci		default:
20562306a36Sopenharmony_ci			DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
20662306a36Sopenharmony_ci			return -EINVAL;
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		if (submit_cmd.size % 4) {
21062306a36Sopenharmony_ci			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
21162306a36Sopenharmony_ci					submit_cmd.size);
21262306a36Sopenharmony_ci			ret = -EINVAL;
21362306a36Sopenharmony_ci			goto out;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		submit->cmd[i].type = submit_cmd.type;
21762306a36Sopenharmony_ci		submit->cmd[i].size = submit_cmd.size / 4;
21862306a36Sopenharmony_ci		submit->cmd[i].offset = submit_cmd.submit_offset / 4;
21962306a36Sopenharmony_ci		submit->cmd[i].idx  = submit_cmd.submit_idx;
22062306a36Sopenharmony_ci		submit->cmd[i].nr_relocs = submit_cmd.nr_relocs;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		userptr = u64_to_user_ptr(submit_cmd.relocs);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		sz = array_size(submit_cmd.nr_relocs,
22562306a36Sopenharmony_ci				sizeof(struct drm_msm_gem_submit_reloc));
22662306a36Sopenharmony_ci		/* check for overflow: */
22762306a36Sopenharmony_ci		if (sz == SIZE_MAX) {
22862306a36Sopenharmony_ci			ret = -ENOMEM;
22962306a36Sopenharmony_ci			goto out;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci		submit->cmd[i].relocs = kmalloc(sz, GFP_KERNEL);
23262306a36Sopenharmony_ci		if (!submit->cmd[i].relocs) {
23362306a36Sopenharmony_ci			ret = -ENOMEM;
23462306a36Sopenharmony_ci			goto out;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		ret = copy_from_user(submit->cmd[i].relocs, userptr, sz);
23762306a36Sopenharmony_ci		if (ret) {
23862306a36Sopenharmony_ci			ret = -EFAULT;
23962306a36Sopenharmony_ci			goto out;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciout:
24462306a36Sopenharmony_ci	return ret;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* Unwind bo state, according to cleanup_flags.  In the success case, only
24862306a36Sopenharmony_ci * the lock is dropped at the end of the submit (and active/pin ref is dropped
24962306a36Sopenharmony_ci * later when the submit is retired).
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic void submit_cleanup_bo(struct msm_gem_submit *submit, int i,
25262306a36Sopenharmony_ci		unsigned cleanup_flags)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct drm_gem_object *obj = submit->bos[i].obj;
25562306a36Sopenharmony_ci	unsigned flags = submit->bos[i].flags & cleanup_flags;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * Clear flags bit before dropping lock, so that the msm_job_run()
25962306a36Sopenharmony_ci	 * path isn't racing with submit_cleanup() (ie. the read/modify/
26062306a36Sopenharmony_ci	 * write is protected by the obj lock in all paths)
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	submit->bos[i].flags &= ~cleanup_flags;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (flags & BO_PINNED)
26562306a36Sopenharmony_ci		msm_gem_unpin_locked(obj);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (flags & BO_LOCKED)
26862306a36Sopenharmony_ci		dma_resv_unlock(obj->resv);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	unsigned cleanup_flags = BO_PINNED | BO_LOCKED;
27462306a36Sopenharmony_ci	submit_cleanup_bo(submit, i, cleanup_flags);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!(submit->bos[i].flags & BO_VALID))
27762306a36Sopenharmony_ci		submit->bos[i].iova = 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* This is where we make sure all the bo's are reserved and pin'd: */
28162306a36Sopenharmony_cistatic int submit_lock_objects(struct msm_gem_submit *submit)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	int contended, slow_locked = -1, i, ret = 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ciretry:
28662306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
28762306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		if (slow_locked == i)
29062306a36Sopenharmony_ci			slow_locked = -1;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		contended = i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (!(submit->bos[i].flags & BO_LOCKED)) {
29562306a36Sopenharmony_ci			ret = dma_resv_lock_interruptible(obj->resv,
29662306a36Sopenharmony_ci							  &submit->ticket);
29762306a36Sopenharmony_ci			if (ret)
29862306a36Sopenharmony_ci				goto fail;
29962306a36Sopenharmony_ci			submit->bos[i].flags |= BO_LOCKED;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ww_acquire_done(&submit->ticket);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cifail:
30862306a36Sopenharmony_ci	if (ret == -EALREADY) {
30962306a36Sopenharmony_ci		DRM_ERROR("handle %u at index %u already on submit list\n",
31062306a36Sopenharmony_ci				submit->bos[i].handle, i);
31162306a36Sopenharmony_ci		ret = -EINVAL;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	for (; i >= 0; i--)
31562306a36Sopenharmony_ci		submit_unlock_unpin_bo(submit, i);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (slow_locked > 0)
31862306a36Sopenharmony_ci		submit_unlock_unpin_bo(submit, slow_locked);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (ret == -EDEADLK) {
32162306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[contended].obj;
32262306a36Sopenharmony_ci		/* we lost out in a seqno race, lock and retry.. */
32362306a36Sopenharmony_ci		ret = dma_resv_lock_slow_interruptible(obj->resv,
32462306a36Sopenharmony_ci						       &submit->ticket);
32562306a36Sopenharmony_ci		if (!ret) {
32662306a36Sopenharmony_ci			submit->bos[contended].flags |= BO_LOCKED;
32762306a36Sopenharmony_ci			slow_locked = contended;
32862306a36Sopenharmony_ci			goto retry;
32962306a36Sopenharmony_ci		}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		/* Not expecting -EALREADY here, if the bo was already
33262306a36Sopenharmony_ci		 * locked, we should have gotten -EALREADY already from
33362306a36Sopenharmony_ci		 * the dma_resv_lock_interruptable() call.
33462306a36Sopenharmony_ci		 */
33562306a36Sopenharmony_ci		WARN_ON_ONCE(ret == -EALREADY);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return ret;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	int i, ret = 0;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
34662306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
34762306a36Sopenharmony_ci		bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		/* NOTE: _reserve_shared() must happen before
35062306a36Sopenharmony_ci		 * _add_shared_fence(), which makes this a slightly
35162306a36Sopenharmony_ci		 * strange place to call it.  OTOH this is a
35262306a36Sopenharmony_ci		 * convenient can-fail point to hook it in.
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci		ret = dma_resv_reserve_fences(obj->resv, 1);
35562306a36Sopenharmony_ci		if (ret)
35662306a36Sopenharmony_ci			return ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		/* If userspace has determined that explicit fencing is
35962306a36Sopenharmony_ci		 * used, it can disable implicit sync on the entire
36062306a36Sopenharmony_ci		 * submit:
36162306a36Sopenharmony_ci		 */
36262306a36Sopenharmony_ci		if (no_implicit)
36362306a36Sopenharmony_ci			continue;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		/* Otherwise userspace can ask for implicit sync to be
36662306a36Sopenharmony_ci		 * disabled on specific buffers.  This is useful for internal
36762306a36Sopenharmony_ci		 * usermode driver managed buffers, suballocation, etc.
36862306a36Sopenharmony_ci		 */
36962306a36Sopenharmony_ci		if (submit->bos[i].flags & MSM_SUBMIT_BO_NO_IMPLICIT)
37062306a36Sopenharmony_ci			continue;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		ret = drm_sched_job_add_implicit_dependencies(&submit->base,
37362306a36Sopenharmony_ci							      obj,
37462306a36Sopenharmony_ci							      write);
37562306a36Sopenharmony_ci		if (ret)
37662306a36Sopenharmony_ci			break;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return ret;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic int submit_pin_objects(struct msm_gem_submit *submit)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct msm_drm_private *priv = submit->dev->dev_private;
38562306a36Sopenharmony_ci	int i, ret = 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	submit->valid = true;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
39062306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
39162306a36Sopenharmony_ci		struct msm_gem_vma *vma;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* if locking succeeded, pin bo: */
39462306a36Sopenharmony_ci		vma = msm_gem_get_vma_locked(obj, submit->aspace);
39562306a36Sopenharmony_ci		if (IS_ERR(vma)) {
39662306a36Sopenharmony_ci			ret = PTR_ERR(vma);
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		ret = msm_gem_pin_vma_locked(obj, vma);
40162306a36Sopenharmony_ci		if (ret)
40262306a36Sopenharmony_ci			break;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (vma->iova == submit->bos[i].iova) {
40562306a36Sopenharmony_ci			submit->bos[i].flags |= BO_VALID;
40662306a36Sopenharmony_ci		} else {
40762306a36Sopenharmony_ci			submit->bos[i].iova = vma->iova;
40862306a36Sopenharmony_ci			/* iova changed, so address in cmdstream is not valid: */
40962306a36Sopenharmony_ci			submit->bos[i].flags &= ~BO_VALID;
41062306a36Sopenharmony_ci			submit->valid = false;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/*
41562306a36Sopenharmony_ci	 * A second loop while holding the LRU lock (a) avoids acquiring/dropping
41662306a36Sopenharmony_ci	 * the LRU lock for each individual bo, while (b) avoiding holding the
41762306a36Sopenharmony_ci	 * LRU lock while calling msm_gem_pin_vma_locked() (which could trigger
41862306a36Sopenharmony_ci	 * get_pages() which could trigger reclaim.. and if we held the LRU lock
41962306a36Sopenharmony_ci	 * could trigger deadlock with the shrinker).
42062306a36Sopenharmony_ci	 */
42162306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
42262306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
42362306a36Sopenharmony_ci		msm_gem_pin_obj_locked(submit->bos[i].obj);
42462306a36Sopenharmony_ci		submit->bos[i].flags |= BO_PINNED;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return ret;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void submit_attach_object_fences(struct msm_gem_submit *submit)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	int i;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
43662306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
43962306a36Sopenharmony_ci			dma_resv_add_fence(obj->resv, submit->user_fence,
44062306a36Sopenharmony_ci					   DMA_RESV_USAGE_WRITE);
44162306a36Sopenharmony_ci		else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
44262306a36Sopenharmony_ci			dma_resv_add_fence(obj->resv, submit->user_fence,
44362306a36Sopenharmony_ci					   DMA_RESV_USAGE_READ);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
44862306a36Sopenharmony_ci		struct drm_gem_object **obj, uint64_t *iova, bool *valid)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	if (idx >= submit->nr_bos) {
45162306a36Sopenharmony_ci		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
45262306a36Sopenharmony_ci				idx, submit->nr_bos);
45362306a36Sopenharmony_ci		return -EINVAL;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (obj)
45762306a36Sopenharmony_ci		*obj = submit->bos[idx].obj;
45862306a36Sopenharmony_ci	if (iova)
45962306a36Sopenharmony_ci		*iova = submit->bos[idx].iova;
46062306a36Sopenharmony_ci	if (valid)
46162306a36Sopenharmony_ci		*valid = !!(submit->bos[idx].flags & BO_VALID);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/* process the reloc's and patch up the cmdstream as needed: */
46762306a36Sopenharmony_cistatic int submit_reloc(struct msm_gem_submit *submit, struct drm_gem_object *obj,
46862306a36Sopenharmony_ci		uint32_t offset, uint32_t nr_relocs, struct drm_msm_gem_submit_reloc *relocs)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	uint32_t i, last_offset = 0;
47162306a36Sopenharmony_ci	uint32_t *ptr;
47262306a36Sopenharmony_ci	int ret = 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!nr_relocs)
47562306a36Sopenharmony_ci		return 0;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (offset % 4) {
47862306a36Sopenharmony_ci		DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
47962306a36Sopenharmony_ci		return -EINVAL;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* For now, just map the entire thing.  Eventually we probably
48362306a36Sopenharmony_ci	 * to do it page-by-page, w/ kmap() if not vmap()d..
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	ptr = msm_gem_get_vaddr_locked(obj);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (IS_ERR(ptr)) {
48862306a36Sopenharmony_ci		ret = PTR_ERR(ptr);
48962306a36Sopenharmony_ci		DBG("failed to map: %d", ret);
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	for (i = 0; i < nr_relocs; i++) {
49462306a36Sopenharmony_ci		struct drm_msm_gem_submit_reloc submit_reloc = relocs[i];
49562306a36Sopenharmony_ci		uint32_t off;
49662306a36Sopenharmony_ci		uint64_t iova;
49762306a36Sopenharmony_ci		bool valid;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (submit_reloc.submit_offset % 4) {
50062306a36Sopenharmony_ci			DRM_ERROR("non-aligned reloc offset: %u\n",
50162306a36Sopenharmony_ci					submit_reloc.submit_offset);
50262306a36Sopenharmony_ci			ret = -EINVAL;
50362306a36Sopenharmony_ci			goto out;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* offset in dwords: */
50762306a36Sopenharmony_ci		off = submit_reloc.submit_offset / 4;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		if ((off >= (obj->size / 4)) ||
51062306a36Sopenharmony_ci				(off < last_offset)) {
51162306a36Sopenharmony_ci			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
51262306a36Sopenharmony_ci			ret = -EINVAL;
51362306a36Sopenharmony_ci			goto out;
51462306a36Sopenharmony_ci		}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
51762306a36Sopenharmony_ci		if (ret)
51862306a36Sopenharmony_ci			goto out;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (valid)
52162306a36Sopenharmony_ci			continue;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		iova += submit_reloc.reloc_offset;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		if (submit_reloc.shift < 0)
52662306a36Sopenharmony_ci			iova >>= -submit_reloc.shift;
52762306a36Sopenharmony_ci		else
52862306a36Sopenharmony_ci			iova <<= submit_reloc.shift;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		ptr[off] = iova | submit_reloc.or;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		last_offset = off;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciout:
53662306a36Sopenharmony_ci	msm_gem_put_vaddr_locked(obj);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return ret;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/* Cleanup submit at end of ioctl.  In the error case, this also drops
54262306a36Sopenharmony_ci * references, unpins, and drops active refcnt.  In the non-error case,
54362306a36Sopenharmony_ci * this is done when the submit is retired.
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_cistatic void submit_cleanup(struct msm_gem_submit *submit, bool error)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	unsigned cleanup_flags = BO_LOCKED;
54862306a36Sopenharmony_ci	unsigned i;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (error)
55162306a36Sopenharmony_ci		cleanup_flags |= BO_PINNED;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
55462306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
55562306a36Sopenharmony_ci		submit_cleanup_bo(submit, i, cleanup_flags);
55662306a36Sopenharmony_ci		if (error)
55762306a36Sopenharmony_ci			drm_gem_object_put(obj);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_civoid msm_submit_retire(struct msm_gem_submit *submit)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	int i;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
56662306a36Sopenharmony_ci		struct drm_gem_object *obj = submit->bos[i].obj;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		drm_gem_object_put(obj);
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistruct msm_submit_post_dep {
57362306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
57462306a36Sopenharmony_ci	uint64_t point;
57562306a36Sopenharmony_ci	struct dma_fence_chain *chain;
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit,
57962306a36Sopenharmony_ci                                           struct drm_file *file,
58062306a36Sopenharmony_ci                                           uint64_t in_syncobjs_addr,
58162306a36Sopenharmony_ci                                           uint32_t nr_in_syncobjs,
58262306a36Sopenharmony_ci                                           size_t syncobj_stride)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct drm_syncobj **syncobjs = NULL;
58562306a36Sopenharmony_ci	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
58662306a36Sopenharmony_ci	int ret = 0;
58762306a36Sopenharmony_ci	uint32_t i, j;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
59062306a36Sopenharmony_ci	                   GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
59162306a36Sopenharmony_ci	if (!syncobjs)
59262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	for (i = 0; i < nr_in_syncobjs; ++i) {
59562306a36Sopenharmony_ci		uint64_t address = in_syncobjs_addr + i * syncobj_stride;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (copy_from_user(&syncobj_desc,
59862306a36Sopenharmony_ci			           u64_to_user_ptr(address),
59962306a36Sopenharmony_ci			           min(syncobj_stride, sizeof(syncobj_desc)))) {
60062306a36Sopenharmony_ci			ret = -EFAULT;
60162306a36Sopenharmony_ci			break;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		if (syncobj_desc.point &&
60562306a36Sopenharmony_ci		    !drm_core_check_feature(submit->dev, DRIVER_SYNCOBJ_TIMELINE)) {
60662306a36Sopenharmony_ci			ret = -EOPNOTSUPP;
60762306a36Sopenharmony_ci			break;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) {
61162306a36Sopenharmony_ci			ret = -EINVAL;
61262306a36Sopenharmony_ci			break;
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		ret = drm_sched_job_add_syncobj_dependency(&submit->base, file,
61662306a36Sopenharmony_ci							   syncobj_desc.handle, syncobj_desc.point);
61762306a36Sopenharmony_ci		if (ret)
61862306a36Sopenharmony_ci			break;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) {
62162306a36Sopenharmony_ci			syncobjs[i] =
62262306a36Sopenharmony_ci				drm_syncobj_find(file, syncobj_desc.handle);
62362306a36Sopenharmony_ci			if (!syncobjs[i]) {
62462306a36Sopenharmony_ci				ret = -EINVAL;
62562306a36Sopenharmony_ci				break;
62662306a36Sopenharmony_ci			}
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (ret) {
63162306a36Sopenharmony_ci		for (j = 0; j <= i; ++j) {
63262306a36Sopenharmony_ci			if (syncobjs[j])
63362306a36Sopenharmony_ci				drm_syncobj_put(syncobjs[j]);
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci		kfree(syncobjs);
63662306a36Sopenharmony_ci		return ERR_PTR(ret);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	return syncobjs;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic void msm_reset_syncobjs(struct drm_syncobj **syncobjs,
64262306a36Sopenharmony_ci                               uint32_t nr_syncobjs)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	uint32_t i;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
64762306a36Sopenharmony_ci		if (syncobjs[i])
64862306a36Sopenharmony_ci			drm_syncobj_replace_fence(syncobjs[i], NULL);
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev,
65362306a36Sopenharmony_ci                                                       struct drm_file *file,
65462306a36Sopenharmony_ci                                                       uint64_t syncobjs_addr,
65562306a36Sopenharmony_ci                                                       uint32_t nr_syncobjs,
65662306a36Sopenharmony_ci                                                       size_t syncobj_stride)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct msm_submit_post_dep *post_deps;
65962306a36Sopenharmony_ci	struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
66062306a36Sopenharmony_ci	int ret = 0;
66162306a36Sopenharmony_ci	uint32_t i, j;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	post_deps = kcalloc(nr_syncobjs, sizeof(*post_deps),
66462306a36Sopenharmony_ci			    GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
66562306a36Sopenharmony_ci	if (!post_deps)
66662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	for (i = 0; i < nr_syncobjs; ++i) {
66962306a36Sopenharmony_ci		uint64_t address = syncobjs_addr + i * syncobj_stride;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		if (copy_from_user(&syncobj_desc,
67262306a36Sopenharmony_ci			           u64_to_user_ptr(address),
67362306a36Sopenharmony_ci			           min(syncobj_stride, sizeof(syncobj_desc)))) {
67462306a36Sopenharmony_ci			ret = -EFAULT;
67562306a36Sopenharmony_ci			break;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		post_deps[i].point = syncobj_desc.point;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		if (syncobj_desc.flags) {
68162306a36Sopenharmony_ci			ret = -EINVAL;
68262306a36Sopenharmony_ci			break;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		if (syncobj_desc.point) {
68662306a36Sopenharmony_ci			if (!drm_core_check_feature(dev,
68762306a36Sopenharmony_ci			                            DRIVER_SYNCOBJ_TIMELINE)) {
68862306a36Sopenharmony_ci				ret = -EOPNOTSUPP;
68962306a36Sopenharmony_ci				break;
69062306a36Sopenharmony_ci			}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci			post_deps[i].chain = dma_fence_chain_alloc();
69362306a36Sopenharmony_ci			if (!post_deps[i].chain) {
69462306a36Sopenharmony_ci				ret = -ENOMEM;
69562306a36Sopenharmony_ci				break;
69662306a36Sopenharmony_ci			}
69762306a36Sopenharmony_ci		}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		post_deps[i].syncobj =
70062306a36Sopenharmony_ci			drm_syncobj_find(file, syncobj_desc.handle);
70162306a36Sopenharmony_ci		if (!post_deps[i].syncobj) {
70262306a36Sopenharmony_ci			ret = -EINVAL;
70362306a36Sopenharmony_ci			break;
70462306a36Sopenharmony_ci		}
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if (ret) {
70862306a36Sopenharmony_ci		for (j = 0; j <= i; ++j) {
70962306a36Sopenharmony_ci			dma_fence_chain_free(post_deps[j].chain);
71062306a36Sopenharmony_ci			if (post_deps[j].syncobj)
71162306a36Sopenharmony_ci				drm_syncobj_put(post_deps[j].syncobj);
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		kfree(post_deps);
71562306a36Sopenharmony_ci		return ERR_PTR(ret);
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return post_deps;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic void msm_process_post_deps(struct msm_submit_post_dep *post_deps,
72262306a36Sopenharmony_ci                                  uint32_t count, struct dma_fence *fence)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	uint32_t i;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	for (i = 0; post_deps && i < count; ++i) {
72762306a36Sopenharmony_ci		if (post_deps[i].chain) {
72862306a36Sopenharmony_ci			drm_syncobj_add_point(post_deps[i].syncobj,
72962306a36Sopenharmony_ci			                      post_deps[i].chain,
73062306a36Sopenharmony_ci			                      fence, post_deps[i].point);
73162306a36Sopenharmony_ci			post_deps[i].chain = NULL;
73262306a36Sopenharmony_ci		} else {
73362306a36Sopenharmony_ci			drm_syncobj_replace_fence(post_deps[i].syncobj,
73462306a36Sopenharmony_ci			                          fence);
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ciint msm_ioctl_gem_submit(struct drm_device *dev, void *data,
74062306a36Sopenharmony_ci		struct drm_file *file)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
74362306a36Sopenharmony_ci	struct drm_msm_gem_submit *args = data;
74462306a36Sopenharmony_ci	struct msm_file_private *ctx = file->driver_priv;
74562306a36Sopenharmony_ci	struct msm_gem_submit *submit = NULL;
74662306a36Sopenharmony_ci	struct msm_gpu *gpu = priv->gpu;
74762306a36Sopenharmony_ci	struct msm_gpu_submitqueue *queue;
74862306a36Sopenharmony_ci	struct msm_ringbuffer *ring;
74962306a36Sopenharmony_ci	struct msm_submit_post_dep *post_deps = NULL;
75062306a36Sopenharmony_ci	struct drm_syncobj **syncobjs_to_reset = NULL;
75162306a36Sopenharmony_ci	int out_fence_fd = -1;
75262306a36Sopenharmony_ci	bool has_ww_ticket = false;
75362306a36Sopenharmony_ci	unsigned i;
75462306a36Sopenharmony_ci	int ret;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (!gpu)
75762306a36Sopenharmony_ci		return -ENXIO;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (args->pad)
76062306a36Sopenharmony_ci		return -EINVAL;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (unlikely(!ctx->aspace) && !capable(CAP_SYS_RAWIO)) {
76362306a36Sopenharmony_ci		DRM_ERROR_RATELIMITED("IOMMU support or CAP_SYS_RAWIO required!\n");
76462306a36Sopenharmony_ci		return -EPERM;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* for now, we just have 3d pipe.. eventually this would need to
76862306a36Sopenharmony_ci	 * be more clever to dispatch to appropriate gpu module:
76962306a36Sopenharmony_ci	 */
77062306a36Sopenharmony_ci	if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
77162306a36Sopenharmony_ci		return -EINVAL;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
77462306a36Sopenharmony_ci		return -EINVAL;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_SUDO) {
77762306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
77862306a36Sopenharmony_ci		    !capable(CAP_SYS_RAWIO))
77962306a36Sopenharmony_ci			return -EINVAL;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	queue = msm_submitqueue_get(ctx, args->queueid);
78362306a36Sopenharmony_ci	if (!queue)
78462306a36Sopenharmony_ci		return -ENOENT;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	ring = gpu->rb[queue->ring_nr];
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
78962306a36Sopenharmony_ci		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
79062306a36Sopenharmony_ci		if (out_fence_fd < 0) {
79162306a36Sopenharmony_ci			ret = out_fence_fd;
79262306a36Sopenharmony_ci			goto out_post_unlock;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds);
79762306a36Sopenharmony_ci	if (IS_ERR(submit)) {
79862306a36Sopenharmony_ci		ret = PTR_ERR(submit);
79962306a36Sopenharmony_ci		goto out_post_unlock;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	trace_msm_gpu_submit(pid_nr(submit->pid), ring->id, submit->ident,
80362306a36Sopenharmony_ci		args->nr_bos, args->nr_cmds);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&queue->lock);
80662306a36Sopenharmony_ci	if (ret)
80762306a36Sopenharmony_ci		goto out_post_unlock;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_SUDO)
81062306a36Sopenharmony_ci		submit->in_rb = true;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
81362306a36Sopenharmony_ci		struct dma_fence *in_fence;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		in_fence = sync_file_get_fence(args->fence_fd);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		if (!in_fence) {
81862306a36Sopenharmony_ci			ret = -EINVAL;
81962306a36Sopenharmony_ci			goto out_unlock;
82062306a36Sopenharmony_ci		}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		ret = drm_sched_job_add_dependency(&submit->base, in_fence);
82362306a36Sopenharmony_ci		if (ret)
82462306a36Sopenharmony_ci			goto out_unlock;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) {
82862306a36Sopenharmony_ci		syncobjs_to_reset = msm_parse_deps(submit, file,
82962306a36Sopenharmony_ci		                                   args->in_syncobjs,
83062306a36Sopenharmony_ci		                                   args->nr_in_syncobjs,
83162306a36Sopenharmony_ci		                                   args->syncobj_stride);
83262306a36Sopenharmony_ci		if (IS_ERR(syncobjs_to_reset)) {
83362306a36Sopenharmony_ci			ret = PTR_ERR(syncobjs_to_reset);
83462306a36Sopenharmony_ci			goto out_unlock;
83562306a36Sopenharmony_ci		}
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) {
83962306a36Sopenharmony_ci		post_deps = msm_parse_post_deps(dev, file,
84062306a36Sopenharmony_ci		                                args->out_syncobjs,
84162306a36Sopenharmony_ci		                                args->nr_out_syncobjs,
84262306a36Sopenharmony_ci		                                args->syncobj_stride);
84362306a36Sopenharmony_ci		if (IS_ERR(post_deps)) {
84462306a36Sopenharmony_ci			ret = PTR_ERR(post_deps);
84562306a36Sopenharmony_ci			goto out_unlock;
84662306a36Sopenharmony_ci		}
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	ret = submit_lookup_objects(submit, args, file);
85062306a36Sopenharmony_ci	if (ret)
85162306a36Sopenharmony_ci		goto out;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	ret = submit_lookup_cmds(submit, args, file);
85462306a36Sopenharmony_ci	if (ret)
85562306a36Sopenharmony_ci		goto out;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* copy_*_user while holding a ww ticket upsets lockdep */
85862306a36Sopenharmony_ci	ww_acquire_init(&submit->ticket, &reservation_ww_class);
85962306a36Sopenharmony_ci	has_ww_ticket = true;
86062306a36Sopenharmony_ci	ret = submit_lock_objects(submit);
86162306a36Sopenharmony_ci	if (ret)
86262306a36Sopenharmony_ci		goto out;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	ret = submit_fence_sync(submit, !!(args->flags & MSM_SUBMIT_NO_IMPLICIT));
86562306a36Sopenharmony_ci	if (ret)
86662306a36Sopenharmony_ci		goto out;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	ret = submit_pin_objects(submit);
86962306a36Sopenharmony_ci	if (ret)
87062306a36Sopenharmony_ci		goto out;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	for (i = 0; i < args->nr_cmds; i++) {
87362306a36Sopenharmony_ci		struct drm_gem_object *obj;
87462306a36Sopenharmony_ci		uint64_t iova;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		ret = submit_bo(submit, submit->cmd[i].idx,
87762306a36Sopenharmony_ci				&obj, &iova, NULL);
87862306a36Sopenharmony_ci		if (ret)
87962306a36Sopenharmony_ci			goto out;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (!submit->cmd[i].size ||
88262306a36Sopenharmony_ci			((submit->cmd[i].size + submit->cmd[i].offset) >
88362306a36Sopenharmony_ci				obj->size / 4)) {
88462306a36Sopenharmony_ci			DRM_ERROR("invalid cmdstream size: %u\n", submit->cmd[i].size * 4);
88562306a36Sopenharmony_ci			ret = -EINVAL;
88662306a36Sopenharmony_ci			goto out;
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		submit->cmd[i].iova = iova + (submit->cmd[i].offset * 4);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		if (submit->valid)
89262306a36Sopenharmony_ci			continue;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		if (!gpu->allow_relocs) {
89562306a36Sopenharmony_ci			if (submit->cmd[i].nr_relocs) {
89662306a36Sopenharmony_ci				DRM_ERROR("relocs not allowed\n");
89762306a36Sopenharmony_ci				ret = -EINVAL;
89862306a36Sopenharmony_ci				goto out;
89962306a36Sopenharmony_ci			}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci			continue;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		ret = submit_reloc(submit, obj, submit->cmd[i].offset * 4,
90562306a36Sopenharmony_ci				submit->cmd[i].nr_relocs, submit->cmd[i].relocs);
90662306a36Sopenharmony_ci		if (ret)
90762306a36Sopenharmony_ci			goto out;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	submit->nr_cmds = i;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	spin_lock(&queue->idr_lock);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * If using userspace provided seqno fence, validate that the id
91862306a36Sopenharmony_ci	 * is available before arming sched job.  Since access to fence_idr
91962306a36Sopenharmony_ci	 * is serialized on the queue lock, the slot should be still avail
92062306a36Sopenharmony_ci	 * after the job is armed
92162306a36Sopenharmony_ci	 */
92262306a36Sopenharmony_ci	if ((args->flags & MSM_SUBMIT_FENCE_SN_IN) &&
92362306a36Sopenharmony_ci			(!args->fence || idr_find(&queue->fence_idr, args->fence))) {
92462306a36Sopenharmony_ci		spin_unlock(&queue->idr_lock);
92562306a36Sopenharmony_ci		idr_preload_end();
92662306a36Sopenharmony_ci		ret = -EINVAL;
92762306a36Sopenharmony_ci		goto out;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	drm_sched_job_arm(&submit->base);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	submit->user_fence = dma_fence_get(&submit->base.s_fence->finished);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (args->flags & MSM_SUBMIT_FENCE_SN_IN) {
93562306a36Sopenharmony_ci		/*
93662306a36Sopenharmony_ci		 * Userspace has assigned the seqno fence that it wants
93762306a36Sopenharmony_ci		 * us to use.  It is an error to pick a fence sequence
93862306a36Sopenharmony_ci		 * number that is not available.
93962306a36Sopenharmony_ci		 */
94062306a36Sopenharmony_ci		submit->fence_id = args->fence;
94162306a36Sopenharmony_ci		ret = idr_alloc_u32(&queue->fence_idr, submit->user_fence,
94262306a36Sopenharmony_ci				    &submit->fence_id, submit->fence_id,
94362306a36Sopenharmony_ci				    GFP_NOWAIT);
94462306a36Sopenharmony_ci		/*
94562306a36Sopenharmony_ci		 * We've already validated that the fence_id slot is valid,
94662306a36Sopenharmony_ci		 * so if idr_alloc_u32 failed, it is a kernel bug
94762306a36Sopenharmony_ci		 */
94862306a36Sopenharmony_ci		WARN_ON(ret);
94962306a36Sopenharmony_ci	} else {
95062306a36Sopenharmony_ci		/*
95162306a36Sopenharmony_ci		 * Allocate an id which can be used by WAIT_FENCE ioctl to map
95262306a36Sopenharmony_ci		 * back to the underlying fence.
95362306a36Sopenharmony_ci		 */
95462306a36Sopenharmony_ci		submit->fence_id = idr_alloc_cyclic(&queue->fence_idr,
95562306a36Sopenharmony_ci						    submit->user_fence, 1,
95662306a36Sopenharmony_ci						    INT_MAX, GFP_NOWAIT);
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	spin_unlock(&queue->idr_lock);
96062306a36Sopenharmony_ci	idr_preload_end();
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (submit->fence_id < 0) {
96362306a36Sopenharmony_ci		ret = submit->fence_id;
96462306a36Sopenharmony_ci		submit->fence_id = 0;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (ret == 0 && args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
96862306a36Sopenharmony_ci		struct sync_file *sync_file = sync_file_create(submit->user_fence);
96962306a36Sopenharmony_ci		if (!sync_file) {
97062306a36Sopenharmony_ci			ret = -ENOMEM;
97162306a36Sopenharmony_ci		} else {
97262306a36Sopenharmony_ci			fd_install(out_fence_fd, sync_file->file);
97362306a36Sopenharmony_ci			args->fence_fd = out_fence_fd;
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	submit_attach_object_fences(submit);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* The scheduler owns a ref now: */
98062306a36Sopenharmony_ci	msm_gem_submit_get(submit);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	msm_rd_dump_submit(priv->rd, submit, NULL);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	drm_sched_entity_push_job(&submit->base);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	args->fence = submit->fence_id;
98762306a36Sopenharmony_ci	queue->last_fence = submit->fence_id;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs);
99062306a36Sopenharmony_ci	msm_process_post_deps(post_deps, args->nr_out_syncobjs,
99162306a36Sopenharmony_ci	                      submit->user_fence);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ciout:
99562306a36Sopenharmony_ci	submit_cleanup(submit, !!ret);
99662306a36Sopenharmony_ci	if (has_ww_ticket)
99762306a36Sopenharmony_ci		ww_acquire_fini(&submit->ticket);
99862306a36Sopenharmony_ciout_unlock:
99962306a36Sopenharmony_ci	mutex_unlock(&queue->lock);
100062306a36Sopenharmony_ciout_post_unlock:
100162306a36Sopenharmony_ci	if (ret && (out_fence_fd >= 0))
100262306a36Sopenharmony_ci		put_unused_fd(out_fence_fd);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(submit)) {
100562306a36Sopenharmony_ci		msm_gem_submit_put(submit);
100662306a36Sopenharmony_ci	} else {
100762306a36Sopenharmony_ci		/*
100862306a36Sopenharmony_ci		 * If the submit hasn't yet taken ownership of the queue
100962306a36Sopenharmony_ci		 * then we need to drop the reference ourself:
101062306a36Sopenharmony_ci		 */
101162306a36Sopenharmony_ci		msm_submitqueue_put(queue);
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(post_deps)) {
101462306a36Sopenharmony_ci		for (i = 0; i < args->nr_out_syncobjs; ++i) {
101562306a36Sopenharmony_ci			kfree(post_deps[i].chain);
101662306a36Sopenharmony_ci			drm_syncobj_put(post_deps[i].syncobj);
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci		kfree(post_deps);
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(syncobjs_to_reset)) {
102262306a36Sopenharmony_ci		for (i = 0; i < args->nr_in_syncobjs; ++i) {
102362306a36Sopenharmony_ci			if (syncobjs_to_reset[i])
102462306a36Sopenharmony_ci				drm_syncobj_put(syncobjs_to_reset[i]);
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci		kfree(syncobjs_to_reset);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return ret;
103062306a36Sopenharmony_ci}
1031