blob: 777ce65f910dde15a495d4e6e9409ed22e380fb5 [file] [log] [blame]
Robert Braggeec688e2016-11-07 19:49:47 +00001/*
2 * Copyright © 2015-2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Robert Bragg <robert@sixbynine.org>
25 */
26
27#include <linux/anon_inodes.h>
28
29#include "i915_drv.h"
30
31struct perf_open_properties {
32 u32 sample_flags;
33
34 u64 single_context:1;
35 u64 ctx_handle;
36};
37
38static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream,
39 struct file *file,
40 char __user *buf,
41 size_t count,
42 loff_t *ppos)
43{
44 /* Note we keep the offset (aka bytes read) separate from any
45 * error status so that the final check for whether we return
46 * the bytes read with a higher precedence than any error (see
47 * comment below) doesn't need to be handled/duplicated in
48 * stream->ops->read() implementations.
49 */
50 size_t offset = 0;
51 int ret = stream->ops->read(stream, buf, count, &offset);
52
53 /* If we've successfully copied any data then reporting that
54 * takes precedence over any internal error status, so the
55 * data isn't lost.
56 *
57 * For example ret will be -ENOSPC whenever there is more
58 * buffered data than can be copied to userspace, but that's
59 * only interesting if we weren't able to copy some data
60 * because it implies the userspace buffer is too small to
61 * receive a single record (and we never split records).
62 *
63 * Another case with ret == -EFAULT is more of a grey area
64 * since it would seem like bad form for userspace to ask us
65 * to overrun its buffer, but the user knows best:
66 *
67 * http://yarchive.net/comp/linux/partial_reads_writes.html
68 */
69 return offset ?: (ret ?: -EAGAIN);
70}
71
72static ssize_t i915_perf_read(struct file *file,
73 char __user *buf,
74 size_t count,
75 loff_t *ppos)
76{
77 struct i915_perf_stream *stream = file->private_data;
78 struct drm_i915_private *dev_priv = stream->dev_priv;
79 ssize_t ret;
80
81 if (!(file->f_flags & O_NONBLOCK)) {
82 /* Allow false positives from stream->ops->wait_unlocked.
83 */
84 do {
85 ret = stream->ops->wait_unlocked(stream);
86 if (ret)
87 return ret;
88
89 mutex_lock(&dev_priv->perf.lock);
90 ret = i915_perf_read_locked(stream, file,
91 buf, count, ppos);
92 mutex_unlock(&dev_priv->perf.lock);
93 } while (ret == -EAGAIN);
94 } else {
95 mutex_lock(&dev_priv->perf.lock);
96 ret = i915_perf_read_locked(stream, file, buf, count, ppos);
97 mutex_unlock(&dev_priv->perf.lock);
98 }
99
100 return ret;
101}
102
103static unsigned int i915_perf_poll_locked(struct i915_perf_stream *stream,
104 struct file *file,
105 poll_table *wait)
106{
107 unsigned int streams = 0;
108
109 stream->ops->poll_wait(stream, file, wait);
110
111 if (stream->ops->can_read(stream))
112 streams |= POLLIN;
113
114 return streams;
115}
116
117static unsigned int i915_perf_poll(struct file *file, poll_table *wait)
118{
119 struct i915_perf_stream *stream = file->private_data;
120 struct drm_i915_private *dev_priv = stream->dev_priv;
121 int ret;
122
123 mutex_lock(&dev_priv->perf.lock);
124 ret = i915_perf_poll_locked(stream, file, wait);
125 mutex_unlock(&dev_priv->perf.lock);
126
127 return ret;
128}
129
130static void i915_perf_enable_locked(struct i915_perf_stream *stream)
131{
132 if (stream->enabled)
133 return;
134
135 /* Allow stream->ops->enable() to refer to this */
136 stream->enabled = true;
137
138 if (stream->ops->enable)
139 stream->ops->enable(stream);
140}
141
142static void i915_perf_disable_locked(struct i915_perf_stream *stream)
143{
144 if (!stream->enabled)
145 return;
146
147 /* Allow stream->ops->disable() to refer to this */
148 stream->enabled = false;
149
150 if (stream->ops->disable)
151 stream->ops->disable(stream);
152}
153
154static long i915_perf_ioctl_locked(struct i915_perf_stream *stream,
155 unsigned int cmd,
156 unsigned long arg)
157{
158 switch (cmd) {
159 case I915_PERF_IOCTL_ENABLE:
160 i915_perf_enable_locked(stream);
161 return 0;
162 case I915_PERF_IOCTL_DISABLE:
163 i915_perf_disable_locked(stream);
164 return 0;
165 }
166
167 return -EINVAL;
168}
169
170static long i915_perf_ioctl(struct file *file,
171 unsigned int cmd,
172 unsigned long arg)
173{
174 struct i915_perf_stream *stream = file->private_data;
175 struct drm_i915_private *dev_priv = stream->dev_priv;
176 long ret;
177
178 mutex_lock(&dev_priv->perf.lock);
179 ret = i915_perf_ioctl_locked(stream, cmd, arg);
180 mutex_unlock(&dev_priv->perf.lock);
181
182 return ret;
183}
184
185static void i915_perf_destroy_locked(struct i915_perf_stream *stream)
186{
187 struct drm_i915_private *dev_priv = stream->dev_priv;
188
189 if (stream->enabled)
190 i915_perf_disable_locked(stream);
191
192 if (stream->ops->destroy)
193 stream->ops->destroy(stream);
194
195 list_del(&stream->link);
196
197 if (stream->ctx) {
198 mutex_lock(&dev_priv->drm.struct_mutex);
199 i915_gem_context_put(stream->ctx);
200 mutex_unlock(&dev_priv->drm.struct_mutex);
201 }
202
203 kfree(stream);
204}
205
206static int i915_perf_release(struct inode *inode, struct file *file)
207{
208 struct i915_perf_stream *stream = file->private_data;
209 struct drm_i915_private *dev_priv = stream->dev_priv;
210
211 mutex_lock(&dev_priv->perf.lock);
212 i915_perf_destroy_locked(stream);
213 mutex_unlock(&dev_priv->perf.lock);
214
215 return 0;
216}
217
218
219static const struct file_operations fops = {
220 .owner = THIS_MODULE,
221 .llseek = no_llseek,
222 .release = i915_perf_release,
223 .poll = i915_perf_poll,
224 .read = i915_perf_read,
225 .unlocked_ioctl = i915_perf_ioctl,
226};
227
228
229static struct i915_gem_context *
230lookup_context(struct drm_i915_private *dev_priv,
231 struct drm_i915_file_private *file_priv,
232 u32 ctx_user_handle)
233{
234 struct i915_gem_context *ctx;
235 int ret;
236
237 ret = i915_mutex_lock_interruptible(&dev_priv->drm);
238 if (ret)
239 return ERR_PTR(ret);
240
241 ctx = i915_gem_context_lookup(file_priv, ctx_user_handle);
242 if (!IS_ERR(ctx))
243 i915_gem_context_get(ctx);
244
245 mutex_unlock(&dev_priv->drm.struct_mutex);
246
247 return ctx;
248}
249
250static int
251i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv,
252 struct drm_i915_perf_open_param *param,
253 struct perf_open_properties *props,
254 struct drm_file *file)
255{
256 struct i915_gem_context *specific_ctx = NULL;
257 struct i915_perf_stream *stream = NULL;
258 unsigned long f_flags = 0;
259 int stream_fd;
260 int ret;
261
262 if (props->single_context) {
263 u32 ctx_handle = props->ctx_handle;
264 struct drm_i915_file_private *file_priv = file->driver_priv;
265
266 specific_ctx = lookup_context(dev_priv, file_priv, ctx_handle);
267 if (IS_ERR(specific_ctx)) {
268 ret = PTR_ERR(specific_ctx);
269 if (ret != -EINTR)
270 DRM_ERROR("Failed to look up context with ID %u for opening perf stream\n",
271 ctx_handle);
272 goto err;
273 }
274 }
275
276 if (!specific_ctx && !capable(CAP_SYS_ADMIN)) {
277 DRM_ERROR("Insufficient privileges to open system-wide i915 perf stream\n");
278 ret = -EACCES;
279 goto err_ctx;
280 }
281
282 stream = kzalloc(sizeof(*stream), GFP_KERNEL);
283 if (!stream) {
284 ret = -ENOMEM;
285 goto err_ctx;
286 }
287
288 stream->sample_flags = props->sample_flags;
289 stream->dev_priv = dev_priv;
290 stream->ctx = specific_ctx;
291
292 /*
293 * TODO: support sampling something
294 *
295 * For now this is as far as we can go.
296 */
297 DRM_ERROR("Unsupported i915 perf stream configuration\n");
298 ret = -EINVAL;
299 goto err_alloc;
300
301 list_add(&stream->link, &dev_priv->perf.streams);
302
303 if (param->flags & I915_PERF_FLAG_FD_CLOEXEC)
304 f_flags |= O_CLOEXEC;
305 if (param->flags & I915_PERF_FLAG_FD_NONBLOCK)
306 f_flags |= O_NONBLOCK;
307
308 stream_fd = anon_inode_getfd("[i915_perf]", &fops, stream, f_flags);
309 if (stream_fd < 0) {
310 ret = stream_fd;
311 goto err_open;
312 }
313
314 if (!(param->flags & I915_PERF_FLAG_DISABLED))
315 i915_perf_enable_locked(stream);
316
317 return stream_fd;
318
319err_open:
320 list_del(&stream->link);
321 if (stream->ops->destroy)
322 stream->ops->destroy(stream);
323err_alloc:
324 kfree(stream);
325err_ctx:
326 if (specific_ctx) {
327 mutex_lock(&dev_priv->drm.struct_mutex);
328 i915_gem_context_put(specific_ctx);
329 mutex_unlock(&dev_priv->drm.struct_mutex);
330 }
331err:
332 return ret;
333}
334
335/* Note we copy the properties from userspace outside of the i915 perf
336 * mutex to avoid an awkward lockdep with mmap_sem.
337 *
338 * Note this function only validates properties in isolation it doesn't
339 * validate that the combination of properties makes sense or that all
340 * properties necessary for a particular kind of stream have been set.
341 */
342static int read_properties_unlocked(struct drm_i915_private *dev_priv,
343 u64 __user *uprops,
344 u32 n_props,
345 struct perf_open_properties *props)
346{
347 u64 __user *uprop = uprops;
348 int i;
349
350 memset(props, 0, sizeof(struct perf_open_properties));
351
352 if (!n_props) {
353 DRM_ERROR("No i915 perf properties given");
354 return -EINVAL;
355 }
356
357 /* Considering that ID = 0 is reserved and assuming that we don't
358 * (currently) expect any configurations to ever specify duplicate
359 * values for a particular property ID then the last _PROP_MAX value is
360 * one greater than the maximum number of properties we expect to get
361 * from userspace.
362 */
363 if (n_props >= DRM_I915_PERF_PROP_MAX) {
364 DRM_ERROR("More i915 perf properties specified than exist");
365 return -EINVAL;
366 }
367
368 for (i = 0; i < n_props; i++) {
369 u64 id, value;
370 int ret;
371
372 ret = get_user(id, uprop);
373 if (ret)
374 return ret;
375
376 ret = get_user(value, uprop + 1);
377 if (ret)
378 return ret;
379
380 switch ((enum drm_i915_perf_property_id)id) {
381 case DRM_I915_PERF_PROP_CTX_HANDLE:
382 props->single_context = 1;
383 props->ctx_handle = value;
384 break;
385 default:
386 MISSING_CASE(id);
387 DRM_ERROR("Unknown i915 perf property ID");
388 return -EINVAL;
389 }
390
391 uprop += 2;
392 }
393
394 return 0;
395}
396
397int i915_perf_open_ioctl(struct drm_device *dev, void *data,
398 struct drm_file *file)
399{
400 struct drm_i915_private *dev_priv = dev->dev_private;
401 struct drm_i915_perf_open_param *param = data;
402 struct perf_open_properties props;
403 u32 known_open_flags;
404 int ret;
405
406 if (!dev_priv->perf.initialized) {
407 DRM_ERROR("i915 perf interface not available for this system");
408 return -ENOTSUPP;
409 }
410
411 known_open_flags = I915_PERF_FLAG_FD_CLOEXEC |
412 I915_PERF_FLAG_FD_NONBLOCK |
413 I915_PERF_FLAG_DISABLED;
414 if (param->flags & ~known_open_flags) {
415 DRM_ERROR("Unknown drm_i915_perf_open_param flag\n");
416 return -EINVAL;
417 }
418
419 ret = read_properties_unlocked(dev_priv,
420 u64_to_user_ptr(param->properties_ptr),
421 param->num_properties,
422 &props);
423 if (ret)
424 return ret;
425
426 mutex_lock(&dev_priv->perf.lock);
427 ret = i915_perf_open_ioctl_locked(dev_priv, param, &props, file);
428 mutex_unlock(&dev_priv->perf.lock);
429
430 return ret;
431}
432
433void i915_perf_init(struct drm_i915_private *dev_priv)
434{
435 INIT_LIST_HEAD(&dev_priv->perf.streams);
436 mutex_init(&dev_priv->perf.lock);
437
438 dev_priv->perf.initialized = true;
439}
440
441void i915_perf_fini(struct drm_i915_private *dev_priv)
442{
443 if (!dev_priv->perf.initialized)
444 return;
445
446 /* Currently nothing to clean up */
447
448 dev_priv->perf.initialized = false;
449}