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