linux/drivers/gpu/drm/vc4/vc4_perfmon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2018 Broadcom
   4 */
   5
   6/**
   7 * DOC: VC4 V3D performance monitor module
   8 *
   9 * The V3D block provides 16 hardware counters which can count various events.
  10 */
  11
  12#include "vc4_drv.h"
  13#include "vc4_regs.h"
  14
  15#define VC4_PERFMONID_MIN       1
  16#define VC4_PERFMONID_MAX       U32_MAX
  17
  18void vc4_perfmon_get(struct vc4_perfmon *perfmon)
  19{
  20        if (perfmon)
  21                refcount_inc(&perfmon->refcnt);
  22}
  23
  24void vc4_perfmon_put(struct vc4_perfmon *perfmon)
  25{
  26        if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
  27                kfree(perfmon);
  28}
  29
  30void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon)
  31{
  32        unsigned int i;
  33        u32 mask;
  34
  35        if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
  36                return;
  37
  38        for (i = 0; i < perfmon->ncounters; i++)
  39                V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]);
  40
  41        mask = GENMASK(perfmon->ncounters - 1, 0);
  42        V3D_WRITE(V3D_PCTRC, mask);
  43        V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask);
  44        vc4->active_perfmon = perfmon;
  45}
  46
  47void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon,
  48                      bool capture)
  49{
  50        unsigned int i;
  51
  52        if (WARN_ON_ONCE(!vc4->active_perfmon ||
  53                         perfmon != vc4->active_perfmon))
  54                return;
  55
  56        if (capture) {
  57                for (i = 0; i < perfmon->ncounters; i++)
  58                        perfmon->counters[i] += V3D_READ(V3D_PCTR(i));
  59        }
  60
  61        V3D_WRITE(V3D_PCTRE, 0);
  62        vc4->active_perfmon = NULL;
  63}
  64
  65struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id)
  66{
  67        struct vc4_perfmon *perfmon;
  68
  69        mutex_lock(&vc4file->perfmon.lock);
  70        perfmon = idr_find(&vc4file->perfmon.idr, id);
  71        vc4_perfmon_get(perfmon);
  72        mutex_unlock(&vc4file->perfmon.lock);
  73
  74        return perfmon;
  75}
  76
  77void vc4_perfmon_open_file(struct vc4_file *vc4file)
  78{
  79        mutex_init(&vc4file->perfmon.lock);
  80        idr_init(&vc4file->perfmon.idr);
  81}
  82
  83static int vc4_perfmon_idr_del(int id, void *elem, void *data)
  84{
  85        struct vc4_perfmon *perfmon = elem;
  86
  87        vc4_perfmon_put(perfmon);
  88
  89        return 0;
  90}
  91
  92void vc4_perfmon_close_file(struct vc4_file *vc4file)
  93{
  94        mutex_lock(&vc4file->perfmon.lock);
  95        idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
  96        idr_destroy(&vc4file->perfmon.idr);
  97        mutex_unlock(&vc4file->perfmon.lock);
  98}
  99
 100int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
 101                             struct drm_file *file_priv)
 102{
 103        struct vc4_file *vc4file = file_priv->driver_priv;
 104        struct drm_vc4_perfmon_create *req = data;
 105        struct vc4_perfmon *perfmon;
 106        unsigned int i;
 107        int ret;
 108
 109        /* Number of monitored counters cannot exceed HW limits. */
 110        if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS ||
 111            !req->ncounters)
 112                return -EINVAL;
 113
 114        /* Make sure all events are valid. */
 115        for (i = 0; i < req->ncounters; i++) {
 116                if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS)
 117                        return -EINVAL;
 118        }
 119
 120        perfmon = kzalloc(sizeof(*perfmon) + (req->ncounters * sizeof(u64)),
 121                          GFP_KERNEL);
 122        if (!perfmon)
 123                return -ENOMEM;
 124
 125        for (i = 0; i < req->ncounters; i++)
 126                perfmon->events[i] = req->events[i];
 127
 128        perfmon->ncounters = req->ncounters;
 129
 130        refcount_set(&perfmon->refcnt, 1);
 131
 132        mutex_lock(&vc4file->perfmon.lock);
 133        ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN,
 134                        VC4_PERFMONID_MAX, GFP_KERNEL);
 135        mutex_unlock(&vc4file->perfmon.lock);
 136
 137        if (ret < 0) {
 138                kfree(perfmon);
 139                return ret;
 140        }
 141
 142        req->id = ret;
 143        return 0;
 144}
 145
 146int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
 147                              struct drm_file *file_priv)
 148{
 149        struct vc4_file *vc4file = file_priv->driver_priv;
 150        struct drm_vc4_perfmon_destroy *req = data;
 151        struct vc4_perfmon *perfmon;
 152
 153        mutex_lock(&vc4file->perfmon.lock);
 154        perfmon = idr_remove(&vc4file->perfmon.idr, req->id);
 155        mutex_unlock(&vc4file->perfmon.lock);
 156
 157        if (!perfmon)
 158                return -EINVAL;
 159
 160        vc4_perfmon_put(perfmon);
 161        return 0;
 162}
 163
 164int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
 165                                 struct drm_file *file_priv)
 166{
 167        struct vc4_file *vc4file = file_priv->driver_priv;
 168        struct drm_vc4_perfmon_get_values *req = data;
 169        struct vc4_perfmon *perfmon;
 170        int ret;
 171
 172        mutex_lock(&vc4file->perfmon.lock);
 173        perfmon = idr_find(&vc4file->perfmon.idr, req->id);
 174        vc4_perfmon_get(perfmon);
 175        mutex_unlock(&vc4file->perfmon.lock);
 176
 177        if (!perfmon)
 178                return -EINVAL;
 179
 180        if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters,
 181                         perfmon->ncounters * sizeof(u64)))
 182                ret = -EFAULT;
 183        else
 184                ret = 0;
 185
 186        vc4_perfmon_put(perfmon);
 187        return ret;
 188}
 189