162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <drm/drm_exec.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "nouveau_drv.h" 662306a36Sopenharmony_ci#include "nouveau_gem.h" 762306a36Sopenharmony_ci#include "nouveau_mem.h" 862306a36Sopenharmony_ci#include "nouveau_dma.h" 962306a36Sopenharmony_ci#include "nouveau_exec.h" 1062306a36Sopenharmony_ci#include "nouveau_abi16.h" 1162306a36Sopenharmony_ci#include "nouveau_chan.h" 1262306a36Sopenharmony_ci#include "nouveau_sched.h" 1362306a36Sopenharmony_ci#include "nouveau_uvmm.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * DOC: Overview 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Nouveau's VM_BIND / EXEC UAPI consists of three ioctls: DRM_NOUVEAU_VM_INIT, 1962306a36Sopenharmony_ci * DRM_NOUVEAU_VM_BIND and DRM_NOUVEAU_EXEC. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * In order to use the UAPI firstly a user client must initialize the VA space 2262306a36Sopenharmony_ci * using the DRM_NOUVEAU_VM_INIT ioctl specifying which region of the VA space 2362306a36Sopenharmony_ci * should be managed by the kernel and which by the UMD. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * The DRM_NOUVEAU_VM_BIND ioctl provides clients an interface to manage the 2662306a36Sopenharmony_ci * userspace-managable portion of the VA space. It provides operations to map 2762306a36Sopenharmony_ci * and unmap memory. Mappings may be flagged as sparse. Sparse mappings are not 2862306a36Sopenharmony_ci * backed by a GEM object and the kernel will ignore GEM handles provided 2962306a36Sopenharmony_ci * alongside a sparse mapping. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Userspace may request memory backed mappings either within or outside of the 3262306a36Sopenharmony_ci * bounds (but not crossing those bounds) of a previously mapped sparse 3362306a36Sopenharmony_ci * mapping. Subsequently requested memory backed mappings within a sparse 3462306a36Sopenharmony_ci * mapping will take precedence over the corresponding range of the sparse 3562306a36Sopenharmony_ci * mapping. If such memory backed mappings are unmapped the kernel will make 3662306a36Sopenharmony_ci * sure that the corresponding sparse mapping will take their place again. 3762306a36Sopenharmony_ci * Requests to unmap a sparse mapping that still contains memory backed mappings 3862306a36Sopenharmony_ci * will result in those memory backed mappings being unmapped first. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Unmap requests are not bound to the range of existing mappings and can even 4162306a36Sopenharmony_ci * overlap the bounds of sparse mappings. For such a request the kernel will 4262306a36Sopenharmony_ci * make sure to unmap all memory backed mappings within the given range, 4362306a36Sopenharmony_ci * splitting up memory backed mappings which are only partially contained 4462306a36Sopenharmony_ci * within the given range. Unmap requests with the sparse flag set must match 4562306a36Sopenharmony_ci * the range of a previously mapped sparse mapping exactly though. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * While the kernel generally permits arbitrary sequences and ranges of memory 4862306a36Sopenharmony_ci * backed mappings being mapped and unmapped, either within a single or multiple 4962306a36Sopenharmony_ci * VM_BIND ioctl calls, there are some restrictions for sparse mappings. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * The kernel does not permit to: 5262306a36Sopenharmony_ci * - unmap non-existent sparse mappings 5362306a36Sopenharmony_ci * - unmap a sparse mapping and map a new sparse mapping overlapping the range 5462306a36Sopenharmony_ci * of the previously unmapped sparse mapping within the same VM_BIND ioctl 5562306a36Sopenharmony_ci * - unmap a sparse mapping and map new memory backed mappings overlapping the 5662306a36Sopenharmony_ci * range of the previously unmapped sparse mapping within the same VM_BIND 5762306a36Sopenharmony_ci * ioctl 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * When using the VM_BIND ioctl to request the kernel to map memory to a given 6062306a36Sopenharmony_ci * virtual address in the GPU's VA space there is no guarantee that the actual 6162306a36Sopenharmony_ci * mappings are created in the GPU's MMU. If the given memory is swapped out 6262306a36Sopenharmony_ci * at the time the bind operation is executed the kernel will stash the mapping 6362306a36Sopenharmony_ci * details into it's internal alloctor and create the actual MMU mappings once 6462306a36Sopenharmony_ci * the memory is swapped back in. While this is transparent for userspace, it is 6562306a36Sopenharmony_ci * guaranteed that all the backing memory is swapped back in and all the memory 6662306a36Sopenharmony_ci * mappings, as requested by userspace previously, are actually mapped once the 6762306a36Sopenharmony_ci * DRM_NOUVEAU_EXEC ioctl is called to submit an exec job. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * A VM_BIND job can be executed either synchronously or asynchronously. If 7062306a36Sopenharmony_ci * exectued asynchronously, userspace may provide a list of syncobjs this job 7162306a36Sopenharmony_ci * will wait for and/or a list of syncobj the kernel will signal once the 7262306a36Sopenharmony_ci * VM_BIND job finished execution. If executed synchronously the ioctl will 7362306a36Sopenharmony_ci * block until the bind job is finished. For synchronous jobs the kernel will 7462306a36Sopenharmony_ci * not permit any syncobjs submitted to the kernel. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * To execute a push buffer the UAPI provides the DRM_NOUVEAU_EXEC ioctl. EXEC 7762306a36Sopenharmony_ci * jobs are always executed asynchronously, and, equal to VM_BIND jobs, provide 7862306a36Sopenharmony_ci * the option to synchronize them with syncobjs. 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * Besides that, EXEC jobs can be scheduled for a specified channel to execute on. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Since VM_BIND jobs update the GPU's VA space on job submit, EXEC jobs do have 8362306a36Sopenharmony_ci * an up to date view of the VA space. However, the actual mappings might still 8462306a36Sopenharmony_ci * be pending. Hence, EXEC jobs require to have the particular fences - of 8562306a36Sopenharmony_ci * the corresponding VM_BIND jobs they depent on - attached to them. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int 8962306a36Sopenharmony_cinouveau_exec_job_submit(struct nouveau_job *job) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct nouveau_exec_job *exec_job = to_nouveau_exec_job(job); 9262306a36Sopenharmony_ci struct nouveau_cli *cli = job->cli; 9362306a36Sopenharmony_ci struct nouveau_uvmm *uvmm = nouveau_cli_uvmm(cli); 9462306a36Sopenharmony_ci struct drm_exec *exec = &job->exec; 9562306a36Sopenharmony_ci struct drm_gem_object *obj; 9662306a36Sopenharmony_ci unsigned long index; 9762306a36Sopenharmony_ci int ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* Create a new fence, but do not emit yet. */ 10062306a36Sopenharmony_ci ret = nouveau_fence_create(&exec_job->fence, exec_job->chan); 10162306a36Sopenharmony_ci if (ret) 10262306a36Sopenharmony_ci return ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci nouveau_uvmm_lock(uvmm); 10562306a36Sopenharmony_ci drm_exec_init(exec, DRM_EXEC_INTERRUPTIBLE_WAIT | 10662306a36Sopenharmony_ci DRM_EXEC_IGNORE_DUPLICATES); 10762306a36Sopenharmony_ci drm_exec_until_all_locked(exec) { 10862306a36Sopenharmony_ci struct drm_gpuva *va; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci drm_gpuva_for_each_va(va, &uvmm->umgr) { 11162306a36Sopenharmony_ci if (unlikely(va == &uvmm->umgr.kernel_alloc_node)) 11262306a36Sopenharmony_ci continue; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = drm_exec_prepare_obj(exec, va->gem.obj, 1); 11562306a36Sopenharmony_ci drm_exec_retry_on_contention(exec); 11662306a36Sopenharmony_ci if (ret) 11762306a36Sopenharmony_ci goto err_uvmm_unlock; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci nouveau_uvmm_unlock(uvmm); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci drm_exec_for_each_locked_object(exec, index, obj) { 12362306a36Sopenharmony_ci struct nouveau_bo *nvbo = nouveau_gem_object(obj); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci ret = nouveau_bo_validate(nvbo, true, false); 12662306a36Sopenharmony_ci if (ret) 12762306a36Sopenharmony_ci goto err_exec_fini; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cierr_uvmm_unlock: 13362306a36Sopenharmony_ci nouveau_uvmm_unlock(uvmm); 13462306a36Sopenharmony_cierr_exec_fini: 13562306a36Sopenharmony_ci drm_exec_fini(exec); 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void 14162306a36Sopenharmony_cinouveau_exec_job_armed_submit(struct nouveau_job *job) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct drm_exec *exec = &job->exec; 14462306a36Sopenharmony_ci struct drm_gem_object *obj; 14562306a36Sopenharmony_ci unsigned long index; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci drm_exec_for_each_locked_object(exec, index, obj) 14862306a36Sopenharmony_ci dma_resv_add_fence(obj->resv, job->done_fence, job->resv_usage); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci drm_exec_fini(exec); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct dma_fence * 15462306a36Sopenharmony_cinouveau_exec_job_run(struct nouveau_job *job) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct nouveau_exec_job *exec_job = to_nouveau_exec_job(job); 15762306a36Sopenharmony_ci struct nouveau_channel *chan = exec_job->chan; 15862306a36Sopenharmony_ci struct nouveau_fence *fence = exec_job->fence; 15962306a36Sopenharmony_ci int i, ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = nouveau_dma_wait(chan, exec_job->push.count + 1, 16); 16262306a36Sopenharmony_ci if (ret) { 16362306a36Sopenharmony_ci NV_PRINTK(err, job->cli, "nv50cal_space: %d\n", ret); 16462306a36Sopenharmony_ci return ERR_PTR(ret); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = 0; i < exec_job->push.count; i++) { 16862306a36Sopenharmony_ci struct drm_nouveau_exec_push *p = &exec_job->push.s[i]; 16962306a36Sopenharmony_ci bool no_prefetch = p->flags & DRM_NOUVEAU_EXEC_PUSH_NO_PREFETCH; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci nv50_dma_push(chan, p->va, p->va_len, no_prefetch); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = nouveau_fence_emit(fence); 17562306a36Sopenharmony_ci if (ret) { 17662306a36Sopenharmony_ci nouveau_fence_unref(&exec_job->fence); 17762306a36Sopenharmony_ci NV_PRINTK(err, job->cli, "error fencing pushbuf: %d\n", ret); 17862306a36Sopenharmony_ci WIND_RING(chan); 17962306a36Sopenharmony_ci return ERR_PTR(ret); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* The fence was emitted successfully, set the job's fence pointer to 18362306a36Sopenharmony_ci * NULL in order to avoid freeing it up when the job is cleaned up. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci exec_job->fence = NULL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return &fence->base; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void 19162306a36Sopenharmony_cinouveau_exec_job_free(struct nouveau_job *job) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct nouveau_exec_job *exec_job = to_nouveau_exec_job(job); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci nouveau_job_free(job); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci kfree(exec_job->fence); 19862306a36Sopenharmony_ci kfree(exec_job->push.s); 19962306a36Sopenharmony_ci kfree(exec_job); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic enum drm_gpu_sched_stat 20362306a36Sopenharmony_cinouveau_exec_job_timeout(struct nouveau_job *job) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct nouveau_exec_job *exec_job = to_nouveau_exec_job(job); 20662306a36Sopenharmony_ci struct nouveau_channel *chan = exec_job->chan; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (unlikely(!atomic_read(&chan->killed))) 20962306a36Sopenharmony_ci nouveau_channel_kill(chan); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci NV_PRINTK(warn, job->cli, "job timeout, channel %d killed!\n", 21262306a36Sopenharmony_ci chan->chid); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci nouveau_sched_entity_fini(job->entity); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return DRM_GPU_SCHED_STAT_NOMINAL; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct nouveau_job_ops nouveau_exec_job_ops = { 22062306a36Sopenharmony_ci .submit = nouveau_exec_job_submit, 22162306a36Sopenharmony_ci .armed_submit = nouveau_exec_job_armed_submit, 22262306a36Sopenharmony_ci .run = nouveau_exec_job_run, 22362306a36Sopenharmony_ci .free = nouveau_exec_job_free, 22462306a36Sopenharmony_ci .timeout = nouveau_exec_job_timeout, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint 22862306a36Sopenharmony_cinouveau_exec_job_init(struct nouveau_exec_job **pjob, 22962306a36Sopenharmony_ci struct nouveau_exec_job_args *__args) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct nouveau_exec_job *job; 23262306a36Sopenharmony_ci struct nouveau_job_args args = {}; 23362306a36Sopenharmony_ci int i, ret; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (i = 0; i < __args->push.count; i++) { 23662306a36Sopenharmony_ci struct drm_nouveau_exec_push *p = &__args->push.s[i]; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (unlikely(p->va_len > NV50_DMA_PUSH_MAX_LENGTH)) { 23962306a36Sopenharmony_ci NV_PRINTK(err, nouveau_cli(__args->file_priv), 24062306a36Sopenharmony_ci "pushbuf size exceeds limit: 0x%x max 0x%x\n", 24162306a36Sopenharmony_ci p->va_len, NV50_DMA_PUSH_MAX_LENGTH); 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci job = *pjob = kzalloc(sizeof(*job), GFP_KERNEL); 24762306a36Sopenharmony_ci if (!job) 24862306a36Sopenharmony_ci return -ENOMEM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci job->push.count = __args->push.count; 25162306a36Sopenharmony_ci if (__args->push.count) { 25262306a36Sopenharmony_ci job->push.s = kmemdup(__args->push.s, 25362306a36Sopenharmony_ci sizeof(*__args->push.s) * 25462306a36Sopenharmony_ci __args->push.count, 25562306a36Sopenharmony_ci GFP_KERNEL); 25662306a36Sopenharmony_ci if (!job->push.s) { 25762306a36Sopenharmony_ci ret = -ENOMEM; 25862306a36Sopenharmony_ci goto err_free_job; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci job->chan = __args->chan; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci args.sched_entity = __args->sched_entity; 26562306a36Sopenharmony_ci args.file_priv = __args->file_priv; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci args.in_sync.count = __args->in_sync.count; 26862306a36Sopenharmony_ci args.in_sync.s = __args->in_sync.s; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci args.out_sync.count = __args->out_sync.count; 27162306a36Sopenharmony_ci args.out_sync.s = __args->out_sync.s; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci args.ops = &nouveau_exec_job_ops; 27462306a36Sopenharmony_ci args.resv_usage = DMA_RESV_USAGE_WRITE; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = nouveau_job_init(&job->base, &args); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci goto err_free_pushs; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cierr_free_pushs: 28362306a36Sopenharmony_ci kfree(job->push.s); 28462306a36Sopenharmony_cierr_free_job: 28562306a36Sopenharmony_ci kfree(job); 28662306a36Sopenharmony_ci *pjob = NULL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int 29262306a36Sopenharmony_cinouveau_exec(struct nouveau_exec_job_args *args) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct nouveau_exec_job *job; 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = nouveau_exec_job_init(&job, args); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = nouveau_job_submit(&job->base); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci goto err_job_fini; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cierr_job_fini: 30862306a36Sopenharmony_ci nouveau_job_fini(&job->base); 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int 31362306a36Sopenharmony_cinouveau_exec_ucopy(struct nouveau_exec_job_args *args, 31462306a36Sopenharmony_ci struct drm_nouveau_exec *req) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct drm_nouveau_sync **s; 31762306a36Sopenharmony_ci u32 inc = req->wait_count; 31862306a36Sopenharmony_ci u64 ins = req->wait_ptr; 31962306a36Sopenharmony_ci u32 outc = req->sig_count; 32062306a36Sopenharmony_ci u64 outs = req->sig_ptr; 32162306a36Sopenharmony_ci u32 pushc = req->push_count; 32262306a36Sopenharmony_ci u64 pushs = req->push_ptr; 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (pushc) { 32662306a36Sopenharmony_ci args->push.count = pushc; 32762306a36Sopenharmony_ci args->push.s = u_memcpya(pushs, pushc, sizeof(*args->push.s)); 32862306a36Sopenharmony_ci if (IS_ERR(args->push.s)) 32962306a36Sopenharmony_ci return PTR_ERR(args->push.s); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (inc) { 33362306a36Sopenharmony_ci s = &args->in_sync.s; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci args->in_sync.count = inc; 33662306a36Sopenharmony_ci *s = u_memcpya(ins, inc, sizeof(**s)); 33762306a36Sopenharmony_ci if (IS_ERR(*s)) { 33862306a36Sopenharmony_ci ret = PTR_ERR(*s); 33962306a36Sopenharmony_ci goto err_free_pushs; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (outc) { 34462306a36Sopenharmony_ci s = &args->out_sync.s; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci args->out_sync.count = outc; 34762306a36Sopenharmony_ci *s = u_memcpya(outs, outc, sizeof(**s)); 34862306a36Sopenharmony_ci if (IS_ERR(*s)) { 34962306a36Sopenharmony_ci ret = PTR_ERR(*s); 35062306a36Sopenharmony_ci goto err_free_ins; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cierr_free_pushs: 35762306a36Sopenharmony_ci u_free(args->push.s); 35862306a36Sopenharmony_cierr_free_ins: 35962306a36Sopenharmony_ci u_free(args->in_sync.s); 36062306a36Sopenharmony_ci return ret; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void 36462306a36Sopenharmony_cinouveau_exec_ufree(struct nouveau_exec_job_args *args) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci u_free(args->push.s); 36762306a36Sopenharmony_ci u_free(args->in_sync.s); 36862306a36Sopenharmony_ci u_free(args->out_sync.s); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ciint 37262306a36Sopenharmony_cinouveau_exec_ioctl_exec(struct drm_device *dev, 37362306a36Sopenharmony_ci void *data, 37462306a36Sopenharmony_ci struct drm_file *file_priv) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv); 37762306a36Sopenharmony_ci struct nouveau_cli *cli = nouveau_cli(file_priv); 37862306a36Sopenharmony_ci struct nouveau_abi16_chan *chan16; 37962306a36Sopenharmony_ci struct nouveau_channel *chan = NULL; 38062306a36Sopenharmony_ci struct nouveau_exec_job_args args = {}; 38162306a36Sopenharmony_ci struct drm_nouveau_exec *req = data; 38262306a36Sopenharmony_ci int push_max, ret = 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (unlikely(!abi16)) 38562306a36Sopenharmony_ci return -ENOMEM; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* abi16 locks already */ 38862306a36Sopenharmony_ci if (unlikely(!nouveau_cli_uvmm(cli))) 38962306a36Sopenharmony_ci return nouveau_abi16_put(abi16, -ENOSYS); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci list_for_each_entry(chan16, &abi16->channels, head) { 39262306a36Sopenharmony_ci if (chan16->chan->chid == req->channel) { 39362306a36Sopenharmony_ci chan = chan16->chan; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (!chan) 39962306a36Sopenharmony_ci return nouveau_abi16_put(abi16, -ENOENT); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (unlikely(atomic_read(&chan->killed))) 40262306a36Sopenharmony_ci return nouveau_abi16_put(abi16, -ENODEV); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!chan->dma.ib_max) 40562306a36Sopenharmony_ci return nouveau_abi16_put(abi16, -ENOSYS); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci push_max = nouveau_exec_push_max_from_ib_max(chan->dma.ib_max); 40862306a36Sopenharmony_ci if (unlikely(req->push_count > push_max)) { 40962306a36Sopenharmony_ci NV_PRINTK(err, cli, "pushbuf push count exceeds limit: %d max %d\n", 41062306a36Sopenharmony_ci req->push_count, push_max); 41162306a36Sopenharmony_ci return nouveau_abi16_put(abi16, -EINVAL); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = nouveau_exec_ucopy(&args, req); 41562306a36Sopenharmony_ci if (ret) 41662306a36Sopenharmony_ci goto out; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci args.sched_entity = &chan16->sched_entity; 41962306a36Sopenharmony_ci args.file_priv = file_priv; 42062306a36Sopenharmony_ci args.chan = chan; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = nouveau_exec(&args); 42362306a36Sopenharmony_ci if (ret) 42462306a36Sopenharmony_ci goto out_free_args; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciout_free_args: 42762306a36Sopenharmony_ci nouveau_exec_ufree(&args); 42862306a36Sopenharmony_ciout: 42962306a36Sopenharmony_ci return nouveau_abi16_put(abi16, ret); 43062306a36Sopenharmony_ci} 431