<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Aug 15, 2016 at 3:57 PM, Chris Wilson <span dir="ltr"><<a href="mailto:chris@chris-wilson.co.uk" target="_blank">chris@chris-wilson.co.uk</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class=""><div class="h5">On Mon, Aug 15, 2016 at 03:41:18PM +0100, Robert Bragg wrote:<br>
> Adds base i915 perf infrastructure for Gen performance metrics.<br>
><br>
> This adds a DRM_IOCTL_I915_PERF_OPEN ioctl that takes an array of uint64<br>
> properties to configure a stream of metrics and returns a new fd usable<br>
> with standard VFS system calls including read() to read typed and sized<br>
> records; ioctl() to enable or disable capture and poll() to wait for<br>
> data.<br>
><br>
> A stream is opened something like:<br>
><br>
> uint64_t properties[] = {<br>
> /* Single context sampling */<br>
> DRM_I915_PERF_PROP_CTX_HANDLE, ctx_handle,<br>
><br>
> /* Include OA reports in samples */<br>
> DRM_I915_PERF_PROP_SAMPLE_OA, true,<br>
><br>
> /* OA unit configuration */<br>
> DRM_I915_PERF_PROP_OA_METRICS_<wbr>SET, metrics_set_id,<br>
> DRM_I915_PERF_PROP_OA_FORMAT, report_format,<br>
> DRM_I915_PERF_PROP_OA_<wbr>EXPONENT, period_exponent,<br>
> };<br>
> struct drm_i915_perf_open_param parm = {<br>
> .flags = I915_PERF_FLAG_FD_CLOEXEC |<br>
> I915_PERF_FLAG_FD_NONBLOCK |<br>
> I915_PERF_FLAG_DISABLED,<br>
> .properties_ptr = (uint64_t)properties,<br>
> .num_properties = sizeof(properties) / 16,<br>
> };<br>
> int fd = drmIoctl(drm_fd, DRM_IOCTL_I915_PERF_OPEN, ¶m);<br>
><br>
> Records read all start with a common { type, size } header with<br>
> DRM_I915_PERF_RECORD_SAMPLE being of most interest. Sample records<br>
> contain an extensible number of fields and it's the<br>
> DRM_I915_PERF_PROP_SAMPLE_xyz properties given when opening that<br>
> determine what's included in every sample.<br>
><br>
> No specific streams are supported yet so any attempt to open a stream<br>
> will return an error.<br>
><br>
> Signed-off-by: Robert Bragg <<a href="mailto:robert@sixbynine.org">robert@sixbynine.org</a>><br>
> ---<br>
> drivers/gpu/drm/i915/Makefile | 3 +<br>
> drivers/gpu/drm/i915/i915_drv.<wbr>c | 6 +<br>
> drivers/gpu/drm/i915/i915_drv.<wbr>h | 92 +++++++++<br>
> drivers/gpu/drm/i915/i915_<wbr>perf.c | 430 ++++++++++++++++++++++++++++++<wbr>+++++++++<br>
> include/uapi/drm/i915_drm.h | 67 ++++++<br>
> 5 files changed, 598 insertions(+)<br>
> create mode 100644 drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
><br>
> diff --git a/drivers/gpu/drm/i915/<wbr>Makefile b/drivers/gpu/drm/i915/<wbr>Makefile<br>
> index 6092f0e..9a2f1f9 100644<br>
> --- a/drivers/gpu/drm/i915/<wbr>Makefile<br>
> +++ b/drivers/gpu/drm/i915/<wbr>Makefile<br>
> @@ -106,6 +106,9 @@ i915-y += dvo_ch7017.o \<br>
> # virtual gpu code<br>
> i915-y += i915_vgpu.o<br>
><br>
> +# perf code<br>
> +i915-y += i915_perf.o<br>
> +<br>
> ifeq ($(CONFIG_DRM_I915_GVT),y)<br>
> i915-y += intel_gvt.o<br>
> include $(src)/gvt/Makefile<br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>drv.c b/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
> index 83afdd0..f5209ff 100644<br>
> --- a/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>drv.c<br>
> @@ -1169,6 +1169,9 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)<br>
> * cannot run before the connectors are registered.<br>
> */<br>
> intel_fbdev_initial_config_<wbr>async(dev);<br>
> +<br>
> + /* Depends on sysfs having been initialized */<br>
> + i915_perf_init(dev_priv);<br>
<br>
</div></div>Then please call it i915_perf_register() and i915_perf_unregister()<br>
respectively.<br>
<span class=""></span> <br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="">
> + struct {<br>
> + bool initialized;<br>
<br>
</span>This is bogus. We simply cannot allow userspace to access internals<br>
before we set them up. As it stands you have no serialisation here, so<br>
the protect is moot.<br></blockquote><div><br></div><div>Ah, previously I was initializing in i915_driver_load() and I think it should have been synchronized by requiring a drm fd to interact with the driver. I'd need to dig in to understand why, but it was previously ok to init before i915_setup_sysfs(), so this looks like I messed up the rebase, not properly appreciating I was now initializing after being visible to userspace.<br><br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
i915_perf_init() needs to be split up into the early initialisation<br>
function to setup internals, and the i915_perf_register() function to<br>
enable userspace (with however many steps you need in between).<br></blockquote><div><br></div><div>I think it's probably just the sysfs bits that may need to be deferred until after i915_sysfs_init(), after drm_dev_register() where we're visible to userspace.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div><div class="h5"><br>
> + struct list_head streams;<br>
> + } perf;<br>
> +<br>
> /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */<br>
> struct {<br>
> int (*execbuf_submit)(struct i915_execbuffer_params *params,<br>
> @@ -3421,6 +3506,9 @@ int i915_gem_context_setparam_<wbr>ioctl(struct drm_device *dev, void *data,<br>
> int i915_gem_context_reset_stats_<wbr>ioctl(struct drm_device *dev, void *data,<br>
> struct drm_file *file);<br>
><br>
> +int i915_perf_open_ioctl(struct drm_device *dev, void *data,<br>
> + struct drm_file *file);<br>
> +<br>
> /* i915_gem_evict.c */<br>
> int __must_check i915_gem_evict_something(<wbr>struct drm_device *dev,<br>
> struct i915_address_space *vm,<br>
> @@ -3540,6 +3628,10 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,<br>
> u32 batch_len,<br>
> bool is_master);<br>
><br>
> +/* i915_perf.c */<br>
> +extern void i915_perf_init(struct drm_i915_private *dev_priv);<br>
> +extern void i915_perf_fini(struct drm_i915_private *dev_priv);<br>
> +<br>
> /* i915_suspend.c */<br>
> extern int i915_save_state(struct drm_device *dev);<br>
> extern int i915_restore_state(struct drm_device *dev);<br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>perf.c b/drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
> new file mode 100644<br>
> index 0000000..d0ed645<br>
> --- /dev/null<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
> @@ -0,0 +1,430 @@<br>
> +/*<br>
> + * Copyright © 2015-2016 Intel Corporation<br>
> + *<br>
> + * Permission is hereby granted, free of charge, to any person obtaining a<br>
> + * copy of this software and associated documentation files (the "Software"),<br>
> + * to deal in the Software without restriction, including without limitation<br>
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
> + * and/or sell copies of the Software, and to permit persons to whom the<br>
> + * Software is furnished to do so, subject to the following conditions:<br>
> + *<br>
> + * The above copyright notice and this permission notice (including the next<br>
> + * paragraph) shall be included in all copies or substantial portions of the<br>
> + * Software.<br>
> + *<br>
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL<br>
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
> + * IN THE SOFTWARE.<br>
> + *<br>
> + * Authors:<br>
> + * Robert Bragg <<a href="mailto:robert@sixbynine.org">robert@sixbynine.org</a>><br>
> + */<br>
> +<br>
> +#include <linux/anon_inodes.h><br>
> +#include <linux/sizes.h><br>
> +<br>
> +#include "i915_drv.h"<br>
> +<br>
> +struct perf_open_properties {<br>
> + u32 sample_flags;<br>
> +<br>
> + u64 single_context:1;<br>
> + u64 ctx_handle;<br>
> +};<br>
> +<br>
> +static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream,<br>
> + struct file *file,<br>
> + char __user *buf,<br>
> + size_t count,<br>
> + loff_t *ppos)<br>
> +{<br>
> + struct i915_perf_read_state state = { count, 0, buf };<br>
> + int ret = stream->ops->read(stream, &state);<br>
> +<br>
> + /* If we've successfully copied any data then reporting that<br>
> + * takes precedence over any internal error status, so the<br>
> + * data isn't lost<br>
> + */<br>
> + return state.read ? state.read : (ret ? ret : -EAGAIN);<br>
<br>
</div></div>return state.read ?: ret ?: -EAGAIN;<br></blockquote><div><br></div><div>Ah, I hadn't heard of the Elvis operator before.<br> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Alternatively you could follow the standard pattern for read. Dare I ask<br>
what is going to go into state that needs the obfuscation?<br></blockquote><div><br></div><div>I had dug around a bit when I was trying to decide how to handle the corner cases here and found some precedent for prioritize reporting any data copied over an error for a read().<br><br></div><div>I've changed the comment to give an example and a reference:<br><br>/* If we've successfully copied any data then reporting that<br> * takes precedence over any internal error status, so the<br> * data isn't lost.<br> *<br> * For example ret will be -ENOSPC whenever there is more<br> * buffered data than can be copied to userspace, but that's<br> * only interesting if we weren't able to copy some data<br> * because it implies the userspace buffer is too small to<br> * receive a single record (and we never split records).<br> *<br> * Another case with ret == -EFAULT is more of a grey area<br> * since it would seem like bad form for userspace to ask us<br> * to overrun its buffer, but the user knows best:<br> *<br> * <a href="http://yarchive.net/comp/linux/partial_reads_writes.html">http://yarchive.net/comp/linux/partial_reads_writes.html</a><br> */<br>return state.read ?: (ret ?: -EAGAIN);<br><br></div><div>Searching for another reference, I also just found the linux device drivers book talks about the same kind of convention:<br><br><a href="http://www.makelinux.net/ldd3/chp-3-sect-7">http://www.makelinux.net/ldd3/chp-3-sect-7</a><br><br>"Both the read and write methods return a negative value if an error occurs. A return value greater than or equal to 0, instead, tells the calling program how many bytes have been successfully transferred. If some data is transferred correctly and then an error happens, the return value must be the count of bytes successfully transferred, and the error does not get reported until the next time the function is called. Implementing this convention requires, of course, that your driver remember that the error has occurred so that it can return the error status in the future."<br><br></div><div>- Robert<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<span class=""><font color="#888888">-Chris<br>
<br>
--<br>
Chris Wilson, Intel Open Source Technology Centre<br>
</font></span></blockquote></div><br></div></div>