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