static struct dma_fence *etnaviv_sched_run_job(struct drm_sched_job *sched_job) { struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job); struct dma_fence *fence = NULL; if (likely(!sched_job->s_fence->finished.error)) fence = etnaviv_gpu_submit(submit); else dev_dbg(submit->gpu->dev, "skipping bad job\n"); return fence; }
int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file) { struct etnaviv_drm_private *priv = dev->dev_private; struct drm_etnaviv_gem_submit *args = data; struct drm_etnaviv_gem_submit_reloc *relocs; struct drm_etnaviv_gem_submit_pmr *pmrs; struct drm_etnaviv_gem_submit_bo *bos; struct etnaviv_gem_submit *submit; struct etnaviv_gpu *gpu; struct sync_file *sync_file = NULL; struct ww_acquire_ctx ticket; int out_fence_fd = -1; void *stream; int ret; if (args->pipe >= ETNA_MAX_PIPES) return -EINVAL; gpu = priv->gpu[args->pipe]; if (!gpu) return -ENXIO; if (args->stream_size % 4) { DRM_ERROR("non-aligned cmdstream buffer size: %u\n", args->stream_size); return -EINVAL; } if (args->exec_state != ETNA_PIPE_3D && args->exec_state != ETNA_PIPE_2D && args->exec_state != ETNA_PIPE_VG) { DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state); return -EINVAL; } if (args->flags & ~ETNA_SUBMIT_FLAGS) { DRM_ERROR("invalid flags: 0x%x\n", args->flags); return -EINVAL; } /* * Copy the command submission and bo array to kernel space in * one go, and do this outside of any locks. */ bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL); relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL); pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL); stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL); if (!bos || !relocs || !pmrs || !stream) { ret = -ENOMEM; goto err_submit_cmds; } ret = copy_from_user(bos, u64_to_user_ptr(args->bos), args->nr_bos * sizeof(*bos)); if (ret) { ret = -EFAULT; goto err_submit_cmds; } ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs), args->nr_relocs * sizeof(*relocs)); if (ret) { ret = -EFAULT; goto err_submit_cmds; } ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs), args->nr_pmrs * sizeof(*pmrs)); if (ret) { ret = -EFAULT; goto err_submit_cmds; } ret = copy_from_user(stream, u64_to_user_ptr(args->stream), args->stream_size); if (ret) { ret = -EFAULT; goto err_submit_cmds; } if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) { out_fence_fd = get_unused_fd_flags(O_CLOEXEC); if (out_fence_fd < 0) { ret = out_fence_fd; goto err_submit_cmds; } } ww_acquire_init(&ticket, &reservation_ww_class); submit = submit_create(dev, gpu, args->nr_bos, args->nr_pmrs); if (!submit) { ret = -ENOMEM; goto err_submit_ww_acquire; } ret = etnaviv_cmdbuf_init(gpu->cmdbuf_suballoc, &submit->cmdbuf, ALIGN(args->stream_size, 8) + 8); if (ret) goto err_submit_objects; submit->cmdbuf.ctx = file->driver_priv; submit->exec_state = args->exec_state; submit->flags = args->flags; ret = submit_lookup_objects(submit, file, bos, args->nr_bos); if (ret) goto err_submit_objects; ret = submit_lock_objects(submit, &ticket); if (ret) goto err_submit_objects; if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4, relocs, args->nr_relocs)) { ret = -EINVAL; goto err_submit_objects; } if (args->flags & ETNA_SUBMIT_FENCE_FD_IN) { submit->in_fence = sync_file_get_fence(args->fence_fd); if (!submit->in_fence) { ret = -EINVAL; goto err_submit_objects; } } ret = submit_fence_sync(submit); if (ret) goto err_submit_objects; ret = submit_pin_objects(submit); if (ret) goto err_submit_objects; ret = submit_reloc(submit, stream, args->stream_size / 4, relocs, args->nr_relocs); if (ret) goto err_submit_objects; ret = submit_perfmon_validate(submit, args->exec_state, pmrs); if (ret) goto err_submit_objects; memcpy(submit->cmdbuf.vaddr, stream, args->stream_size); submit->cmdbuf.user_size = ALIGN(args->stream_size, 8); ret = etnaviv_gpu_submit(gpu, submit); if (ret) goto err_submit_objects; submit_attach_object_fences(submit); if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) { /* * This can be improved: ideally we want to allocate the sync * file before kicking off the GPU job and just attach the * fence to the sync file here, eliminating the ENOMEM * possibility at this stage. */ sync_file = sync_file_create(submit->out_fence); if (!sync_file) { ret = -ENOMEM; goto err_submit_objects; } fd_install(out_fence_fd, sync_file->file); } args->fence_fd = out_fence_fd; args->fence = submit->out_fence->seqno; err_submit_objects: etnaviv_submit_put(submit); err_submit_ww_acquire: ww_acquire_fini(&ticket); err_submit_cmds: if (ret && (out_fence_fd >= 0)) put_unused_fd(out_fence_fd); if (stream) kvfree(stream); if (bos) kvfree(bos); if (relocs) kvfree(relocs); if (pmrs) kvfree(pmrs); return ret; }