drm/v3d: Implement show_fdinfo() callback for GPU usage stats
authorMaíra Canal <mcanal@igalia.com>
Tue, 5 Sep 2023 21:06:34 +0000 (18:06 -0300)
committerMaíra Canal <mcanal@igalia.com>
Mon, 6 Nov 2023 13:09:23 +0000 (10:09 -0300)
This patch exposes the accumulated amount of active time per client
through the fdinfo infrastructure. The amount of active time is exposed
for each V3D queue: BIN, RENDER, CSD, TFU and CACHE_CLEAN.

In order to calculate the amount of active time per client, a CPU clock
is used through the function local_clock(). The point where the jobs has
started is marked and is finally compared with the time that the job had
finished.

Moreover, the number of jobs submitted to each queue is also exposed on
fdinfo through the identifier "v3d-jobs-<queue>".

Co-developed-by: Jose Maria Casanova Crespo <jmcasanova@igalia.com>
Signed-off-by: Jose Maria Casanova Crespo <jmcasanova@igalia.com>
Signed-off-by: Maíra Canal <mcanal@igalia.com>
Acked-by: Jose Maria Casanova Crespo <jmcasanova@igalia.com>
Reviewed-by: Melissa Wen <mwen@igalia.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230905213416.1290219-3-mcanal@igalia.com
drivers/gpu/drm/v3d/v3d_drv.c
drivers/gpu/drm/v3d/v3d_drv.h
drivers/gpu/drm/v3d/v3d_gem.c
drivers/gpu/drm/v3d/v3d_irq.c
drivers/gpu/drm/v3d/v3d_sched.c

index 1ab46bdf8ad717c1ca5b8783ee71ed5dfb247312..82984b4df2a2ca10da02711758ebaa79d788f74f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/sched/clock.h>
 #include <linux/reset.h>
 
 #include <drm/drm_drv.h>
@@ -111,6 +112,10 @@ v3d_open(struct drm_device *dev, struct drm_file *file)
        v3d_priv->v3d = v3d;
 
        for (i = 0; i < V3D_MAX_QUEUES; i++) {
+               v3d_priv->enabled_ns[i] = 0;
+               v3d_priv->start_ns[i] = 0;
+               v3d_priv->jobs_sent[i] = 0;
+
                sched = &v3d->queue[i].sched;
                drm_sched_entity_init(&v3d_priv->sched_entity[i],
                                      DRM_SCHED_PRIORITY_NORMAL, &sched,
@@ -136,7 +141,35 @@ v3d_postclose(struct drm_device *dev, struct drm_file *file)
        kfree(v3d_priv);
 }
 
-DEFINE_DRM_GEM_FOPS(v3d_drm_fops);
+static void v3d_show_fdinfo(struct drm_printer *p, struct drm_file *file)
+{
+       struct v3d_file_priv *file_priv = file->driver_priv;
+       u64 timestamp = local_clock();
+       enum v3d_queue queue;
+
+       for (queue = 0; queue < V3D_MAX_QUEUES; queue++) {
+               /* Note that, in case of a GPU reset, the time spent during an
+                * attempt of executing the job is not computed in the runtime.
+                */
+               drm_printf(p, "drm-engine-%s: \t%llu ns\n",
+                          v3d_queue_to_string(queue),
+                          file_priv->start_ns[queue] ? file_priv->enabled_ns[queue]
+                                                     + timestamp - file_priv->start_ns[queue]
+                                                     : file_priv->enabled_ns[queue]);
+
+               /* Note that we only count jobs that completed. Therefore, jobs
+                * that were resubmitted due to a GPU reset are not computed.
+                */
+               drm_printf(p, "v3d-jobs-%s: \t%llu jobs\n",
+                          v3d_queue_to_string(queue), file_priv->jobs_sent[queue]);
+       }
+}
+
+static const struct file_operations v3d_drm_fops = {
+       .owner = THIS_MODULE,
+       DRM_GEM_FOPS,
+       .show_fdinfo = drm_show_fdinfo,
+};
 
 /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP
  * protection between clients.  Note that render nodes would be
@@ -176,6 +209,7 @@ static const struct drm_driver v3d_drm_driver = {
        .ioctls = v3d_drm_ioctls,
        .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls),
        .fops = &v3d_drm_fops,
+       .show_fdinfo = v3d_show_fdinfo,
 
        .name = DRIVER_NAME,
        .desc = DRIVER_DESC,
index 106454f28956b96336cd729251267ca9d6107334..8f9d93239a174d4322a346ad8e56cc238754f911 100644 (file)
@@ -21,6 +21,18 @@ struct reset_control;
 
 #define V3D_MAX_QUEUES (V3D_CACHE_CLEAN + 1)
 
+static inline char *v3d_queue_to_string(enum v3d_queue queue)
+{
+       switch (queue) {
+       case V3D_BIN: return "bin";
+       case V3D_RENDER: return "render";
+       case V3D_TFU: return "tfu";
+       case V3D_CSD: return "csd";
+       case V3D_CACHE_CLEAN: return "cache_clean";
+       }
+       return "UNKNOWN";
+}
+
 struct v3d_queue_state {
        struct drm_gpu_scheduler sched;
 
@@ -167,6 +179,12 @@ struct v3d_file_priv {
        } perfmon;
 
        struct drm_sched_entity sched_entity[V3D_MAX_QUEUES];
+
+       u64 start_ns[V3D_MAX_QUEUES];
+
+       u64 enabled_ns[V3D_MAX_QUEUES];
+
+       u64 jobs_sent[V3D_MAX_QUEUES];
 };
 
 struct v3d_bo {
@@ -238,6 +256,11 @@ struct v3d_job {
         */
        struct v3d_perfmon *perfmon;
 
+       /* File descriptor of the process that submitted the job that could be used
+        * for collecting stats by process of GPU usage.
+        */
+       struct drm_file *file;
+
        /* Callback for the freeing of the job on refcount going to 0. */
        void (*free)(struct kref *ref);
 };
index 5688d46d30e69bd753267aac435a4ab3c090602c..a33e90f29bd52f86c7d77f5042abeb75ad245f62 100644 (file)
@@ -415,6 +415,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
        job = *container;
        job->v3d = v3d;
        job->free = free;
+       job->file = file_priv;
 
        ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue],
                                 v3d_priv);
index 9705e0cb576d8332cf79f72d576ece35bf6b66af..ba390b782103ac1d585797a610b83a1f85efaa76 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <linux/platform_device.h>
+#include <linux/sched/clock.h>
 
 #include "v3d_drv.h"
 #include "v3d_regs.h"
@@ -101,6 +102,11 @@ v3d_irq(int irq, void *arg)
        if (intsts & V3D_INT_FLDONE) {
                struct v3d_fence *fence =
                        to_v3d_fence(v3d->bin_job->base.irq_fence);
+               struct v3d_file_priv *file = v3d->bin_job->base.file->driver_priv;
+
+               file->enabled_ns[V3D_BIN] += local_clock() - file->start_ns[V3D_BIN];
+               file->jobs_sent[V3D_BIN]++;
+               file->start_ns[V3D_BIN] = 0;
 
                trace_v3d_bcl_irq(&v3d->drm, fence->seqno);
                dma_fence_signal(&fence->base);
@@ -110,6 +116,11 @@ v3d_irq(int irq, void *arg)
        if (intsts & V3D_INT_FRDONE) {
                struct v3d_fence *fence =
                        to_v3d_fence(v3d->render_job->base.irq_fence);
+               struct v3d_file_priv *file = v3d->render_job->base.file->driver_priv;
+
+               file->enabled_ns[V3D_RENDER] += local_clock() - file->start_ns[V3D_RENDER];
+               file->jobs_sent[V3D_RENDER]++;
+               file->start_ns[V3D_RENDER] = 0;
 
                trace_v3d_rcl_irq(&v3d->drm, fence->seqno);
                dma_fence_signal(&fence->base);
@@ -119,6 +130,11 @@ v3d_irq(int irq, void *arg)
        if (intsts & V3D_INT_CSDDONE(v3d->ver)) {
                struct v3d_fence *fence =
                        to_v3d_fence(v3d->csd_job->base.irq_fence);
+               struct v3d_file_priv *file = v3d->csd_job->base.file->driver_priv;
+
+               file->enabled_ns[V3D_CSD] += local_clock() - file->start_ns[V3D_CSD];
+               file->jobs_sent[V3D_CSD]++;
+               file->start_ns[V3D_CSD] = 0;
 
                trace_v3d_csd_irq(&v3d->drm, fence->seqno);
                dma_fence_signal(&fence->base);
@@ -155,6 +171,11 @@ v3d_hub_irq(int irq, void *arg)
        if (intsts & V3D_HUB_INT_TFUC) {
                struct v3d_fence *fence =
                        to_v3d_fence(v3d->tfu_job->base.irq_fence);
+               struct v3d_file_priv *file = v3d->tfu_job->base.file->driver_priv;
+
+               file->enabled_ns[V3D_TFU] += local_clock() - file->start_ns[V3D_TFU];
+               file->jobs_sent[V3D_TFU]++;
+               file->start_ns[V3D_TFU] = 0;
 
                trace_v3d_tfu_irq(&v3d->drm, fence->seqno);
                dma_fence_signal(&fence->base);
index f59b63e51b14b944bcdb199bbfa65cf783b78245..19afceacda837f1037f6d9125744dcf0eb417ad8 100644 (file)
@@ -18,6 +18,7 @@
  * semaphores to interlock between them.
  */
 
+#include <linux/sched/clock.h>
 #include <linux/kthread.h>
 
 #include "v3d_drv.h"
@@ -76,6 +77,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
 {
        struct v3d_bin_job *job = to_bin_job(sched_job);
        struct v3d_dev *v3d = job->base.v3d;
+       struct v3d_file_priv *file = job->base.file->driver_priv;
        struct drm_device *dev = &v3d->drm;
        struct dma_fence *fence;
        unsigned long irqflags;
@@ -107,6 +109,8 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
        trace_v3d_submit_cl(dev, false, to_v3d_fence(fence)->seqno,
                            job->start, job->end);
 
+       file->start_ns[V3D_BIN] = local_clock();
+
        v3d_switch_perfmon(v3d, &job->base);
 
        /* Set the current and end address of the control list.
@@ -131,6 +135,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
 {
        struct v3d_render_job *job = to_render_job(sched_job);
        struct v3d_dev *v3d = job->base.v3d;
+       struct v3d_file_priv *file = job->base.file->driver_priv;
        struct drm_device *dev = &v3d->drm;
        struct dma_fence *fence;
 
@@ -158,6 +163,8 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
        trace_v3d_submit_cl(dev, true, to_v3d_fence(fence)->seqno,
                            job->start, job->end);
 
+       file->start_ns[V3D_RENDER] = local_clock();
+
        v3d_switch_perfmon(v3d, &job->base);
 
        /* XXX: Set the QCFG */
@@ -176,6 +183,7 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
 {
        struct v3d_tfu_job *job = to_tfu_job(sched_job);
        struct v3d_dev *v3d = job->base.v3d;
+       struct v3d_file_priv *file = job->base.file->driver_priv;
        struct drm_device *dev = &v3d->drm;
        struct dma_fence *fence;
 
@@ -190,6 +198,8 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
 
        trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno);
 
+       file->start_ns[V3D_TFU] = local_clock();
+
        V3D_WRITE(V3D_TFU_IIA(v3d->ver), job->args.iia);
        V3D_WRITE(V3D_TFU_IIS(v3d->ver), job->args.iis);
        V3D_WRITE(V3D_TFU_ICA(v3d->ver), job->args.ica);
@@ -215,6 +225,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
 {
        struct v3d_csd_job *job = to_csd_job(sched_job);
        struct v3d_dev *v3d = job->base.v3d;
+       struct v3d_file_priv *file = job->base.file->driver_priv;
        struct drm_device *dev = &v3d->drm;
        struct dma_fence *fence;
        int i, csd_cfg0_reg, csd_cfg_reg_count;
@@ -233,6 +244,8 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
 
        trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno);
 
+       file->start_ns[V3D_CSD] = local_clock();
+
        v3d_switch_perfmon(v3d, &job->base);
 
        csd_cfg0_reg = V3D_CSD_QUEUED_CFG0(v3d->ver);
@@ -250,9 +263,16 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job)
 {
        struct v3d_job *job = to_v3d_job(sched_job);
        struct v3d_dev *v3d = job->v3d;
+       struct v3d_file_priv *file = job->file->driver_priv;
+
+       file->start_ns[V3D_CACHE_CLEAN] = local_clock();
 
        v3d_clean_caches(v3d);
 
+       file->enabled_ns[V3D_CACHE_CLEAN] += local_clock() - file->start_ns[V3D_CACHE_CLEAN];
+       file->jobs_sent[V3D_CACHE_CLEAN]++;
+       file->start_ns[V3D_CACHE_CLEAN] = 0;
+
        return NULL;
 }