diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c index 306d458752d7..5db37856db12 100644 --- a/drivers/gpu/drm/tegra/submit.c +++ b/drivers/gpu/drm/tegra/submit.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2020 NVIDIA Corporation */ +#include #include #include #include @@ -349,6 +350,71 @@ static int submit_get_syncpt(struct tegra_drm_context *context, struct host1x_jo return 0; } +static int submit_handle_in_fence(struct tegra_drm_context *context, struct host1x *host1x, + struct dma_fence *fence, u32 *num_waits) +{ + struct dma_fence_unwrap iter; + struct host1x *fence_host1x; + struct dma_fence *f; + int err = 0; + + *num_waits = 0; + + dma_fence_unwrap_for_each(f, &iter, fence) { + long wait_err; + + if (err) + continue; + + if (dma_fence_is_signaled(f)) + continue; + + err = host1x_fence_extract(f, &fence_host1x, NULL, NULL); + if (!err && fence_host1x == host1x) { + (*num_waits)++; + continue; + } + + wait_err = dma_fence_wait_timeout(f, true, msecs_to_jiffies(10000)); + if (wait_err == 0) { + SUBMIT_ERR(context, "wait for syncobj_in timed out"); + err = -ETIMEDOUT; + continue; + } else if (wait_err < 0) { + /* In practice, -ERESTARTSYS */ + err = wait_err; + continue; + } else { + err = 0; + } + } + + return err; +} + +static void submit_job_add_prefence(struct host1x_job *job, struct dma_fence *fence, u32 class) +{ + struct dma_fence_unwrap iter; + struct dma_fence *f; + + dma_fence_unwrap_for_each(f, &iter, fence) { + u32 id, threshold; + + if (dma_fence_is_signaled(f)) + continue; + + if (host1x_fence_extract(f, NULL, &id, &threshold)) { + /* + * The fence may have signalled since the check above, and + * the dma_fence framework then detached its ops. + */ + continue; + } + + host1x_job_add_wait(job, id, threshold, false, class); + } +} + static int submit_job_add_gather(struct host1x_job *job, struct tegra_drm_context *context, struct drm_tegra_submit_cmd_gather_uptr *cmd, struct gather_bo *bo, u32 *offset, @@ -394,7 +460,7 @@ static int submit_job_add_gather(struct host1x_job *job, struct tegra_drm_contex static struct host1x_job * submit_create_job(struct tegra_drm_context *context, struct gather_bo *bo, struct drm_tegra_channel_submit *args, struct tegra_drm_submit_data *job_data, - struct xarray *syncpoints) + struct xarray *syncpoints, struct dma_fence *in_fence, u32 num_prefence_waits) { struct drm_tegra_submit_cmd *cmds; u32 i, gather_offset = 0, class; @@ -411,7 +477,7 @@ submit_create_job(struct tegra_drm_context *context, struct gather_bo *bo, return ERR_CAST(cmds); } - job = host1x_job_alloc(context->channel, args->num_cmds, 0, true); + job = host1x_job_alloc(context->channel, args->num_cmds + num_prefence_waits, 0, true); if (!job) { SUBMIT_ERR(context, "failed to allocate memory for job"); job = ERR_PTR(-ENOMEM); @@ -426,6 +492,9 @@ submit_create_job(struct tegra_drm_context *context, struct gather_bo *bo, job->class = context->client->base.class; job->serialize = true; + if (in_fence) + submit_job_add_prefence(job, in_fence, class); + for (i = 0; i < args->num_cmds; i++) { struct drm_tegra_submit_cmd *cmd = &cmds[i]; @@ -513,12 +582,15 @@ static void release_job(struct host1x_job *job) int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, struct drm_file *file) { + struct host1x *host1x = dev_get_drvdata(drm->dev->parent); struct host1x_memory_context *active_memctx = NULL; struct tegra_drm_file *fpriv = file->driver_priv; struct drm_tegra_channel_submit *args = data; struct tegra_drm_submit_data *job_data; struct drm_syncobj *syncobj = NULL; + struct dma_fence *in_fence = NULL; struct tegra_drm_context *context; + u32 num_prefence_waits = 0; struct host1x_job *job; struct gather_bo *bo; u32 i; @@ -546,26 +618,15 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, } if (args->syncobj_in) { - struct dma_fence *fence; - long wait_err; - - err = drm_syncobj_find_fence(file, args->syncobj_in, 0, 0, &fence); + err = drm_syncobj_find_fence(file, args->syncobj_in, 0, 0, &in_fence); if (err) { SUBMIT_ERR(context, "invalid syncobj_in '%#x'", args->syncobj_in); goto unlock; } - wait_err = dma_fence_wait_timeout(fence, true, msecs_to_jiffies(10000)); - dma_fence_put(fence); - if (wait_err == 0) { - SUBMIT_ERR(context, "wait for syncobj_in timed out"); - err = -ETIMEDOUT; - goto unlock; - } else if (wait_err < 0) { - /* In practice, -ERESTARTSYS */ - err = wait_err; - goto unlock; - } + err = submit_handle_in_fence(context, host1x, in_fence, &num_prefence_waits); + if (err) + goto put_in_fence; } if (args->syncobj_out) { @@ -573,14 +634,14 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, if (!syncobj) { SUBMIT_ERR(context, "invalid syncobj_out '%#x'", args->syncobj_out); err = -ENOENT; - goto unlock; + goto put_in_fence; } } /* Allocate gather BO and copy gather words in. */ err = submit_copy_gather_data(&bo, drm->dev, context, args); if (err) - goto unlock; + goto put_in_fence; job_data = kzalloc_obj(*job_data); if (!job_data) { @@ -595,12 +656,20 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, goto free_job_data; /* Allocate host1x_job and add gathers and waits to it. */ - job = submit_create_job(context, bo, args, job_data, &fpriv->syncpoints); + job = submit_create_job(context, bo, args, job_data, &fpriv->syncpoints, in_fence, + num_prefence_waits); if (IS_ERR(job)) { err = PTR_ERR(job); goto free_job_data; } + /* + * The prefence has been recorded as plain syncpoint waits, so the fence + * itself is not needed anymore. + */ + dma_fence_put(in_fence); + in_fence = NULL; + /* Map gather data for Host1x. */ err = host1x_job_pin(job, context->client->base.dev); if (err) { @@ -702,6 +771,8 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, kfree(job_data); put_bo: gather_bo_put(&bo->base); +put_in_fence: + dma_fence_put(in_fence); unlock: if (syncobj) drm_syncobj_put(syncobj); diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 8f21f3a44832..b17472103e91 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -986,8 +986,8 @@ struct drm_tegra_channel_submit { /** * @syncobj_in: [in] * - * Handle for DRM syncobj that will be waited before submission. - * Ignored if zero. + * Handle for DRM syncobj that will be waited for before the job + * executes. Ignored if zero. */ __u32 syncobj_in;