/* For userspace errors, use DRM_UT_DRIVER.. so that userspace can enable * error msgs for debugging, but we don't spam dmesg by default
*/ #define SUBMIT_ERROR(err, submit, fmt, ...) \
UERR(err, (submit)->dev, fmt, ##__VA_ARGS__)
/* * In error paths, we could unref the submit without calling * drm_sched_entity_push_job(), so msm_job_free() will never * get called. Since drm_sched_job_cleanup() will NULL out * s_fence, we can use that to detect this case.
*/ if (submit->base.s_fence)
drm_sched_job_cleanup(&submit->base);
if (submit->fence_id) {
spin_lock(&submit->queue->idr_lock);
idr_remove(&submit->queue->fence_idr, submit->fence_id);
spin_unlock(&submit->queue->idr_lock);
}
dma_fence_put(submit->user_fence);
/* * If the submit is freed before msm_job_run(), then hw_fence is * just some pre-allocated memory, not a reference counted fence. * Once the job runs and the hw_fence is initialized, it will * have a refcount of at least one, since the submit holds a ref * to the hw_fence.
*/ if (kref_read(&submit->hw_fence->refcount) == 0) {
kfree(submit->hw_fence);
} else {
dma_fence_put(submit->hw_fence);
}
for (i = 0; i < args->nr_bos; i++) { struct drm_gem_object *obj;
/* normally use drm_gem_object_lookup(), but for bulk lookup * all under single table_lock just hit object_idr directly:
*/
obj = idr_find(&file->object_idr, submit->bos[i].handle); if (!obj) {
ret = SUBMIT_ERROR(EINVAL, submit, "invalid handle %u at index %u\n", submit->bos[i].handle, i); goto out_unlock;
}
drm_exec_until_all_locked (&submit->exec) {
ret = drm_gpuvm_prepare_vm(submit->vm, exec, 1);
drm_exec_retry_on_contention(exec); if (ret) break;
ret = drm_gpuvm_prepare_objects(submit->vm, exec, 1);
drm_exec_retry_on_contention(exec); if (ret) break;
}
return ret;
}
/* This is where we make sure all the bo's are reserved and pin'd: */ staticint submit_lock_objects(struct msm_gem_submit *submit)
{ unsigned flags = DRM_EXEC_INTERRUPTIBLE_WAIT; int ret = 0;
if (msm_context_is_vmbind(submit->queue->ctx)) return submit_lock_objects_vmbind(submit);
drm_exec_until_all_locked (&submit->exec) {
ret = drm_exec_lock_obj(&submit->exec,
drm_gpuvm_resv_obj(submit->vm));
drm_exec_retry_on_contention(&submit->exec); if (ret) break; for (unsigned i = 0; i < submit->nr_bos; i++) { struct drm_gem_object *obj = submit->bos[i].obj;
ret = drm_exec_prepare_obj(&submit->exec, obj, 1);
drm_exec_retry_on_contention(&submit->exec); if (ret) break;
}
}
return ret;
}
staticint submit_fence_sync(struct msm_gem_submit *submit)
{ int i, ret = 0;
for (i = 0; i < submit->nr_bos; i++) { struct drm_gem_object *obj = submit->bos[i].obj; bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
/* Otherwise userspace can ask for implicit sync to be * disabled on specific buffers. This is useful for internal * usermode driver managed buffers, suballocation, etc.
*/ if (submit->bos[i].flags & MSM_SUBMIT_BO_NO_IMPLICIT) continue;
ret = drm_sched_job_add_implicit_dependencies(&submit->base,
obj,
write); if (ret) break;
}
return ret;
}
staticint submit_pin_objects(struct msm_gem_submit *submit)
{ struct msm_drm_private *priv = submit->dev->dev_private; int i, ret = 0;
for (i = 0; i < submit->nr_bos; i++) { struct drm_gem_object *obj = submit->bos[i].obj; struct drm_gpuva *vma;
/* if locking succeeded, pin bo: */
vma = msm_gem_get_vma_locked(obj, submit->vm); if (IS_ERR(vma)) {
ret = PTR_ERR(vma); break;
}
ret = msm_gem_pin_vma_locked(obj, vma); if (ret) break;
/* * A second loop while holding the LRU lock (a) avoids acquiring/dropping * the LRU lock for each individual bo, while (b) avoiding holding the * LRU lock while calling msm_gem_pin_vma_locked() (which could trigger * get_pages() which could trigger reclaim.. and if we held the LRU lock * could trigger deadlock with the shrinker).
*/
mutex_lock(&priv->lru.lock); for (i = 0; i < submit->nr_bos; i++) {
msm_gem_pin_obj_locked(submit->bos[i].obj);
}
mutex_unlock(&priv->lru.lock);
submit->bos_pinned = true;
return ret;
}
staticvoid submit_unpin_objects(struct msm_gem_submit *submit)
{ if (!submit->bos_pinned) return;
for (int i = 0; i < submit->nr_bos; i++) { struct drm_gem_object *obj = submit->bos[i].obj;
if (obj)
*obj = submit->bos[idx].obj; if (iova)
*iova = submit->bos[idx].iova;
return 0;
}
/* process the reloc's and patch up the cmdstream as needed: */ staticint submit_reloc(struct msm_gem_submit *submit, struct drm_gem_object *obj,
uint32_t offset, uint32_t nr_relocs, struct drm_msm_gem_submit_reloc *relocs)
{
uint32_t i, last_offset = 0;
uint32_t *ptr; int ret = 0;
/* For now, just map the entire thing. Eventually we probably * to do it page-by-page, w/ kmap() if not vmap()d..
*/
ptr = msm_gem_get_vaddr_locked(obj);
if (IS_ERR(ptr)) {
ret = PTR_ERR(ptr);
DBG("failed to map: %d", ret); return ret;
}
for (i = 0; i < nr_relocs; i++) { struct drm_msm_gem_submit_reloc submit_reloc = relocs[i];
uint32_t off;
uint64_t iova;
if (submit_reloc.submit_offset % 4) {
ret = SUBMIT_ERROR(EINVAL, submit, "non-aligned reloc offset: %u\n",
submit_reloc.submit_offset); goto out;
}
/* offset in dwords: */
off = submit_reloc.submit_offset / 4;
if ((off >= (obj->size / 4)) ||
(off < last_offset)) {
ret = SUBMIT_ERROR(EINVAL, submit, "invalid offset %u at reloc %u\n", off, i); goto out;
}
ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova); if (ret) goto out;
/* Cleanup submit at end of ioctl. In the error case, this also drops * references, unpins, and drops active refcnt. In the non-error case, * this is done when the submit is retired.
*/ staticvoid submit_cleanup(struct msm_gem_submit *submit, bool error)
{ if (error)
submit_unpin_objects(submit);
if (submit->exec.objects)
drm_exec_fini(&submit->exec);
/* if job wasn't enqueued to scheduler, early retirement: */ if (error)
msm_submit_retire(submit);
}
void msm_submit_retire(struct msm_gem_submit *submit)
{ int i;
for (i = 0; i < submit->nr_bos; i++) { struct drm_gem_object *obj = submit->bos[i].obj; struct drm_gpuvm_bo *vm_bo = submit->bos[i].vm_bo;
if (to_msm_vm(ctx->vm)->unusable) return UERR(EPIPE, dev, "context is unusable");
/* for now, we just have 3d pipe.. eventually this would need to * be more clever to dispatch to appropriate gpu module:
*/ if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0) return UERR(EINVAL, dev, "invalid pipe");
if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS) return UERR(EINVAL, dev, "invalid flags");
if (args->flags & MSM_SUBMIT_SUDO) { if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
!capable(CAP_SYS_RAWIO)) return -EINVAL;
}
queue = msm_submitqueue_get(ctx, args->queueid); if (!queue) return -ENOENT;
if (queue->flags & MSM_SUBMITQUEUE_VM_BIND) {
ret = UERR(EINVAL, dev, "Invalid queue type"); goto out_post_unlock;
}
ring = gpu->rb[queue->ring_nr];
if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
out_fence_fd = get_unused_fd_flags(O_CLOEXEC); if (out_fence_fd < 0) {
ret = out_fence_fd; goto out_post_unlock;
}
}
submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds,
file->client_id); if (IS_ERR(submit)) {
ret = PTR_ERR(submit); goto out_post_unlock;
}
if (!gpu->allow_relocs) {
ret = UERR(EINVAL, dev, "relocs not allowed\n"); goto out;
}
ret = submit_reloc(submit, obj, submit->cmd[i].offset * 4,
submit->cmd[i].nr_relocs, submit->cmd[i].relocs); if (ret) goto out;
}
submit->nr_cmds = args->nr_cmds;
idr_preload(GFP_KERNEL);
spin_lock(&queue->idr_lock);
/* * If using userspace provided seqno fence, validate that the id * is available before arming sched job. Since access to fence_idr * is serialized on the queue lock, the slot should be still avail * after the job is armed
*/ if ((args->flags & MSM_SUBMIT_FENCE_SN_IN) &&
(!args->fence || idr_find(&queue->fence_idr, args->fence))) {
spin_unlock(&queue->idr_lock);
idr_preload_end();
ret = UERR(EINVAL, dev, "invalid in-fence-sn"); goto out;
}
if (args->flags & MSM_SUBMIT_FENCE_SN_IN) { /* * Userspace has assigned the seqno fence that it wants * us to use. It is an error to pick a fence sequence * number that is not available.
*/
submit->fence_id = args->fence;
ret = idr_alloc_u32(&queue->fence_idr, submit->user_fence,
&submit->fence_id, submit->fence_id,
GFP_NOWAIT); /* * We've already validated that the fence_id slot is valid, * so if idr_alloc_u32 failed, it is a kernel bug
*/
WARN_ON(ret);
} else { /* * Allocate an id which can be used by WAIT_FENCE ioctl to map * back to the underlying fence.
*/
submit->fence_id = idr_alloc_cyclic(&queue->fence_idr,
submit->user_fence, 1,
INT_MAX, GFP_NOWAIT);
}
spin_unlock(&queue->idr_lock);
idr_preload_end();
if (submit->fence_id < 0) {
ret = submit->fence_id;
submit->fence_id = 0;
}
if (ret == 0 && args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
sync_file = sync_file_create(submit->user_fence); if (!sync_file)
ret = -ENOMEM;
}
if (ret) goto out;
submit_attach_object_fences(submit);
if (msm_context_is_vmbind(ctx)) { /* * If we are not using VM_BIND, submit_pin_vmas() will validate * just the BOs attached to the submit. In that case we don't * need to validate the _entire_ vm, because userspace tracked * what BOs are associated with the submit.
*/
ret = drm_gpuvm_validate(submit->vm, &submit->exec); if (ret) goto out;
}
/* The scheduler owns a ref now: */
msm_gem_submit_get(submit);
out:
submit_cleanup(submit, !!ret);
out_unlock:
mutex_unlock(&queue->lock);
out_post_unlock: if (ret) { if (out_fence_fd >= 0)
put_unused_fd(out_fence_fd); if (sync_file)
fput(sync_file->file);
} elseif (sync_file) {
fd_install(out_fence_fd, sync_file->file);
args->fence_fd = out_fence_fd;
}
if (!IS_ERR_OR_NULL(submit)) {
msm_gem_submit_put(submit);
} else { /* * If the submit hasn't yet taken ownership of the queue * then we need to drop the reference ourself:
*/
msm_submitqueue_put(queue);
} if (!IS_ERR_OR_NULL(post_deps)) { for (i = 0; i < args->nr_out_syncobjs; ++i) {
kfree(post_deps[i].chain);
drm_syncobj_put(post_deps[i].syncobj);
}
kfree(post_deps);
}
if (!IS_ERR_OR_NULL(syncobjs_to_reset)) { for (i = 0; i < args->nr_in_syncobjs; ++i) { if (syncobjs_to_reset[i])
drm_syncobj_put(syncobjs_to_reset[i]);
}
kfree(syncobjs_to_reset);
}
return ret;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.