root/drivers/gpu/drm/msm/msm_perf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wait_sample
  2. refill_buf
  3. perf_read
  4. perf_open
  5. perf_release
  6. msm_perf_debugfs_init
  7. msm_perf_debugfs_cleanup

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2013 Red Hat
   4  * Author: Rob Clark <robdclark@gmail.com>
   5  */
   6 
   7 /* For profiling, userspace can:
   8  *
   9  *   tail -f /sys/kernel/debug/dri/<minor>/gpu
  10  *
  11  * This will enable performance counters/profiling to track the busy time
  12  * and any gpu specific performance counters that are supported.
  13  */
  14 
  15 #ifdef CONFIG_DEBUG_FS
  16 
  17 #include <linux/debugfs.h>
  18 #include <linux/uaccess.h>
  19 
  20 #include <drm/drm_file.h>
  21 
  22 #include "msm_drv.h"
  23 #include "msm_gpu.h"
  24 
  25 struct msm_perf_state {
  26         struct drm_device *dev;
  27 
  28         bool open;
  29         int cnt;
  30         struct mutex read_lock;
  31 
  32         char buf[256];
  33         int buftot, bufpos;
  34 
  35         unsigned long next_jiffies;
  36 };
  37 
  38 #define SAMPLE_TIME (HZ/4)
  39 
  40 /* wait for next sample time: */
  41 static int wait_sample(struct msm_perf_state *perf)
  42 {
  43         unsigned long start_jiffies = jiffies;
  44 
  45         if (time_after(perf->next_jiffies, start_jiffies)) {
  46                 unsigned long remaining_jiffies =
  47                         perf->next_jiffies - start_jiffies;
  48                 int ret = schedule_timeout_interruptible(remaining_jiffies);
  49                 if (ret > 0) {
  50                         /* interrupted */
  51                         return -ERESTARTSYS;
  52                 }
  53         }
  54         perf->next_jiffies += SAMPLE_TIME;
  55         return 0;
  56 }
  57 
  58 static int refill_buf(struct msm_perf_state *perf)
  59 {
  60         struct msm_drm_private *priv = perf->dev->dev_private;
  61         struct msm_gpu *gpu = priv->gpu;
  62         char *ptr = perf->buf;
  63         int rem = sizeof(perf->buf);
  64         int i, n;
  65 
  66         if ((perf->cnt++ % 32) == 0) {
  67                 /* Header line: */
  68                 n = snprintf(ptr, rem, "%%BUSY");
  69                 ptr += n;
  70                 rem -= n;
  71 
  72                 for (i = 0; i < gpu->num_perfcntrs; i++) {
  73                         const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
  74                         n = snprintf(ptr, rem, "\t%s", perfcntr->name);
  75                         ptr += n;
  76                         rem -= n;
  77                 }
  78         } else {
  79                 /* Sample line: */
  80                 uint32_t activetime = 0, totaltime = 0;
  81                 uint32_t cntrs[5];
  82                 uint32_t val;
  83                 int ret;
  84 
  85                 /* sleep until next sample time: */
  86                 ret = wait_sample(perf);
  87                 if (ret)
  88                         return ret;
  89 
  90                 ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,
  91                                 ARRAY_SIZE(cntrs), cntrs);
  92                 if (ret < 0)
  93                         return ret;
  94 
  95                 val = totaltime ? 1000 * activetime / totaltime : 0;
  96                 n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);
  97                 ptr += n;
  98                 rem -= n;
  99 
 100                 for (i = 0; i < ret; i++) {
 101                         /* cycle counters (I think).. convert to MHz.. */
 102                         val = cntrs[i] / 10000;
 103                         n = snprintf(ptr, rem, "\t%5d.%02d",
 104                                         val / 100, val % 100);
 105                         ptr += n;
 106                         rem -= n;
 107                 }
 108         }
 109 
 110         n = snprintf(ptr, rem, "\n");
 111         ptr += n;
 112         rem -= n;
 113 
 114         perf->bufpos = 0;
 115         perf->buftot = ptr - perf->buf;
 116 
 117         return 0;
 118 }
 119 
 120 static ssize_t perf_read(struct file *file, char __user *buf,
 121                 size_t sz, loff_t *ppos)
 122 {
 123         struct msm_perf_state *perf = file->private_data;
 124         int n = 0, ret = 0;
 125 
 126         mutex_lock(&perf->read_lock);
 127 
 128         if (perf->bufpos >= perf->buftot) {
 129                 ret = refill_buf(perf);
 130                 if (ret)
 131                         goto out;
 132         }
 133 
 134         n = min((int)sz, perf->buftot - perf->bufpos);
 135         if (copy_to_user(buf, &perf->buf[perf->bufpos], n)) {
 136                 ret = -EFAULT;
 137                 goto out;
 138         }
 139 
 140         perf->bufpos += n;
 141         *ppos += n;
 142 
 143 out:
 144         mutex_unlock(&perf->read_lock);
 145         if (ret)
 146                 return ret;
 147         return n;
 148 }
 149 
 150 static int perf_open(struct inode *inode, struct file *file)
 151 {
 152         struct msm_perf_state *perf = inode->i_private;
 153         struct drm_device *dev = perf->dev;
 154         struct msm_drm_private *priv = dev->dev_private;
 155         struct msm_gpu *gpu = priv->gpu;
 156         int ret = 0;
 157 
 158         mutex_lock(&dev->struct_mutex);
 159 
 160         if (perf->open || !gpu) {
 161                 ret = -EBUSY;
 162                 goto out;
 163         }
 164 
 165         file->private_data = perf;
 166         perf->open = true;
 167         perf->cnt = 0;
 168         perf->buftot = 0;
 169         perf->bufpos = 0;
 170         msm_gpu_perfcntr_start(gpu);
 171         perf->next_jiffies = jiffies + SAMPLE_TIME;
 172 
 173 out:
 174         mutex_unlock(&dev->struct_mutex);
 175         return ret;
 176 }
 177 
 178 static int perf_release(struct inode *inode, struct file *file)
 179 {
 180         struct msm_perf_state *perf = inode->i_private;
 181         struct msm_drm_private *priv = perf->dev->dev_private;
 182         msm_gpu_perfcntr_stop(priv->gpu);
 183         perf->open = false;
 184         return 0;
 185 }
 186 
 187 
 188 static const struct file_operations perf_debugfs_fops = {
 189         .owner = THIS_MODULE,
 190         .open = perf_open,
 191         .read = perf_read,
 192         .llseek = no_llseek,
 193         .release = perf_release,
 194 };
 195 
 196 int msm_perf_debugfs_init(struct drm_minor *minor)
 197 {
 198         struct msm_drm_private *priv = minor->dev->dev_private;
 199         struct msm_perf_state *perf;
 200 
 201         /* only create on first minor: */
 202         if (priv->perf)
 203                 return 0;
 204 
 205         perf = kzalloc(sizeof(*perf), GFP_KERNEL);
 206         if (!perf)
 207                 return -ENOMEM;
 208 
 209         perf->dev = minor->dev;
 210 
 211         mutex_init(&perf->read_lock);
 212         priv->perf = perf;
 213 
 214         debugfs_create_file("perf", S_IFREG | S_IRUGO, minor->debugfs_root,
 215                             perf, &perf_debugfs_fops);
 216         return 0;
 217 }
 218 
 219 void msm_perf_debugfs_cleanup(struct msm_drm_private *priv)
 220 {
 221         struct msm_perf_state *perf = priv->perf;
 222 
 223         if (!perf)
 224                 return;
 225 
 226         priv->perf = NULL;
 227 
 228         mutex_destroy(&perf->read_lock);
 229 
 230         kfree(perf);
 231 }
 232 
 233 #endif

/* [<][>][^][v][top][bottom][index][help] */