summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/imagination/pvr_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/imagination/pvr_queue.c')
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c173
1 files changed, 114 insertions, 59 deletions
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index dd88949f6194..941c017399fc 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -10,6 +10,7 @@
#include "pvr_drv.h"
#include "pvr_job.h"
#include "pvr_queue.h"
+#include "pvr_trace.h"
#include "pvr_vm.h"
#include "pvr_rogue_fwif_client.h"
@@ -178,10 +179,35 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = {
};
/**
+ * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO.
+ * @f: The dma_fence to check.
+ *
+ * Return:
+ * * true if the dma_fence is backed by a UFO, or
+ * * false otherwise.
+ */
+static inline bool
+pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
+{
+ /*
+ * Currently the only dma_fence backed by a UFO object is the job fence,
+ * e.g. pvr_job::done_fence, wrapped by a pvr_queue_fence object.
+ */
+ return f && f->ops == &pvr_queue_job_fence_ops;
+}
+
+/**
* to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is
- * backed by a UFO.
+ * already backed by a UFO.
* @f: The dma_fence to turn into a pvr_queue_fence.
*
+ * This could be called on:
+ * - a job fence directly, in which case it simply returns the containing pvr_queue_fence;
+ * - a drm_sched_fence's scheduled or finished fence, in which case it will first try to follow
+ * the parent pointer to find the job fence (note that the parent pointer is initialized
+ * only after the run_job() callback is called on the drm_sched_fence's owning job);
+ * - any other dma_fence, in which case it will return NULL.
+ *
* Return:
* * A non-NULL pvr_queue_fence object if the dma_fence is backed by a UFO, or
* * NULL otherwise.
@@ -194,7 +220,7 @@ to_pvr_queue_job_fence(struct dma_fence *f)
if (sched_fence)
f = sched_fence->parent;
- if (f && f->ops == &pvr_queue_job_fence_ops)
+ if (pvr_queue_fence_is_ufo_backed(f))
return container_of(f, struct pvr_queue_fence, base);
return NULL;
@@ -349,11 +375,23 @@ static u32 ufo_cmds_size(u32 elem_count)
static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count)
{
- /* One UFO cmd for the fence signaling, one UFO cmd per native fence native,
- * and a command for the job itself.
+ /*
+ * One UFO command per native fence this job will be waiting on (unless any are
+ * signaled by the time the job is submitted), plus a command for the job itself,
+ * plus one UFO command for the fence signaling.
*/
- return ufo_cmds_size(1) + ufo_cmds_size(ufo_wait_count) +
- pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len);
+ return ufo_cmds_size(ufo_wait_count) +
+ pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len) +
+ ufo_cmds_size(1);
+}
+
+static bool
+is_paired_job_fence(struct dma_fence *fence, struct pvr_job *job)
+{
+ /* This assumes "fence" is one of "job"'s drm_sched_job::dependencies */
+ return job->type == DRM_PVR_JOB_TYPE_FRAGMENT &&
+ job->paired_job &&
+ &job->paired_job->base.s_fence->scheduled == fence;
}
/**
@@ -371,6 +409,17 @@ static unsigned long job_count_remaining_native_deps(struct pvr_job *job)
xa_for_each(&job->base.dependencies, index, fence) {
struct pvr_queue_fence *jfence;
+ if (is_paired_job_fence(fence, job)) {
+ /*
+ * A fence between paired jobs won't resolve to a pvr_queue_fence (i.e.
+ * be backed by a UFO) until the jobs have been submitted, together.
+ * The submitting code will insert a partial render fence command for this.
+ */
+ WARN_ON(dma_fence_is_signaled(fence));
+ remaining_count++;
+ continue;
+ }
+
jfence = to_pvr_queue_job_fence(fence);
if (!jfence)
continue;
@@ -468,29 +517,37 @@ pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job)
}
static struct dma_fence *
-pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job)
+pvr_queue_get_paired_frag_job_dep(struct pvr_job *job)
{
struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
job->paired_job : NULL;
+ struct pvr_queue *frag_queue = frag_job ? frag_job->ctx->queues.fragment : NULL;
struct dma_fence *f;
unsigned long index;
if (!frag_job)
return NULL;
+ /* Have the geometry job wait on the paired fragment job's dependencies as well. */
xa_for_each(&frag_job->base.dependencies, index, f) {
/* Skip already signaled fences. */
if (dma_fence_is_signaled(f))
continue;
- /* Skip our own fence. */
+ /*
+ * The paired job fence won't be signaled until both jobs have
+ * been submitted, so we can't wait on it to schedule them.
+ */
if (f == &job->base.s_fence->scheduled)
continue;
return dma_fence_get(f);
}
- return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity);
+ /* Initialize the paired fragment job's done_fence, so we can signal it. */
+ pvr_queue_job_fence_init(frag_job->done_fence, frag_queue);
+
+ return pvr_queue_get_job_cccb_fence(frag_queue, frag_job);
}
/**
@@ -509,32 +566,25 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity);
struct dma_fence *internal_dep = NULL;
- /*
- * Initialize the done_fence, so we can signal it. This must be done
- * here because otherwise by the time of run_job() the job will end up
- * in the pending list without a valid fence.
- */
if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
/*
- * This will be called on a paired fragment job after being
- * submitted to firmware. We can tell if this is the case and
- * bail early from whether run_job() has been called on the
- * geometry job, which would issue a pm ref.
+ * This will be called on a paired fragment job after being submitted
+ * to the firmware as part of the paired geometry job's submission.
+ * We can tell if this is the case and bail early from whether run_job()
+ * has been called on the geometry job, which would issue a pm ref on
+ * this job as well.
*/
- if (job->paired_job->has_pm_ref)
+ if (job->has_pm_ref)
return NULL;
-
- /*
- * In this case we need to use the job's own ctx to initialise
- * the done_fence. The other steps are done in the ctx of the
- * paired geometry job.
- */
- pvr_queue_job_fence_init(job->done_fence,
- job->ctx->queues.fragment);
- } else {
- pvr_queue_job_fence_init(job->done_fence, queue);
}
+ /*
+ * Initialize the done_fence, so we can signal it. This must be done
+ * here because otherwise by the time of run_job() the job will end up
+ * in the pending list without a valid fence.
+ */
+ pvr_queue_job_fence_init(job->done_fence, queue);
+
/* CCCB fence is used to make sure we have enough space in the CCCB to
* submit our commands.
*/
@@ -555,7 +605,7 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
/* The paired job fence should come last, when everything else is ready. */
if (!internal_dep)
- internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job);
+ internal_dep = pvr_queue_get_paired_frag_job_dep(job);
return internal_dep;
}
@@ -630,11 +680,7 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
if (!jfence)
continue;
- /* Skip the partial render fence, we will place it at the end. */
- if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job &&
- &job->paired_job->base.s_fence->scheduled == fence)
- continue;
-
+ /* Some dependencies might have been signaled since prepare_job() */
if (dma_fence_is_signaled(&jfence->base))
continue;
@@ -649,8 +695,13 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
}
}
- /* Partial render fence goes last. */
if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
+ /*
+ * The loop above will only process dependencies backed by a UFO i.e. with
+ * a valid parent fence assigned, but the paired job dependency won't have
+ * one until both jobs have been submitted. Access the parent fence directly
+ * here instead, submitting it last as partial render fence.
+ */
jfence = to_pvr_queue_job_fence(job->paired_job->done_fence);
if (!WARN_ON(!jfence)) {
pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj,
@@ -675,11 +726,13 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1;
}
+ trace_pvr_job_submit_fw(job);
+
/* Submit job to FW */
pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
job->id, job->id);
- /* Signal the job fence. */
+ /* Update command to signal the job fence. */
pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, &ufos[0].addr);
ufos[0].value = job->done_fence->seqno;
pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_UPDATE,
@@ -709,10 +762,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
}
/* The only kind of jobs that can be paired are geometry and fragment, and
- * we bail out early if we see a fragment job that's paired with a geomtry
- * job.
- * Paired jobs must also target the same context and point to the same
- * HWRT.
+ * we bail out early if we see a fragment job that's paired with a geometry job.
+ * Paired jobs must also target the same context and point to the same HWRT.
*/
if (WARN_ON(job->paired_job &&
(job->type != DRM_PVR_JOB_TYPE_GEOMETRY ||
@@ -803,7 +854,9 @@ static void pvr_queue_start(struct pvr_queue *queue)
* the scheduler, and re-assign parent fences in the middle.
*
* Return:
- * * DRM_GPU_SCHED_STAT_RESET.
+ * *%DRM_GPU_SCHED_STAT_NO_HANG if the job fence has already been
+ * signaled, or
+ * *%DRM_GPU_SCHED_STAT_RESET otherwise.
*/
static enum drm_gpu_sched_stat
pvr_queue_timedout_job(struct drm_sched_job *s_job)
@@ -814,6 +867,9 @@ pvr_queue_timedout_job(struct drm_sched_job *s_job)
struct pvr_job *job;
u32 job_count = 0;
+ if (dma_fence_is_signaled(s_job->s_fence->parent))
+ return DRM_GPU_SCHED_STAT_NO_HANG;
+
dev_err(sched->dev, "Job timeout\n");
/* Before we stop the scheduler, make sure the queue is out of any list, so
@@ -882,16 +938,16 @@ static const struct drm_sched_backend_ops pvr_queue_sched_ops = {
};
/**
- * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO object
+ * pvr_queue_fence_is_native() - Check if a dma_fence is native to this driver.
* @f: Fence to test.
*
- * A UFO-backed fence is a fence that can be signaled or waited upon FW-side.
- * pvr_job::done_fence objects are backed by the timeline UFO attached to the queue
- * they are pushed to, but those fences are not directly exposed to the outside
- * world, so we also need to check if the fence we're being passed is a
- * drm_sched_fence that was coming from our driver.
+ * Check if the fence we're being passed is a drm_sched_fence that is coming from this driver.
+ *
+ * It may be a UFO-backed fence i.e. a fence that can be signaled or waited upon FW-side,
+ * such as pvr_job::done_fence objects that are backed by the timeline UFO attached to the queue
+ * they are pushed to.
*/
-bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
+bool pvr_queue_fence_is_native(struct dma_fence *f)
{
struct drm_sched_fence *sched_fence = f ? to_drm_sched_fence(f) : NULL;
@@ -899,10 +955,7 @@ bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
sched_fence->sched->ops == &pvr_queue_sched_ops)
return true;
- if (f && f->ops == &pvr_queue_job_fence_ops)
- return true;
-
- return false;
+ return pvr_queue_fence_is_ufo_backed(f);
}
/**
@@ -934,9 +987,8 @@ pvr_queue_signal_done_fences(struct pvr_queue *queue)
}
/**
- * pvr_queue_check_job_waiting_for_cccb_space() - Check if the job waiting for CCCB space
- * can be unblocked
- * pushed to the CCCB
+ * pvr_queue_check_job_waiting_for_cccb_space() - Check if a job waiting for CCCB space
+ * can be unblocked and pushed to the CCCB.
* @queue: Queue to check
*
* If we have a job waiting for CCCB, and this job now fits in the CCCB, we signal
@@ -1149,6 +1201,8 @@ void pvr_queue_job_cleanup(struct pvr_job *job)
if (job->base.s_fence)
drm_sched_job_cleanup(&job->base);
+
+ trace_pvr_job_done(job);
}
/**
@@ -1228,7 +1282,6 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
const struct drm_sched_init_args sched_args = {
.ops = &pvr_queue_sched_ops,
.submit_wq = pvr_dev->sched_wq,
- .num_rqs = 1,
.credit_limit = 64 * 1024,
.hang_limit = 1,
.timeout = msecs_to_jiffies(500),
@@ -1386,11 +1439,12 @@ void pvr_queue_kill(struct pvr_queue *queue)
/**
* pvr_queue_destroy() - Destroy a queue.
* @queue: The queue to destroy.
+ * @cleanup_queue_entity: Whether to cleanup the queue entity.
*
* Cleanup the queue and free the resources attached to it. Should be
* called from the context release function.
*/
-void pvr_queue_destroy(struct pvr_queue *queue)
+void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity)
{
if (!queue)
return;
@@ -1400,7 +1454,8 @@ void pvr_queue_destroy(struct pvr_queue *queue)
mutex_unlock(&queue->ctx->pvr_dev->queues.lock);
drm_sched_fini(&queue->scheduler);
- drm_sched_entity_fini(&queue->entity);
+ if (cleanup_queue_entity)
+ drm_sched_entity_fini(&queue->entity);
if (WARN_ON(queue->last_queued_job_scheduled_fence))
dma_fence_put(queue->last_queued_job_scheduled_fence);