18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Etnaviv Project 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 78c2ecf20Sopenharmony_ci#include <linux/dma-fence-array.h> 88c2ecf20Sopenharmony_ci#include <linux/file.h> 98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-resv.h> 118c2ecf20Sopenharmony_ci#include <linux/sync_file.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "etnaviv_cmdbuf.h" 168c2ecf20Sopenharmony_ci#include "etnaviv_drv.h" 178c2ecf20Sopenharmony_ci#include "etnaviv_gpu.h" 188c2ecf20Sopenharmony_ci#include "etnaviv_gem.h" 198c2ecf20Sopenharmony_ci#include "etnaviv_perfmon.h" 208c2ecf20Sopenharmony_ci#include "etnaviv_sched.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Cmdstream submission: 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE) 278c2ecf20Sopenharmony_ci/* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */ 288c2ecf20Sopenharmony_ci#define BO_LOCKED 0x4000 298c2ecf20Sopenharmony_ci#define BO_PINNED 0x2000 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic struct etnaviv_gem_submit *submit_create(struct drm_device *dev, 328c2ecf20Sopenharmony_ci struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit; 358c2ecf20Sopenharmony_ci size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit)); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci submit = kzalloc(sz, GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!submit) 398c2ecf20Sopenharmony_ci return NULL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci submit->pmrs = kcalloc(nr_pmrs, sizeof(struct etnaviv_perfmon_request), 428c2ecf20Sopenharmony_ci GFP_KERNEL); 438c2ecf20Sopenharmony_ci if (!submit->pmrs) { 448c2ecf20Sopenharmony_ci kfree(submit); 458c2ecf20Sopenharmony_ci return NULL; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci submit->nr_pmrs = nr_pmrs; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci submit->gpu = gpu; 508c2ecf20Sopenharmony_ci kref_init(&submit->refcount); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return submit; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int submit_lookup_objects(struct etnaviv_gem_submit *submit, 568c2ecf20Sopenharmony_ci struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos, 578c2ecf20Sopenharmony_ci unsigned nr_bos) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct drm_etnaviv_gem_submit_bo *bo; 608c2ecf20Sopenharmony_ci unsigned i; 618c2ecf20Sopenharmony_ci int ret = 0; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci spin_lock(&file->table_lock); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) { 668c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (bo->flags & BO_INVALID_FLAGS) { 698c2ecf20Sopenharmony_ci DRM_ERROR("invalid flags: %x\n", bo->flags); 708c2ecf20Sopenharmony_ci ret = -EINVAL; 718c2ecf20Sopenharmony_ci goto out_unlock; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci submit->bos[i].flags = bo->flags; 758c2ecf20Sopenharmony_ci if (submit->flags & ETNA_SUBMIT_SOFTPIN) { 768c2ecf20Sopenharmony_ci if (bo->presumed < ETNAVIV_SOFTPIN_START_ADDRESS) { 778c2ecf20Sopenharmony_ci DRM_ERROR("invalid softpin address\n"); 788c2ecf20Sopenharmony_ci ret = -EINVAL; 798c2ecf20Sopenharmony_ci goto out_unlock; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci submit->bos[i].va = bo->presumed; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* normally use drm_gem_object_lookup(), but for bulk lookup 858c2ecf20Sopenharmony_ci * all under single table_lock just hit object_idr directly: 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci obj = idr_find(&file->object_idr, bo->handle); 888c2ecf20Sopenharmony_ci if (!obj) { 898c2ecf20Sopenharmony_ci DRM_ERROR("invalid handle %u at index %u\n", 908c2ecf20Sopenharmony_ci bo->handle, i); 918c2ecf20Sopenharmony_ci ret = -EINVAL; 928c2ecf20Sopenharmony_ci goto out_unlock; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * Take a refcount on the object. The file table lock 978c2ecf20Sopenharmony_ci * prevents the object_idr's refcount on this being dropped. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci drm_gem_object_get(obj); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci submit->bos[i].obj = to_etnaviv_bo(obj); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciout_unlock: 1058c2ecf20Sopenharmony_ci submit->nr_bos = i; 1068c2ecf20Sopenharmony_ci spin_unlock(&file->table_lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void submit_unlock_object(struct etnaviv_gem_submit *submit, int i) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci if (submit->bos[i].flags & BO_LOCKED) { 1148c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &submit->bos[i].obj->base; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci dma_resv_unlock(obj->resv); 1178c2ecf20Sopenharmony_ci submit->bos[i].flags &= ~BO_LOCKED; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int submit_lock_objects(struct etnaviv_gem_submit *submit, 1228c2ecf20Sopenharmony_ci struct ww_acquire_ctx *ticket) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int contended, slow_locked = -1, i, ret = 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciretry: 1278c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 1288c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &submit->bos[i].obj->base; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (slow_locked == i) 1318c2ecf20Sopenharmony_ci slow_locked = -1; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci contended = i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!(submit->bos[i].flags & BO_LOCKED)) { 1368c2ecf20Sopenharmony_ci ret = dma_resv_lock_interruptible(obj->resv, ticket); 1378c2ecf20Sopenharmony_ci if (ret == -EALREADY) 1388c2ecf20Sopenharmony_ci DRM_ERROR("BO at index %u already on submit list\n", 1398c2ecf20Sopenharmony_ci i); 1408c2ecf20Sopenharmony_ci if (ret) 1418c2ecf20Sopenharmony_ci goto fail; 1428c2ecf20Sopenharmony_ci submit->bos[i].flags |= BO_LOCKED; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ww_acquire_done(ticket); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cifail: 1518c2ecf20Sopenharmony_ci for (; i >= 0; i--) 1528c2ecf20Sopenharmony_ci submit_unlock_object(submit, i); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (slow_locked > 0) 1558c2ecf20Sopenharmony_ci submit_unlock_object(submit, slow_locked); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (ret == -EDEADLK) { 1588c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci obj = &submit->bos[contended].obj->base; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* we lost out in a seqno race, lock and retry.. */ 1638c2ecf20Sopenharmony_ci ret = dma_resv_lock_slow_interruptible(obj->resv, ticket); 1648c2ecf20Sopenharmony_ci if (!ret) { 1658c2ecf20Sopenharmony_ci submit->bos[contended].flags |= BO_LOCKED; 1668c2ecf20Sopenharmony_ci slow_locked = contended; 1678c2ecf20Sopenharmony_ci goto retry; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int submit_fence_sync(struct etnaviv_gem_submit *submit) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int i, ret = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 1798c2ecf20Sopenharmony_ci struct etnaviv_gem_submit_bo *bo = &submit->bos[i]; 1808c2ecf20Sopenharmony_ci struct dma_resv *robj = bo->obj->base.resv; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) { 1838c2ecf20Sopenharmony_ci ret = dma_resv_reserve_shared(robj, 1); 1848c2ecf20Sopenharmony_ci if (ret) 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (submit->flags & ETNA_SUBMIT_NO_IMPLICIT) 1898c2ecf20Sopenharmony_ci continue; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (bo->flags & ETNA_SUBMIT_BO_WRITE) { 1928c2ecf20Sopenharmony_ci ret = dma_resv_get_fences_rcu(robj, &bo->excl, 1938c2ecf20Sopenharmony_ci &bo->nr_shared, 1948c2ecf20Sopenharmony_ci &bo->shared); 1958c2ecf20Sopenharmony_ci if (ret) 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci bo->excl = dma_resv_get_excl_rcu(robj); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void submit_attach_object_fences(struct etnaviv_gem_submit *submit) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 2118c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &submit->bos[i].obj->base; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE) 2148c2ecf20Sopenharmony_ci dma_resv_add_excl_fence(obj->resv, 2158c2ecf20Sopenharmony_ci submit->out_fence); 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci dma_resv_add_shared_fence(obj->resv, 2188c2ecf20Sopenharmony_ci submit->out_fence); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci submit_unlock_object(submit, i); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int submit_pin_objects(struct etnaviv_gem_submit *submit) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int i, ret = 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 2298c2ecf20Sopenharmony_ci struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; 2308c2ecf20Sopenharmony_ci struct etnaviv_vram_mapping *mapping; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base, 2338c2ecf20Sopenharmony_ci submit->mmu_context, 2348c2ecf20Sopenharmony_ci submit->bos[i].va); 2358c2ecf20Sopenharmony_ci if (IS_ERR(mapping)) { 2368c2ecf20Sopenharmony_ci ret = PTR_ERR(mapping); 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if ((submit->flags & ETNA_SUBMIT_SOFTPIN) && 2418c2ecf20Sopenharmony_ci submit->bos[i].va != mapping->iova) { 2428c2ecf20Sopenharmony_ci etnaviv_gem_mapping_unreference(mapping); 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci atomic_inc(&etnaviv_obj->gpu_active); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci submit->bos[i].flags |= BO_PINNED; 2498c2ecf20Sopenharmony_ci submit->bos[i].mapping = mapping; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int submit_bo(struct etnaviv_gem_submit *submit, u32 idx, 2568c2ecf20Sopenharmony_ci struct etnaviv_gem_submit_bo **bo) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (idx >= submit->nr_bos) { 2598c2ecf20Sopenharmony_ci DRM_ERROR("invalid buffer index: %u (out of %u)\n", 2608c2ecf20Sopenharmony_ci idx, submit->nr_bos); 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci *bo = &submit->bos[idx]; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* process the reloc's and patch up the cmdstream as needed: */ 2708c2ecf20Sopenharmony_cistatic int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, 2718c2ecf20Sopenharmony_ci u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs, 2728c2ecf20Sopenharmony_ci u32 nr_relocs) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci u32 i, last_offset = 0; 2758c2ecf20Sopenharmony_ci u32 *ptr = stream; 2768c2ecf20Sopenharmony_ci int ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Submits using softpin don't blend with relocs */ 2798c2ecf20Sopenharmony_ci if ((submit->flags & ETNA_SUBMIT_SOFTPIN) && nr_relocs != 0) 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci for (i = 0; i < nr_relocs; i++) { 2838c2ecf20Sopenharmony_ci const struct drm_etnaviv_gem_submit_reloc *r = relocs + i; 2848c2ecf20Sopenharmony_ci struct etnaviv_gem_submit_bo *bo; 2858c2ecf20Sopenharmony_ci u32 off; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (unlikely(r->flags)) { 2888c2ecf20Sopenharmony_ci DRM_ERROR("invalid reloc flags\n"); 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (r->submit_offset % 4) { 2938c2ecf20Sopenharmony_ci DRM_ERROR("non-aligned reloc offset: %u\n", 2948c2ecf20Sopenharmony_ci r->submit_offset); 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* offset in dwords: */ 2998c2ecf20Sopenharmony_ci off = r->submit_offset / 4; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if ((off >= size ) || 3028c2ecf20Sopenharmony_ci (off < last_offset)) { 3038c2ecf20Sopenharmony_ci DRM_ERROR("invalid offset %u at reloc %u\n", off, i); 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = submit_bo(submit, r->reloc_idx, &bo); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (r->reloc_offset > bo->obj->base.size - sizeof(*ptr)) { 3128c2ecf20Sopenharmony_ci DRM_ERROR("relocation %u outside object\n", i); 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ptr[off] = bo->mapping->iova + r->reloc_offset; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci last_offset = off; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int submit_perfmon_validate(struct etnaviv_gem_submit *submit, 3258c2ecf20Sopenharmony_ci u32 exec_state, const struct drm_etnaviv_gem_submit_pmr *pmrs) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci u32 i; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_pmrs; i++) { 3308c2ecf20Sopenharmony_ci const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i; 3318c2ecf20Sopenharmony_ci struct etnaviv_gem_submit_bo *bo; 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = submit_bo(submit, r->read_idx, &bo); 3358c2ecf20Sopenharmony_ci if (ret) 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* at offset 0 a sequence number gets stored used for userspace sync */ 3398c2ecf20Sopenharmony_ci if (r->read_offset == 0) { 3408c2ecf20Sopenharmony_ci DRM_ERROR("perfmon request: offset is 0"); 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (r->read_offset >= bo->obj->base.size - sizeof(u32)) { 3458c2ecf20Sopenharmony_ci DRM_ERROR("perfmon request: offset %u outside object", i); 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) { 3508c2ecf20Sopenharmony_ci DRM_ERROR("perfmon request: flags are not valid"); 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (etnaviv_pm_req_validate(r, exec_state)) { 3558c2ecf20Sopenharmony_ci DRM_ERROR("perfmon request: domain or signal not valid"); 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci submit->pmrs[i].flags = r->flags; 3608c2ecf20Sopenharmony_ci submit->pmrs[i].domain = r->domain; 3618c2ecf20Sopenharmony_ci submit->pmrs[i].signal = r->signal; 3628c2ecf20Sopenharmony_ci submit->pmrs[i].sequence = r->sequence; 3638c2ecf20Sopenharmony_ci submit->pmrs[i].offset = r->read_offset; 3648c2ecf20Sopenharmony_ci submit->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void submit_cleanup(struct kref *kref) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit = 3738c2ecf20Sopenharmony_ci container_of(kref, struct etnaviv_gem_submit, refcount); 3748c2ecf20Sopenharmony_ci unsigned i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (submit->runtime_resumed) 3778c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(submit->gpu->dev); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (submit->cmdbuf.suballoc) 3808c2ecf20Sopenharmony_ci etnaviv_cmdbuf_free(&submit->cmdbuf); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (submit->mmu_context) 3838c2ecf20Sopenharmony_ci etnaviv_iommu_context_put(submit->mmu_context); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (submit->prev_mmu_context) 3868c2ecf20Sopenharmony_ci etnaviv_iommu_context_put(submit->prev_mmu_context); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci for (i = 0; i < submit->nr_bos; i++) { 3898c2ecf20Sopenharmony_ci struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* unpin all objects */ 3928c2ecf20Sopenharmony_ci if (submit->bos[i].flags & BO_PINNED) { 3938c2ecf20Sopenharmony_ci etnaviv_gem_mapping_unreference(submit->bos[i].mapping); 3948c2ecf20Sopenharmony_ci atomic_dec(&etnaviv_obj->gpu_active); 3958c2ecf20Sopenharmony_ci submit->bos[i].mapping = NULL; 3968c2ecf20Sopenharmony_ci submit->bos[i].flags &= ~BO_PINNED; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* if the GPU submit failed, objects might still be locked */ 4008c2ecf20Sopenharmony_ci submit_unlock_object(submit, i); 4018c2ecf20Sopenharmony_ci drm_gem_object_put(&etnaviv_obj->base); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci wake_up_all(&submit->gpu->fence_event); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (submit->in_fence) 4078c2ecf20Sopenharmony_ci dma_fence_put(submit->in_fence); 4088c2ecf20Sopenharmony_ci if (submit->out_fence) { 4098c2ecf20Sopenharmony_ci /* first remove from IDR, so fence can not be found anymore */ 4108c2ecf20Sopenharmony_ci mutex_lock(&submit->gpu->fence_lock); 4118c2ecf20Sopenharmony_ci idr_remove(&submit->gpu->fence_idr, submit->out_fence_id); 4128c2ecf20Sopenharmony_ci mutex_unlock(&submit->gpu->fence_lock); 4138c2ecf20Sopenharmony_ci dma_fence_put(submit->out_fence); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci kfree(submit->pmrs); 4168c2ecf20Sopenharmony_ci kfree(submit); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_civoid etnaviv_submit_put(struct etnaviv_gem_submit *submit) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci kref_put(&submit->refcount, submit_cleanup); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciint etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, 4258c2ecf20Sopenharmony_ci struct drm_file *file) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct etnaviv_file_private *ctx = file->driver_priv; 4288c2ecf20Sopenharmony_ci struct etnaviv_drm_private *priv = dev->dev_private; 4298c2ecf20Sopenharmony_ci struct drm_etnaviv_gem_submit *args = data; 4308c2ecf20Sopenharmony_ci struct drm_etnaviv_gem_submit_reloc *relocs; 4318c2ecf20Sopenharmony_ci struct drm_etnaviv_gem_submit_pmr *pmrs; 4328c2ecf20Sopenharmony_ci struct drm_etnaviv_gem_submit_bo *bos; 4338c2ecf20Sopenharmony_ci struct etnaviv_gem_submit *submit; 4348c2ecf20Sopenharmony_ci struct etnaviv_gpu *gpu; 4358c2ecf20Sopenharmony_ci struct sync_file *sync_file = NULL; 4368c2ecf20Sopenharmony_ci struct ww_acquire_ctx ticket; 4378c2ecf20Sopenharmony_ci int out_fence_fd = -1; 4388c2ecf20Sopenharmony_ci void *stream; 4398c2ecf20Sopenharmony_ci int ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (args->pipe >= ETNA_MAX_PIPES) 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci gpu = priv->gpu[args->pipe]; 4458c2ecf20Sopenharmony_ci if (!gpu) 4468c2ecf20Sopenharmony_ci return -ENXIO; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (args->stream_size % 4) { 4498c2ecf20Sopenharmony_ci DRM_ERROR("non-aligned cmdstream buffer size: %u\n", 4508c2ecf20Sopenharmony_ci args->stream_size); 4518c2ecf20Sopenharmony_ci return -EINVAL; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (args->exec_state != ETNA_PIPE_3D && 4558c2ecf20Sopenharmony_ci args->exec_state != ETNA_PIPE_2D && 4568c2ecf20Sopenharmony_ci args->exec_state != ETNA_PIPE_VG) { 4578c2ecf20Sopenharmony_ci DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state); 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (args->flags & ~ETNA_SUBMIT_FLAGS) { 4628c2ecf20Sopenharmony_ci DRM_ERROR("invalid flags: 0x%x\n", args->flags); 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if ((args->flags & ETNA_SUBMIT_SOFTPIN) && 4678c2ecf20Sopenharmony_ci priv->mmu_global->version != ETNAVIV_IOMMU_V2) { 4688c2ecf20Sopenharmony_ci DRM_ERROR("softpin requested on incompatible MMU\n"); 4698c2ecf20Sopenharmony_ci return -EINVAL; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (args->stream_size > SZ_128K || args->nr_relocs > SZ_128K || 4738c2ecf20Sopenharmony_ci args->nr_bos > SZ_128K || args->nr_pmrs > 128) { 4748c2ecf20Sopenharmony_ci DRM_ERROR("submit arguments out of size limits\n"); 4758c2ecf20Sopenharmony_ci return -EINVAL; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * Copy the command submission and bo array to kernel space in 4808c2ecf20Sopenharmony_ci * one go, and do this outside of any locks. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL); 4838c2ecf20Sopenharmony_ci relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL); 4848c2ecf20Sopenharmony_ci pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL); 4858c2ecf20Sopenharmony_ci stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!bos || !relocs || !pmrs || !stream) { 4878c2ecf20Sopenharmony_ci ret = -ENOMEM; 4888c2ecf20Sopenharmony_ci goto err_submit_cmds; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret = copy_from_user(bos, u64_to_user_ptr(args->bos), 4928c2ecf20Sopenharmony_ci args->nr_bos * sizeof(*bos)); 4938c2ecf20Sopenharmony_ci if (ret) { 4948c2ecf20Sopenharmony_ci ret = -EFAULT; 4958c2ecf20Sopenharmony_ci goto err_submit_cmds; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs), 4998c2ecf20Sopenharmony_ci args->nr_relocs * sizeof(*relocs)); 5008c2ecf20Sopenharmony_ci if (ret) { 5018c2ecf20Sopenharmony_ci ret = -EFAULT; 5028c2ecf20Sopenharmony_ci goto err_submit_cmds; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs), 5068c2ecf20Sopenharmony_ci args->nr_pmrs * sizeof(*pmrs)); 5078c2ecf20Sopenharmony_ci if (ret) { 5088c2ecf20Sopenharmony_ci ret = -EFAULT; 5098c2ecf20Sopenharmony_ci goto err_submit_cmds; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = copy_from_user(stream, u64_to_user_ptr(args->stream), 5138c2ecf20Sopenharmony_ci args->stream_size); 5148c2ecf20Sopenharmony_ci if (ret) { 5158c2ecf20Sopenharmony_ci ret = -EFAULT; 5168c2ecf20Sopenharmony_ci goto err_submit_cmds; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) { 5208c2ecf20Sopenharmony_ci out_fence_fd = get_unused_fd_flags(O_CLOEXEC); 5218c2ecf20Sopenharmony_ci if (out_fence_fd < 0) { 5228c2ecf20Sopenharmony_ci ret = out_fence_fd; 5238c2ecf20Sopenharmony_ci goto err_submit_cmds; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ww_acquire_init(&ticket, &reservation_ww_class); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci submit = submit_create(dev, gpu, args->nr_bos, args->nr_pmrs); 5308c2ecf20Sopenharmony_ci if (!submit) { 5318c2ecf20Sopenharmony_ci ret = -ENOMEM; 5328c2ecf20Sopenharmony_ci goto err_submit_ww_acquire; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &submit->cmdbuf, 5368c2ecf20Sopenharmony_ci ALIGN(args->stream_size, 8) + 8); 5378c2ecf20Sopenharmony_ci if (ret) 5388c2ecf20Sopenharmony_ci goto err_submit_objects; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci submit->ctx = file->driver_priv; 5418c2ecf20Sopenharmony_ci submit->mmu_context = etnaviv_iommu_context_get(submit->ctx->mmu); 5428c2ecf20Sopenharmony_ci submit->exec_state = args->exec_state; 5438c2ecf20Sopenharmony_ci submit->flags = args->flags; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = submit_lookup_objects(submit, file, bos, args->nr_bos); 5468c2ecf20Sopenharmony_ci if (ret) 5478c2ecf20Sopenharmony_ci goto err_submit_objects; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if ((priv->mmu_global->version != ETNAVIV_IOMMU_V2) && 5508c2ecf20Sopenharmony_ci !etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4, 5518c2ecf20Sopenharmony_ci relocs, args->nr_relocs)) { 5528c2ecf20Sopenharmony_ci ret = -EINVAL; 5538c2ecf20Sopenharmony_ci goto err_submit_objects; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (args->flags & ETNA_SUBMIT_FENCE_FD_IN) { 5578c2ecf20Sopenharmony_ci submit->in_fence = sync_file_get_fence(args->fence_fd); 5588c2ecf20Sopenharmony_ci if (!submit->in_fence) { 5598c2ecf20Sopenharmony_ci ret = -EINVAL; 5608c2ecf20Sopenharmony_ci goto err_submit_objects; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ret = submit_pin_objects(submit); 5658c2ecf20Sopenharmony_ci if (ret) 5668c2ecf20Sopenharmony_ci goto err_submit_objects; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ret = submit_reloc(submit, stream, args->stream_size / 4, 5698c2ecf20Sopenharmony_ci relocs, args->nr_relocs); 5708c2ecf20Sopenharmony_ci if (ret) 5718c2ecf20Sopenharmony_ci goto err_submit_objects; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ret = submit_perfmon_validate(submit, args->exec_state, pmrs); 5748c2ecf20Sopenharmony_ci if (ret) 5758c2ecf20Sopenharmony_ci goto err_submit_objects; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci memcpy(submit->cmdbuf.vaddr, stream, args->stream_size); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ret = submit_lock_objects(submit, &ticket); 5808c2ecf20Sopenharmony_ci if (ret) 5818c2ecf20Sopenharmony_ci goto err_submit_objects; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = submit_fence_sync(submit); 5848c2ecf20Sopenharmony_ci if (ret) 5858c2ecf20Sopenharmony_ci goto err_submit_objects; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ret = etnaviv_sched_push_job(&ctx->sched_entity[args->pipe], submit); 5888c2ecf20Sopenharmony_ci if (ret) 5898c2ecf20Sopenharmony_ci goto err_submit_objects; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci submit_attach_object_fences(submit); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) { 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * This can be improved: ideally we want to allocate the sync 5968c2ecf20Sopenharmony_ci * file before kicking off the GPU job and just attach the 5978c2ecf20Sopenharmony_ci * fence to the sync file here, eliminating the ENOMEM 5988c2ecf20Sopenharmony_ci * possibility at this stage. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci sync_file = sync_file_create(submit->out_fence); 6018c2ecf20Sopenharmony_ci if (!sync_file) { 6028c2ecf20Sopenharmony_ci ret = -ENOMEM; 6038c2ecf20Sopenharmony_ci goto err_submit_objects; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci fd_install(out_fence_fd, sync_file->file); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci args->fence_fd = out_fence_fd; 6098c2ecf20Sopenharmony_ci args->fence = submit->out_fence_id; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cierr_submit_objects: 6128c2ecf20Sopenharmony_ci etnaviv_submit_put(submit); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cierr_submit_ww_acquire: 6158c2ecf20Sopenharmony_ci ww_acquire_fini(&ticket); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cierr_submit_cmds: 6188c2ecf20Sopenharmony_ci if (ret && (out_fence_fd >= 0)) 6198c2ecf20Sopenharmony_ci put_unused_fd(out_fence_fd); 6208c2ecf20Sopenharmony_ci if (stream) 6218c2ecf20Sopenharmony_ci kvfree(stream); 6228c2ecf20Sopenharmony_ci if (bos) 6238c2ecf20Sopenharmony_ci kvfree(bos); 6248c2ecf20Sopenharmony_ci if (relocs) 6258c2ecf20Sopenharmony_ci kvfree(relocs); 6268c2ecf20Sopenharmony_ci if (pmrs) 6278c2ecf20Sopenharmony_ci kvfree(pmrs); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 631