diff mbox

[5/9] drm/i915: Enable i915 perf stream for Haswell OA unit

Message ID 1461162194-1424-6-git-send-email-robert@sixbynine.org (mailing list archive)
State New, archived
Headers show

Commit Message

Robert Bragg April 20, 2016, 2:23 p.m. UTC
Gen graphics hardware can be set up to periodically write snapshots of
performance counters into a circular buffer via its Observation
Architecture and this patch exposes that capability to userspace via the
i915 perf interface.

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Robert Bragg <robert@sixbynine.org>
Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h         |  56 +-
 drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
 drivers/gpu/drm/i915/i915_perf.c        | 940 +++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
 include/uapi/drm/i915_drm.h             |  70 ++-
 5 files changed, 1408 insertions(+), 20 deletions(-)

Comments

kernel test robot April 20, 2016, 4:16 p.m. UTC | #1
Hi,

[auto build test ERROR on drm-intel/for-linux-next]
[also build test ERROR on next-20160420]
[cannot apply to v4.6-rc4]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Robert-Bragg/Enable-Gen-7-Observation-Architecture/20160420-222746
base:   git://anongit.freedesktop.org/drm-intel for-linux-next
config: i386-randconfig-s1-201616 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/built-in.o: In function `i915_perf_open_ioctl_locked':
>> (.text+0x2cadf4): undefined reference to `__udivdi3'
   drivers/built-in.o: In function `i915_perf_open_ioctl_locked':
   (.text+0x2cae0d): undefined reference to `__udivdi3'

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot April 20, 2016, 8:30 p.m. UTC | #2
Hi,

[auto build test ERROR on drm-intel/for-linux-next]
[also build test ERROR on next-20160420]
[cannot apply to v4.6-rc4]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Robert-Bragg/Enable-Gen-7-Observation-Architecture/20160420-222746
base:   git://anongit.freedesktop.org/drm-intel for-linux-next
config: i386-allmodconfig (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

>> ERROR: "__udivdi3" [drivers/gpu/drm/i915/i915.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Chris Wilson April 20, 2016, 9:11 p.m. UTC | #3
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static void gen7_update_oacontrol_locked(struct drm_i915_private *dev_priv)
> +{
> +	assert_spin_locked(&dev_priv->perf.hook_lock);
> +
> +	if (dev_priv->perf.oa.exclusive_stream->enabled) {
> +		unsigned long ctx_id = 0;
> +
> +		if (dev_priv->perf.oa.exclusive_stream->ctx)
> +			ctx_id = dev_priv->perf.oa.specific_ctx_id;
> +
> +		if (dev_priv->perf.oa.exclusive_stream->ctx == NULL || ctx_id) {
> +			bool periodic = dev_priv->perf.oa.periodic;
> +			u32 period_exponent = dev_priv->perf.oa.period_exponent;
> +			u32 report_format = dev_priv->perf.oa.oa_buffer.format;
> +
> +			I915_WRITE(GEN7_OACONTROL,
> +				   (ctx_id & GEN7_OACONTROL_CTX_MASK) |
> +				   (period_exponent <<
> +				    GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
> +				   (periodic ?
> +				    GEN7_OACONTROL_TIMER_ENABLE : 0) |
> +				   (report_format <<
> +				    GEN7_OACONTROL_FORMAT_SHIFT) |
> +				   (ctx_id ?
> +				    GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
> +				   GEN7_OACONTROL_ENABLE);

So this works by only recording when the OACONTROL context address
matches the CCID.

Rather than hooking into switch context and checking every batch whether
you have the exclusive context in case it changed address, you could
just pin the exclusive context when told by the user to bind perf to
that context. Then it will also have the same address until oa is
finished (and releases it vma pin).
-Chris
Chris Wilson April 20, 2016, 10:15 p.m. UTC | #4
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static int alloc_oa_buffer(struct drm_i915_private *dev_priv)
> +{
> +	struct drm_i915_gem_object *bo;
> +	int ret;
> +
> +	BUG_ON(dev_priv->perf.oa.oa_buffer.obj);
> +
> +	ret = i915_mutex_lock_interruptible(dev_priv->dev);
> +	if (ret)
> +		return ret;
> +
> +	bo = i915_gem_alloc_object(dev_priv->dev, OA_BUFFER_SIZE);
> +	if (bo == NULL) {
> +		DRM_ERROR("Failed to allocate OA buffer\n");
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +	dev_priv->perf.oa.oa_buffer.obj = bo;
> +
> +	ret = i915_gem_object_set_cache_level(bo, I915_CACHE_LLC);
> +	if (ret)
> +		goto err_unref;
> +
> +	/* PreHSW required 512K alignment, HSW requires 16M */
> +	ret = i915_gem_obj_ggtt_pin(bo, SZ_16M, 0);
> +	if (ret)
> +		goto err_unref;
> +
> +	dev_priv->perf.oa.oa_buffer.gtt_offset = i915_gem_obj_ggtt_offset(bo);
> +	dev_priv->perf.oa.oa_buffer.addr = vmap_oa_buffer(bo);

Now i915_gem_object_pin_map(bo) instead of manually vmapping it, and
i915_gem_object_unpin_map() to release.
-Chris
Chris Wilson April 20, 2016, 10:46 p.m. UTC | #5
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
> +{
> +	/* Pre-DevBDW: OABUFFER must be set with counters off,
> +	 * before OASTATUS1, but after OASTATUS2
> +	 */
> +	I915_WRITE(GEN7_OASTATUS2, dev_priv->perf.oa.oa_buffer.gtt_offset |
> +		   OA_MEM_SELECT_GGTT); /* head */
> +	I915_WRITE(GEN7_OABUFFER, dev_priv->perf.oa.oa_buffer.gtt_offset);
> +	I915_WRITE(GEN7_OASTATUS1, dev_priv->perf.oa.oa_buffer.gtt_offset |
> +		   OABUFFER_SIZE_16M); /* tail */
> +
> +	/* On Haswell we have to track which OASTATUS1 flags we've
> +	 * already seen since they can't be cleared while periodic
> +	 * sampling is enabled.
> +	 */
> +	dev_priv->perf.oa.gen7_latched_oastatus1 = 0;
> +
> +	/* We have a sanity check in gen7_append_oa_reports() that
> +	 * looks at the report-id field to make sure it's non-zero
> +	 * which relies on the assumption that new reports are
> +	 * being written to zeroed memory...
> +	 */
> +	memset(dev_priv->perf.oa.oa_buffer.addr, 0, SZ_16M);

You allocated zeroed memory.
-Chris
Chris Wilson April 20, 2016, 10:52 p.m. UTC | #6
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static int i915_oa_read(struct i915_perf_stream *stream,
> +			struct i915_perf_read_state *read_state)
> +{
> +	struct drm_i915_private *dev_priv = stream->dev_priv;
> +
> +	return dev_priv->perf.oa.ops.read(stream, read_state);
> +}

> +	stream->destroy = i915_oa_stream_destroy;
> +	stream->enable = i915_oa_stream_enable;
> +	stream->disable = i915_oa_stream_disable;
> +	stream->can_read = i915_oa_can_read;
> +	stream->wait_unlocked = i915_oa_wait_unlocked;
> +	stream->poll_wait = i915_oa_poll_wait;
> +	stream->read = i915_oa_read;

Why aren't these a const ops table?
-Chris
Chris Wilson April 20, 2016, 11:09 p.m. UTC | #7
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static void i915_oa_stream_enable(struct i915_perf_stream *stream)
> +{
> +	struct drm_i915_private *dev_priv = stream->dev_priv;
> +
> +	dev_priv->perf.oa.ops.oa_enable(dev_priv);
> +
> +	if (dev_priv->perf.oa.periodic)
> +		hrtimer_start(&dev_priv->perf.oa.poll_check_timer,
> +			      ns_to_ktime(POLL_PERIOD),
> +			      HRTIMER_MODE_REL_PINNED);
> +}

> +static void i915_oa_stream_disable(struct i915_perf_stream *stream)
> +{
> +	struct drm_i915_private *dev_priv = stream->dev_priv;
> +
> +	dev_priv->perf.oa.ops.oa_disable(dev_priv);
> +
> +	if (dev_priv->perf.oa.periodic)
> +		hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer);
> +}

> +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer)
> +{
> +	struct drm_i915_private *dev_priv =
> +		container_of(hrtimer, typeof(*dev_priv),
> +			     perf.oa.poll_check_timer);
> +
> +	if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv))
> +		wake_up(&dev_priv->perf.oa.poll_wq);
> +
> +	hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD));
> +
> +	return HRTIMER_RESTART;
> +}

> @@ -424,8 +1313,37 @@ void i915_perf_init(struct drm_device *dev)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(dev);
>  
> +	if (!IS_HASWELL(dev))
> +		return;
> +
> +	hrtimer_init(&dev_priv->perf.oa.poll_check_timer,
> +		     CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +	dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb;
> +	init_waitqueue_head(&dev_priv->perf.oa.poll_wq);

This timer only serves to wake up pollers / wait_unlocked, right? So why
is it always running (when the stream is enabled)?

What happens to poll / wait_unlocked if oa.periodic is not set? It seems
like those functions would block indefinitely.
-Chris
Chris Wilson April 20, 2016, 11:16 p.m. UTC | #8
On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> +static int hsw_enable_metric_set(struct drm_i915_private *dev_priv)
> +{
> +	int ret = i915_oa_select_metric_set_hsw(dev_priv);
> +
> +	if (ret)
> +		return ret;
> +
> +	I915_WRITE(GDT_CHICKEN_BITS, GT_NOA_ENABLE);
> +
> +	/* PRM:
> +	 *
> +	 * OA unit is using “crclk” for its functionality. When trunk
> +	 * level clock gating takes place, OA clock would be gated,
> +	 * unable to count the events from non-render clock domain.
> +	 * Render clock gating must be disabled when OA is enabled to
> +	 * count the events from non-render domain. Unit level clock
> +	 * gating for RCS should also be disabled.
> +	 */
> +	I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) &
> +				    ~GEN7_DOP_CLOCK_GATE_ENABLE));
> +	I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) |
> +				  GEN6_CSUNIT_CLOCK_GATE_DISABLE));
> +
> +	config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs,
> +		       dev_priv->perf.oa.mux_regs_len);
> +
> +	/* It takes a fairly long time for a new MUX configuration to
> +	 * be be applied after these register writes. This delay
> +	 * duration was derived empirically based on the render_basic
> +	 * config but hopefully it covers the maximum configuration
> +	 * latency...
> +	 */
> +	mdelay(100);

You really want to busy spin for 100ms? msleep() perhaps!

Did you look for some register you can observe the change in when the
mux is reconfigured? Is even reading one of the OA registers enough?

> +	config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs,
> +		       dev_priv->perf.oa.b_counter_regs_len);
> +
> +	return 0;
> +}
> +
> +static void hsw_disable_metric_set(struct drm_i915_private *dev_priv)
> +{
> +	I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) &
> +				  ~GEN6_CSUNIT_CLOCK_GATE_DISABLE));
> +	I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) |
> +				    GEN7_DOP_CLOCK_GATE_ENABLE));
> +
> +	I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) &
> +				      ~GT_NOA_ENABLE));

You didn't preserve any other chicken bits during enable_metric_set.
-Chris
Robert Bragg April 21, 2016, 3:01 p.m. UTC | #9
On Thu, Apr 21, 2016 at 12:16 AM, Chris Wilson <chris@chris-wilson.co.uk>
wrote:

> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> > +static int hsw_enable_metric_set(struct drm_i915_private *dev_priv)
> > +{
> > +     int ret = i915_oa_select_metric_set_hsw(dev_priv);
> > +
> > +     if (ret)
> > +             return ret;
> > +
> > +     I915_WRITE(GDT_CHICKEN_BITS, GT_NOA_ENABLE);
> > +
> > +     /* PRM:
> > +      *
> > +      * OA unit is using “crclk” for its functionality. When trunk
> > +      * level clock gating takes place, OA clock would be gated,
> > +      * unable to count the events from non-render clock domain.
> > +      * Render clock gating must be disabled when OA is enabled to
> > +      * count the events from non-render domain. Unit level clock
> > +      * gating for RCS should also be disabled.
> > +      */
> > +     I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) &
> > +                                 ~GEN7_DOP_CLOCK_GATE_ENABLE));
> > +     I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) |
> > +                               GEN6_CSUNIT_CLOCK_GATE_DISABLE));
> > +
> > +     config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs,
> > +                    dev_priv->perf.oa.mux_regs_len);
> > +
> > +     /* It takes a fairly long time for a new MUX configuration to
> > +      * be be applied after these register writes. This delay
> > +      * duration was derived empirically based on the render_basic
> > +      * config but hopefully it covers the maximum configuration
> > +      * latency...
> > +      */
> > +     mdelay(100);
>
> You really want to busy spin for 100ms? msleep() perhaps!
>

Ah, oops, I forgot to change this, thanks!


>
> Did you look for some register you can observe the change in when the
> mux is reconfigured? Is even reading one of the OA registers enough?
>

Although I can't really comprehend why the delay apparently needs to be
quite so long, based on my limited understanding of some of the NOA
michroarchitecture involved here it makes some sense to me there would be a
delay that's also somewhat variable depending on the particular MUX config
and I don't know of a trick for getting explicit feedback of completion
unfortunately.

I did bring this up briefly, recently in discussion with others more
familiar with the HW side of things, but haven't had much feedback on this
so far. afaik other OS drivers aren't currently accounting for a need to
have a delay here.

For reference, 100ms was picked as I was experimenting with stepping up the
delay by orders of magnitude and found 10ms wasn't enough. Potentially I
could experiment further with delays between 10 and 100ms, but I suppose it
won't make a big difference.



>
> > +     config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs,
> > +                    dev_priv->perf.oa.b_counter_regs_len);
> > +
> > +     return 0;
> > +}
> > +
> > +static void hsw_disable_metric_set(struct drm_i915_private *dev_priv)
> > +{
> > +     I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) &
> > +                               ~GEN6_CSUNIT_CLOCK_GATE_DISABLE));
> > +     I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) |
> > +                                 GEN7_DOP_CLOCK_GATE_ENABLE));
> > +
> > +     I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) &
> > +                                   ~GT_NOA_ENABLE));
>
> You didn't preserve any other chicken bits during enable_metric_set.
>

Hmm, good point. I think I'll aim to preserve other bits when setting if
that works, just in case something else needs to fiddle with the same
register later.


> -Chris
>
> --
> Chris Wilson, Intel Open Source Technology Centre
>
Robert Bragg April 21, 2016, 3:18 p.m. UTC | #10
On Thu, Apr 21, 2016 at 12:09 AM, Chris Wilson <chris@chris-wilson.co.uk>
wrote:

> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> > +static void i915_oa_stream_enable(struct i915_perf_stream *stream)
> > +{
> > +     struct drm_i915_private *dev_priv = stream->dev_priv;
> > +
> > +     dev_priv->perf.oa.ops.oa_enable(dev_priv);
> > +
> > +     if (dev_priv->perf.oa.periodic)
> > +             hrtimer_start(&dev_priv->perf.oa.poll_check_timer,
> > +                           ns_to_ktime(POLL_PERIOD),
> > +                           HRTIMER_MODE_REL_PINNED);
> > +}
>
> > +static void i915_oa_stream_disable(struct i915_perf_stream *stream)
> > +{
> > +     struct drm_i915_private *dev_priv = stream->dev_priv;
> > +
> > +     dev_priv->perf.oa.ops.oa_disable(dev_priv);
> > +
> > +     if (dev_priv->perf.oa.periodic)
> > +             hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer);
> > +}
>
> > +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer
> *hrtimer)
> > +{
> > +     struct drm_i915_private *dev_priv =
> > +             container_of(hrtimer, typeof(*dev_priv),
> > +                          perf.oa.poll_check_timer);
> > +
> > +     if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv))
> > +             wake_up(&dev_priv->perf.oa.poll_wq);
> > +
> > +     hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD));
> > +
> > +     return HRTIMER_RESTART;
> > +}
>
> > @@ -424,8 +1313,37 @@ void i915_perf_init(struct drm_device *dev)
> >  {
> >       struct drm_i915_private *dev_priv = to_i915(dev);
> >
> > +     if (!IS_HASWELL(dev))
> > +             return;
> > +
> > +     hrtimer_init(&dev_priv->perf.oa.poll_check_timer,
> > +                  CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> > +     dev_priv->perf.oa.poll_check_timer.function =
> oa_poll_check_timer_cb;
> > +     init_waitqueue_head(&dev_priv->perf.oa.poll_wq);
>
> This timer only serves to wake up pollers / wait_unlocked, right? So why
> is it always running (when the stream is enabled)?
>
> What happens to poll / wait_unlocked if oa.periodic is not set? It seems
> like those functions would block indefinitely.
>

Right, it's unecessary. I'll look at limitting it to just while polling or
for blocking reads.

Good point about the blocking case too.

I just started testing that scenario yesterday, writting an MI_RPC unit
test which opens a stream without requesting periodic sampling, but didn't
poll or read in that case so far so didn't hit this yet.

At least for the read() this is partially considered by returning -EIO if
attempting a blocking read while the stream is disabled, but it doesn't
consider the case that the stream is enabled but periodic sampling isn't
enabled.

Regards,
- Robert


> -Chris
>
> --
> Chris Wilson, Intel Open Source Technology Centre
>
Robert Bragg April 21, 2016, 3:43 p.m. UTC | #11
On Wed, Apr 20, 2016 at 11:52 PM, Chris Wilson <chris@chris-wilson.co.uk>
wrote:

> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> > +static int i915_oa_read(struct i915_perf_stream *stream,
> > +                     struct i915_perf_read_state *read_state)
> > +{
> > +     struct drm_i915_private *dev_priv = stream->dev_priv;
> > +
> > +     return dev_priv->perf.oa.ops.read(stream, read_state);
> > +}
>
> > +     stream->destroy = i915_oa_stream_destroy;
> > +     stream->enable = i915_oa_stream_enable;
> > +     stream->disable = i915_oa_stream_disable;
> > +     stream->can_read = i915_oa_can_read;
> > +     stream->wait_unlocked = i915_oa_wait_unlocked;
> > +     stream->poll_wait = i915_oa_poll_wait;
> > +     stream->read = i915_oa_read;
>
> Why aren't these a const ops table?
>

No particular reason; I guess it just seemed straightforward enough at the
time. I suppose it avoids some redundant pointer indirection and could suit
defining streams in the future that might find it awkward to have static
ops (don't have anything like that in mind though) but it's at the expense
of a slightly larger stream struct (though also don't see that as a concern
currently).

Can change if you like.

Regards,
- Robert


> -Chris
>
> --
> Chris Wilson, Intel Open Source Technology Centre
>
Robert Bragg April 21, 2016, 4:15 p.m. UTC | #12
On Wed, Apr 20, 2016 at 10:11 PM, Chris Wilson <chris@chris-wilson.co.uk>
wrote:

> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> > +static void gen7_update_oacontrol_locked(struct drm_i915_private
> *dev_priv)
> > +{
> > +     assert_spin_locked(&dev_priv->perf.hook_lock);
> > +
> > +     if (dev_priv->perf.oa.exclusive_stream->enabled) {
> > +             unsigned long ctx_id = 0;
> > +
> > +             if (dev_priv->perf.oa.exclusive_stream->ctx)
> > +                     ctx_id = dev_priv->perf.oa.specific_ctx_id;
> > +
> > +             if (dev_priv->perf.oa.exclusive_stream->ctx == NULL ||
> ctx_id) {
> > +                     bool periodic = dev_priv->perf.oa.periodic;
> > +                     u32 period_exponent =
> dev_priv->perf.oa.period_exponent;
> > +                     u32 report_format =
> dev_priv->perf.oa.oa_buffer.format;
> > +
> > +                     I915_WRITE(GEN7_OACONTROL,
> > +                                (ctx_id & GEN7_OACONTROL_CTX_MASK) |
> > +                                (period_exponent <<
> > +                                 GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
> > +                                (periodic ?
> > +                                 GEN7_OACONTROL_TIMER_ENABLE : 0) |
> > +                                (report_format <<
> > +                                 GEN7_OACONTROL_FORMAT_SHIFT) |
> > +                                (ctx_id ?
> > +                                 GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
> > +                                GEN7_OACONTROL_ENABLE);
>
> So this works by only recording when the OACONTROL context address
> matches the CCID.
>

> Rather than hooking into switch context and checking every batch whether
> you have the exclusive context in case it changed address, you could
> just pin the exclusive context when told by the user to bind perf to
> that context. Then it will also have the same address until oa is
> finished (and releases it vma pin).
>

Yeah, this was the approach I first went with when the driver was perf
based, though we ended up deciding to got with hooking into pinning and
updating the OA state in the end.

E.g. for reference:
https://lists.freedesktop.org/archives/intel-gfx/2014-November/055385.html
(wow, sad face after seeing how long I've been kicking this stuff)

I'd prefer to stick with this approach now, unless you see a big problem
with it.

Regards,
- Robert



> -Chris
>
> --
> Chris Wilson, Intel Open Source Technology Centre
>
Chris Wilson April 21, 2016, 4:21 p.m. UTC | #13
On Thu, Apr 21, 2016 at 04:43:19PM +0100, Robert Bragg wrote:
>    On Wed, Apr 20, 2016 at 11:52 PM, Chris Wilson
>    <[1]chris@chris-wilson.co.uk> wrote:
> 
>      On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
>      > +static int i915_oa_read(struct i915_perf_stream *stream,
>      > +                     struct i915_perf_read_state *read_state)
>      > +{
>      > +     struct drm_i915_private *dev_priv = stream->dev_priv;
>      > +
>      > +     return dev_priv->perf.oa.ops.read(stream, read_state);
>      > +}
> 
>      > +     stream->destroy = i915_oa_stream_destroy;
>      > +     stream->enable = i915_oa_stream_enable;
>      > +     stream->disable = i915_oa_stream_disable;
>      > +     stream->can_read = i915_oa_can_read;
>      > +     stream->wait_unlocked = i915_oa_wait_unlocked;
>      > +     stream->poll_wait = i915_oa_poll_wait;
>      > +     stream->read = i915_oa_read;
> 
>      Why aren't these a const ops table?
> 
>    No particular reason; I guess it just seemed straightforward enough at the
>    time. I suppose it avoids some redundant pointer indirection and could
>    suit defining streams in the future that might find it awkward to have
>    static ops (don't have anything like that in mind though) but it's at the
>    expense of a slightly larger stream struct (though also don't see that as
>    a concern currently).
> 
>    Can change if you like.

I think it is safe to say it is considered best practice to have vfunc
tables in read-only memory. Certainly raises an eyebrow when they look
like they could be modified on the fly.
-Chris
Robert Bragg April 22, 2016, 1:10 a.m. UTC | #14
On Thu, Apr 21, 2016 at 4:18 PM, Robert Bragg <robert@sixbynine.org> wrote:

>
>
> On Thu, Apr 21, 2016 at 12:09 AM, Chris Wilson <chris@chris-wilson.co.uk>
> wrote:
>
>> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
>> > +static void i915_oa_stream_enable(struct i915_perf_stream *stream)
>> > +{
>> > +     struct drm_i915_private *dev_priv = stream->dev_priv;
>> > +
>> > +     dev_priv->perf.oa.ops.oa_enable(dev_priv);
>> > +
>> > +     if (dev_priv->perf.oa.periodic)
>> > +             hrtimer_start(&dev_priv->perf.oa.poll_check_timer,
>> > +                           ns_to_ktime(POLL_PERIOD),
>> > +                           HRTIMER_MODE_REL_PINNED);
>> > +}
>>
>> > +static void i915_oa_stream_disable(struct i915_perf_stream *stream)
>> > +{
>> > +     struct drm_i915_private *dev_priv = stream->dev_priv;
>> > +
>> > +     dev_priv->perf.oa.ops.oa_disable(dev_priv);
>> > +
>> > +     if (dev_priv->perf.oa.periodic)
>> > +             hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer);
>> > +}
>>
>> > +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer
>> *hrtimer)
>> > +{
>> > +     struct drm_i915_private *dev_priv =
>> > +             container_of(hrtimer, typeof(*dev_priv),
>> > +                          perf.oa.poll_check_timer);
>> > +
>> > +     if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv))
>> > +             wake_up(&dev_priv->perf.oa.poll_wq);
>> > +
>> > +     hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD));
>> > +
>> > +     return HRTIMER_RESTART;
>> > +}
>>
>> > @@ -424,8 +1313,37 @@ void i915_perf_init(struct drm_device *dev)
>> >  {
>> >       struct drm_i915_private *dev_priv = to_i915(dev);
>> >
>> > +     if (!IS_HASWELL(dev))
>> > +             return;
>> > +
>> > +     hrtimer_init(&dev_priv->perf.oa.poll_check_timer,
>> > +                  CLOCK_MONOTONIC, HRTIMER_MODE_REL);
>> > +     dev_priv->perf.oa.poll_check_timer.function =
>> oa_poll_check_timer_cb;
>> > +     init_waitqueue_head(&dev_priv->perf.oa.poll_wq);
>>
>> This timer only serves to wake up pollers / wait_unlocked, right? So why
>> is it always running (when the stream is enabled)?
>>
>>
> Right, it's unecessary. I'll look at limitting it to just while polling or
> for blocking reads.
>

Actually, looking at this, I couldn't see a clean way to synchronized with
do_sys_poll() returning to be able to cancel the hrtimer. The .poll fop is
only responsible for registering a wait queue via poll_wait() and checking
if there are already events pending before any wait.

The current hrtimer frequency was picked as a reasonable default to ensure
we pick up samples before an overflow with high frequency OA sampling but
also avoids latency in picking up samples written at a lower frequency.
Something I've considered a few times before is that we might want to add a
property that can influence the latency userspace is happy with, which
might also alieviate some of this concern that the hrtimer runs all the
time the stream is enabled when it could often be fine to run at a much
lower frequency than the current 200Hz.

For now, maybe it's ok to stick with the fixed frequency, and I'll plan to
experiment with a property for influencing the maximum latency?

Regards,
- Robert
Robert Bragg April 22, 2016, 11:04 a.m. UTC | #15
On Wed, Apr 20, 2016 at 11:46 PM, Chris Wilson <chris@chris-wilson.co.uk>
wrote:

> On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
> > +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
> > +{
> > +     /* Pre-DevBDW: OABUFFER must be set with counters off,
> > +      * before OASTATUS1, but after OASTATUS2
> > +      */
> > +     I915_WRITE(GEN7_OASTATUS2, dev_priv->perf.oa.oa_buffer.gtt_offset |
> > +                OA_MEM_SELECT_GGTT); /* head */
> > +     I915_WRITE(GEN7_OABUFFER, dev_priv->perf.oa.oa_buffer.gtt_offset);
> > +     I915_WRITE(GEN7_OASTATUS1, dev_priv->perf.oa.oa_buffer.gtt_offset |
> > +                OABUFFER_SIZE_16M); /* tail */
> > +
> > +     /* On Haswell we have to track which OASTATUS1 flags we've
> > +      * already seen since they can't be cleared while periodic
> > +      * sampling is enabled.
> > +      */
> > +     dev_priv->perf.oa.gen7_latched_oastatus1 = 0;
> > +
> > +     /* We have a sanity check in gen7_append_oa_reports() that
> > +      * looks at the report-id field to make sure it's non-zero
> > +      * which relies on the assumption that new reports are
> > +      * being written to zeroed memory...
> > +      */
> > +     memset(dev_priv->perf.oa.oa_buffer.addr, 0, SZ_16M);
>
> You allocated zeroed memory.
>

yup. currently I have this memset here because we may re-init the buffer if
the stream is disabled then re-enabled (via I915_PERF_IOCTL_ENABLE) or if
we have to reset the unit on error. In these cases there may be some number
of reports in the buffer with non-zero report-id fields while we still want
to be sure new reports are being written to zereod memory so that the
sanity check that report-id != 0 will continue to be valid.

I've had it in mind to consider optimizing this at some point to minimize
how much of the buffer is cleared, maybe just for the _DISABLE/_ENABLE case
where I'd expect the buffer will mostly be empty before disabling the
stream.

- Robert


-Chris
>
> --
> Chris Wilson, Intel Open Source Technology Centre
>
Chris Wilson April 22, 2016, 11:18 a.m. UTC | #16
On Fri, Apr 22, 2016 at 12:04:26PM +0100, Robert Bragg wrote:
>    On Wed, Apr 20, 2016 at 11:46 PM, Chris Wilson
>    <[1]chris@chris-wilson.co.uk> wrote:
> 
>      On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
>      > +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
>      > +{
>      > +     /* Pre-DevBDW: OABUFFER must be set with counters off,
>      > +      * before OASTATUS1, but after OASTATUS2
>      > +      */
>      > +     I915_WRITE(GEN7_OASTATUS2,
>      dev_priv->perf.oa.oa_buffer.gtt_offset |
>      > +                OA_MEM_SELECT_GGTT); /* head */
>      > +     I915_WRITE(GEN7_OABUFFER,
>      dev_priv->perf.oa.oa_buffer.gtt_offset);
>      > +     I915_WRITE(GEN7_OASTATUS1,
>      dev_priv->perf.oa.oa_buffer.gtt_offset |
>      > +                OABUFFER_SIZE_16M); /* tail */
>      > +
>      > +     /* On Haswell we have to track which OASTATUS1 flags we've
>      > +      * already seen since they can't be cleared while periodic
>      > +      * sampling is enabled.
>      > +      */
>      > +     dev_priv->perf.oa.gen7_latched_oastatus1 = 0;
>      > +
>      > +     /* We have a sanity check in gen7_append_oa_reports() that
>      > +      * looks at the report-id field to make sure it's non-zero
>      > +      * which relies on the assumption that new reports are
>      > +      * being written to zeroed memory...
>      > +      */
>      > +     memset(dev_priv->perf.oa.oa_buffer.addr, 0, SZ_16M);
> 
>      You allocated zeroed memory.
> 
>    yup. currently I have this memset here because we may re-init the buffer
>    if the stream is disabled then re-enabled (via I915_PERF_IOCTL_ENABLE) or
>    if we have to reset the unit on error. In these cases there may be some
>    number of reports in the buffer with non-zero report-id fields while we
>    still want to be sure new reports are being written to zereod memory so
>    that the sanity check that report-id != 0 will continue to be valid.
> 
>    I've had it in mind to consider optimizing this at some point to minimize
>    how much of the buffer is cleared, maybe just for the _DISABLE/_ENABLE
>    case where I'd expect the buffer will mostly be empty before disabling the
>    stream.

Or just make it clear that you are considering buffer reuse. Having the
memset here allows us to use non-shmemfs allocation, it wasn't that I
objected I just didn't understand the comment in the context of
allocation path.
-Chris
Chris Wilson April 23, 2016, 8:48 a.m. UTC | #17
On Thu, Apr 21, 2016 at 05:15:10PM +0100, Robert Bragg wrote:
>    On Wed, Apr 20, 2016 at 10:11 PM, Chris Wilson
>    <[1]chris@chris-wilson.co.uk> wrote:
> 
>      On Wed, Apr 20, 2016 at 03:23:10PM +0100, Robert Bragg wrote:
>      > +static void gen7_update_oacontrol_locked(struct drm_i915_private
>      *dev_priv)
>      > +{
>      > +     assert_spin_locked(&dev_priv->perf.hook_lock);
>      > +
>      > +     if (dev_priv->perf.oa.exclusive_stream->enabled) {
>      > +             unsigned long ctx_id = 0;
>      > +
>      > +             if (dev_priv->perf.oa.exclusive_stream->ctx)
>      > +                     ctx_id = dev_priv->perf.oa.specific_ctx_id;
>      > +
>      > +             if (dev_priv->perf.oa.exclusive_stream->ctx == NULL ||
>      ctx_id) {
>      > +                     bool periodic = dev_priv->perf.oa.periodic;
>      > +                     u32 period_exponent =
>      dev_priv->perf.oa.period_exponent;
>      > +                     u32 report_format =
>      dev_priv->perf.oa.oa_buffer.format;
>      > +
>      > +                     I915_WRITE(GEN7_OACONTROL,
>      > +                                (ctx_id & GEN7_OACONTROL_CTX_MASK) |
>      > +                                (period_exponent <<
>      > +                                 GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
>      > +                                (periodic ?
>      > +                                 GEN7_OACONTROL_TIMER_ENABLE : 0) |
>      > +                                (report_format <<
>      > +                                 GEN7_OACONTROL_FORMAT_SHIFT) |
>      > +                                (ctx_id ?
>      > +                                 GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
>      > +                                GEN7_OACONTROL_ENABLE);
> 
>      So this works by only recording when the OACONTROL context address
>      matches the CCID.
> 
>      Rather than hooking into switch context and checking every batch whether
>      you have the exclusive context in case it changed address, you could
>      just pin the exclusive context when told by the user to bind perf to
>      that context. Then it will also have the same address until oa is
>      finished (and releases it vma pin).
> 
>    Yeah, this was the approach I first went with when the driver was perf
>    based, though we ended up deciding to got with hooking into pinning and
>    updating the OA state in the end.
> 
>    E.g. for reference:
>    [2]https://lists.freedesktop.org/archives/intel-gfx/2014-November/055385.html
>    (wow, sad face after seeing how long I've been kicking this stuff)
> 
>    I'd prefer to stick with this approach now, unless you see a big problem
>    with it.

Given no reason to have the hook, I don't see why we should. Pinning the
context in the GGTT and causing that bit of extra fragmenetation isn't
the worst evil here. and is better practice overall to treat the OA
register as holding the pin on the object is it referencing, along with
lifetime tracking of that register (i.e. unpinning only when we know it
has completed its writes). Given that, the pin_notify is inadequate.
-Chris
Martin Peres April 23, 2016, 10:34 a.m. UTC | #18
On 20/04/16 17:23, Robert Bragg wrote:
> Gen graphics hardware can be set up to periodically write snapshots of
> performance counters into a circular buffer via its Observation
> Architecture and this patch exposes that capability to userspace via the
> i915 perf interface.
>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Robert Bragg <robert@sixbynine.org>
> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
> ---
>   drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>   drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>   drivers/gpu/drm/i915/i915_perf.c        | 940 +++++++++++++++++++++++++++++++-
>   drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>   include/uapi/drm/i915_drm.h             |  70 ++-
>   5 files changed, 1408 insertions(+), 20 deletions(-)
>
> +
> +
> +	/* It takes a fairly long time for a new MUX configuration to
> +	 * be be applied after these register writes. This delay
> +	 * duration was derived empirically based on the render_basic
> +	 * config but hopefully it covers the maximum configuration
> +	 * latency...
> +	 */
> +	mdelay(100);

With such a HW and SW design, how can we ever expose hope to get any
kind of performance when we are trying to monitor different metrics on each
draw call? This may be acceptable for system monitoring, but it is 
problematic
for the GL extensions :s

Since it seems like we are going for a perf API, it means that for every 
change
of metrics, we need to flush the commands, wait for the GPU to be done, then
program the new set of metrics via an IOCTL, wait 100 ms, and then we may
resume rendering ... until the next change. We are talking about a 
latency of
6-7 frames at 60 Hz here... this is non-negligeable...

I understand that we have a ton of counters and we may hide latency by not
allowing using more than half of the counters for every draw call or 
frame, but
even then, this 100ms delay is killing this approach altogether.

To be honest, if it indeed is an HW bug, then the approach that Samuel 
Pitoiset
and I used for Nouveau involving pushing an handle representing a
pre-computed configuration to the command buffer so as a software method
can be ask the kernel to reprogram the counters with as little idle time as
possible, would be useless as waiting for the GPU to be idle would 
usually not
take more than a few ms... which is nothing compared to waiting 100ms.

So, now, the elephant in the room, how can it take that long to apply the
change? Are the OA registers double buffered (NVIDIA's are, so as we can
reconfigure and start monitoring multiple counters at the same time)?

Maybe this 100ms is the polling period and the HW does not allow changing
the configuration in the middle of a polling session. In this case, this 
delay
should be dependent on the polling frequency. But even then, I would really
hope that the HW would allow us to tear down everything, reconfigure and
start polling again without waiting for the next tick. If not possible, 
maybe we
can change the frequency for the polling clock to make the polling event 
happen
sooner.

HW delays are usually a few microseconds, not milliseconds, that really 
suggests
that something funny is happening and the HW design is not understood 
properly.
If the documentation has nothing on this and the HW teams cannot help, 
then I
suggest a little REing session. I really want to see this work land, but 
the way I see
it right now is that we cannot rely on it because of this bug. Maybe 
fixing this bug
would require changing the architecture, so better address it before 
landing the
patches.

Worst case scenario, do not hesitate to contact me if non of the proposed
explanation pans out, I will take the time to read through the OA 
material and try my
REing skills on it. As I said, I really want to see this upstream!

Sorry...

Martin
Robert Bragg May 3, 2016, 7:34 p.m. UTC | #19
Sorry for the delay replying to this, I missed it.

On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr> wrote:

> On 20/04/16 17:23, Robert Bragg wrote:
>
>> Gen graphics hardware can be set up to periodically write snapshots of
>> performance counters into a circular buffer via its Observation
>> Architecture and this patch exposes that capability to userspace via the
>> i915 perf interface.
>>
>> Cc: Chris Wilson <chris@chris-wilson.co.uk>
>> Signed-off-by: Robert Bragg <robert@sixbynine.org>
>> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
>> ---
>>   drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>>   drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>>   drivers/gpu/drm/i915/i915_perf.c        | 940
>> +++++++++++++++++++++++++++++++-
>>   drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>>   include/uapi/drm/i915_drm.h             |  70 ++-
>>   5 files changed, 1408 insertions(+), 20 deletions(-)
>>
>> +
>> +
>> +       /* It takes a fairly long time for a new MUX configuration to
>> +        * be be applied after these register writes. This delay
>> +        * duration was derived empirically based on the render_basic
>> +        * config but hopefully it covers the maximum configuration
>> +        * latency...
>> +        */
>> +       mdelay(100);
>>
>
> With such a HW and SW design, how can we ever expose hope to get any
> kind of performance when we are trying to monitor different metrics on each
> draw call? This may be acceptable for system monitoring, but it is
> problematic
> for the GL extensions :s
>

> Since it seems like we are going for a perf API, it means that for every
> change
> of metrics, we need to flush the commands, wait for the GPU to be done,
> then
> program the new set of metrics via an IOCTL, wait 100 ms, and then we may
> resume rendering ... until the next change. We are talking about a latency
> of
> 6-7 frames at 60 Hz here... this is non-negligeable...
>

> I understand that we have a ton of counters and we may hide latency by not
> allowing using more than half of the counters for every draw call or
> frame, but
> even then, this 100ms delay is killing this approach altogether.
>


Although I'm also really unhappy about introducing this delay recently, the
impact of the delay is typically amortized somewhat by keeping a
configuration open as long as possible.

Even without this explicit delay here the OA unit isn't suited to being
reconfigured on a per draw call basis, though it is able to support per
draw call queries with the same config.

The above assessment assumes wanting to change config between draw calls
which is not something this driver aims to support - as the HW isn't really
designed for that model.

E.g. in the case of INTEL_performance_query, the backend keeps the i915
perf stream open until all OA based query objects are deleted - so you have
to be pretty explicit if you want to change config.

Considering the sets available on Haswell:
* Render Metrics Basic
* Compute Metrics Basic
* Compute Metrics Extended
* Memory Reads Distribution
* Memory Writes Distribution
* Metric set SamplerBalance

Each of these configs can expose around 50 counters as a set.

A GL application is most likely just going to use the render basic set, and
In the case of a tool like gputop/GPA then changing config would usually be
driven by some user interaction to select a set of metrics, where even a
100ms delay will go unnoticed.

In case you aren't familiar with how the GL_INTEL_performance_query side of
things works for OA counters; one thing to be aware of is that there's a
separate MI_REPORT_PERF_COUNT command that Mesa writes either side of a
query which writes all the counters for the current OA config (as
configured via this i915 perf interface) to a buffer. In addition to
collecting reports via MI_REPORT_PERF_COUNT Mesa also configures the unit
for periodic sampling to be able to account for potential counter overflow.


It also might be worth keeping in mind that per draw queries will anyway
trash the pipelining of work, since it's necessary to put stalls between
the draw calls to avoid conflated metrics (not to do with the details of
this driver) so use cases will probably be limited to those that just want
the draw call numbers but don't mind ruining overall frame/application
performance. Periodic sampling or swap-to-swap queries would be better
suited to cases that should minimize their impact.


>
> To be honest, if it indeed is an HW bug, then the approach that Samuel
> Pitoiset
> and I used for Nouveau involving pushing an handle representing a
> pre-computed configuration to the command buffer so as a software method
> can be ask the kernel to reprogram the counters with as little idle time as
> possible, would be useless as waiting for the GPU to be idle would usually
> not
> take more than a few ms... which is nothing compared to waiting 100ms.
>

Yeah, I think this is a really quite different programming model to what
the OA unit is geared for, even if we can somehow knock out this 100ms MUX
config delay.


>
> So, now, the elephant in the room, how can it take that long to apply the
> change? Are the OA registers double buffered (NVIDIA's are, so as we can
> reconfigure and start monitoring multiple counters at the same time)?
>

Based on my understanding of how the HW works internally I can see how some
delay would be expected, but can't currently fathom why it would need to
have this order of magnitude, and so the delay is currently simply based on
experimentation where I was getting unit test failures at 10ms, for invalid
looking reports, but the tests ran reliably at 100ms.

OA configuration state isn't double buffered to allow configuration while
in use.



>
> Maybe this 100ms is the polling period and the HW does not allow changing
> the configuration in the middle of a polling session. In this case, this
> delay
> should be dependent on the polling frequency. But even then, I would really
> hope that the HW would allow us to tear down everything, reconfigure and
> start polling again without waiting for the next tick. If not possible,
> maybe we
> can change the frequency for the polling clock to make the polling event
> happen
> sooner.
>

The tests currently test periods from 160ns to 168 milliseconds while the
delay required falls somewhere between 10 and 100 milliseconds. I think I'd
expect the delay to be > all periods tested if this was the link.

Generally this seems unlikely to me, in part considering how the MUX isn't
really part of the OA unit that handles periodic sampling. I wouldn't rule
out some interaction though so some experimenting along these lines could
be interesting.


>
> HW delays are usually a few microseconds, not milliseconds, that really
> suggests
> that something funny is happening and the HW design is not understood
> properly.
>

Yup.

Although I understand more about the HW than I can write up here, I can't
currently see why the HW should ever really take this long to apply a MUX
config - although I can see why some delay would be required.

It's on my list of things to try and get feedback/ideas on from the OA
architect/HW engineers. I brought this up briefly some time ago but we
didn't have time to go into details.



> If the documentation has nothing on this and the HW teams cannot help,
> then I
> suggest a little REing session


There's no precisely documented delay requirement. Insofar as REing is the
process of inferring how black box HW works through poking it with a stick
and seeing how it reacts, then yep more of that may be necessary. At least
in this case the HW isn't really a black box (maybe stain glass), where I
hopefully have a fairly good sense of how the HW is designed and can prod
folks closer to the HW for feedback/ideas.

So far I haven't spent too long investigating this besides recently homing
in on needing a delay here when my unit tests were failing.


> I really want to see this work land, but the way I see
> it right now is that we cannot rely on it because of this bug. Maybe
> fixing this bug
> would require changing the architecture, so better address it before
> landing the
> patches.
>

I think it's unlikely to change the architecture; rather we might just find
some other things to frob that make the MUX config apply faster (e.g. clock
gating issue); we find a way to get explicit feedback of completion so we
can minimize the delay or a better understanding that lets us choose a
shorter delay in most cases.

The driver is already usable with gputop with this delay and considering
how config changes are typically associated with user interaction I
wouldn't see this as a show stopper - even though it's not ideal. I think
the assertions about it being unusable with GL, were a little overstated
based on making frequent OA config changes which is not really how the
interface is intended to be used.


Thanks for starting to take a look through the code.

Kind Regards,
- Robert


> Worst case scenario, do not hesitate to contact me if non of the proposed
> explanation pans out, I will take the time to read through the OA material
> and try my
> REing skills on it. As I said, I really want to see this upstream!
>

> Sorry...
>
> Martin
>
Robert Bragg May 3, 2016, 8:03 p.m. UTC | #20
On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org> wrote:

> Sorry for the delay replying to this, I missed it.
>
> On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr>
> wrote:
>
>> On 20/04/16 17:23, Robert Bragg wrote:
>>
>>> Gen graphics hardware can be set up to periodically write snapshots of
>>> performance counters into a circular buffer via its Observation
>>> Architecture and this patch exposes that capability to userspace via the
>>> i915 perf interface.
>>>
>>> Cc: Chris Wilson <chris@chris-wilson.co.uk>
>>> Signed-off-by: Robert Bragg <robert@sixbynine.org>
>>> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
>>> ---
>>>   drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>>>   drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>>>   drivers/gpu/drm/i915/i915_perf.c        | 940
>>> +++++++++++++++++++++++++++++++-
>>>   drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>>>   include/uapi/drm/i915_drm.h             |  70 ++-
>>>   5 files changed, 1408 insertions(+), 20 deletions(-)
>>>
>>> +
>>> +
>>> +       /* It takes a fairly long time for a new MUX configuration to
>>> +        * be be applied after these register writes. This delay
>>> +        * duration was derived empirically based on the render_basic
>>> +        * config but hopefully it covers the maximum configuration
>>> +        * latency...
>>> +        */
>>> +       mdelay(100);
>>>
>>
>> With such a HW and SW design, how can we ever expose hope to get any
>> kind of performance when we are trying to monitor different metrics on
>> each
>> draw call? This may be acceptable for system monitoring, but it is
>> problematic
>> for the GL extensions :s
>>
>
>> Since it seems like we are going for a perf API, it means that for every
>> change
>> of metrics, we need to flush the commands, wait for the GPU to be done,
>> then
>> program the new set of metrics via an IOCTL, wait 100 ms, and then we may
>> resume rendering ... until the next change. We are talking about a
>> latency of
>> 6-7 frames at 60 Hz here... this is non-negligeable...
>>
>
>> I understand that we have a ton of counters and we may hide latency by not
>> allowing using more than half of the counters for every draw call or
>> frame, but
>> even then, this 100ms delay is killing this approach altogether.
>>
>
>
>
So revisiting this to double check how things fail with my latest
driver/tests without the delay, I apparently can't reproduce test failures
without the delay any more...

I think the explanation is that since first adding the delay to the driver
I also made the the driver a bit more careful to not forward spurious
reports that look invalid due to a zeroed report id field, and that
mechanism keeps the unit tests happy, even though there are still some
number of invalid reports generated if we don't wait.

One problem with simply having no delay is that the driver prints an error
if it sees an invalid reports so I get a lot of 'Skipping spurious, invalid
OA report' dmesg spam. Also this was intended more as a last resort
mechanism, and I wouldn't feel too happy about squashing the error message
and potentially sweeping other error cases under the carpet.

Experimenting to see if the delay can at least be reduced, I brought the
delay up in millisecond increments and found that although I still see a
lot of spurious reports only waiting 1 or 5 milliseconds, at 10
milliseconds its reduced quite a bit and at 15 milliseconds I don't seem to
have any errors.

15 milliseconds is still a long time, but at least not as long as 100.

Regards,
- Robert
Martin Peres May 4, 2016, 9:04 a.m. UTC | #21
On 03/05/16 22:34, Robert Bragg wrote:
> Sorry for the delay replying to this, I missed it.

No worries!

>
> On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr
> <mailto:martin.peres@free.fr>> wrote:
>
>     On 20/04/16 17:23, Robert Bragg wrote:
>
>         Gen graphics hardware can be set up to periodically write
>         snapshots of
>         performance counters into a circular buffer via its Observation
>         Architecture and this patch exposes that capability to userspace
>         via the
>         i915 perf interface.
>
>         Cc: Chris Wilson <chris@chris-wilson.co.uk
>         <mailto:chris@chris-wilson.co.uk>>
>         Signed-off-by: Robert Bragg <robert@sixbynine.org
>         <mailto:robert@sixbynine.org>>
>         Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
>         <mailto:zhenyuw@linux.intel.com>>
>         ---
>           drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>           drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>           drivers/gpu/drm/i915/i915_perf.c        | 940
>         +++++++++++++++++++++++++++++++-
>           drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>           include/uapi/drm/i915_drm.h             |  70 ++-
>           5 files changed, 1408 insertions(+), 20 deletions(-)
>
>         +
>         +
>         +       /* It takes a fairly long time for a new MUX
>         configuration to
>         +        * be be applied after these register writes. This delay
>         +        * duration was derived empirically based on the
>         render_basic
>         +        * config but hopefully it covers the maximum configuration
>         +        * latency...
>         +        */
>         +       mdelay(100);
>
>
>     With such a HW and SW design, how can we ever expose hope to get any
>     kind of performance when we are trying to monitor different metrics
>     on each
>     draw call? This may be acceptable for system monitoring, but it is
>     problematic
>     for the GL extensions :s
>
>
>     Since it seems like we are going for a perf API, it means that for
>     every change
>     of metrics, we need to flush the commands, wait for the GPU to be
>     done, then
>     program the new set of metrics via an IOCTL, wait 100 ms, and then
>     we may
>     resume rendering ... until the next change. We are talking about a
>     latency of
>     6-7 frames at 60 Hz here... this is non-negligeable...
>
>
>     I understand that we have a ton of counters and we may hide latency
>     by not
>     allowing using more than half of the counters for every draw call or
>     frame, but
>     even then, this 100ms delay is killing this approach altogether.
>
>
>
> Although I'm also really unhappy about introducing this delay recently,
> the impact of the delay is typically amortized somewhat by keeping a
> configuration open as long as possible.
>
> Even without this explicit delay here the OA unit isn't suited to being
> reconfigured on a per draw call basis, though it is able to support per
> draw call queries with the same config.
>
> The above assessment assumes wanting to change config between draw calls
> which is not something this driver aims to support - as the HW isn't
> really designed for that model.
>
> E.g. in the case of INTEL_performance_query, the backend keeps the i915
> perf stream open until all OA based query objects are deleted - so you
> have to be pretty explicit if you want to change config.

OK, I get your point. However, I still want to state that applications 
changing the set would see a disastrous effect as a 100 ms is enough to 
downclock both the CPU and GPU and that would dramatically alter the
metrics. Should we make it clear somewhere, either in the 
INTEL_performance_query or as a warning in mesa_performance if changing 
the set while running? I would think the latter would be preferable as 
it could also cover the case of the AMD extension which, IIRC, does not 
talk about the performance cost of changing the metrics. With this 
caveat made clear, it seems reasonable.

>
> Considering the sets available on Haswell:
> * Render Metrics Basic
> * Compute Metrics Basic
> * Compute Metrics Extended
> * Memory Reads Distribution
> * Memory Writes Distribution
> * Metric set SamplerBalance
>
> Each of these configs can expose around 50 counters as a set.
>
> A GL application is most likely just going to use the render basic set,
> and In the case of a tool like gputop/GPA then changing config would
> usually be driven by some user interaction to select a set of metrics,
> where even a 100ms delay will go unnoticed.

100 ms is becoming visible, but I agree, it would not be a show stopper 
for sure.

On the APITRACE side, this should not be an issue, because we do not 
change the set of metrics while running.

>
> In case you aren't familiar with how the GL_INTEL_performance_query side
> of things works for OA counters; one thing to be aware of is that
> there's a separate MI_REPORT_PERF_COUNT command that Mesa writes either
> side of a query which writes all the counters for the current OA config
> (as configured via this i915 perf interface) to a buffer. In addition to
> collecting reports via MI_REPORT_PERF_COUNT Mesa also configures the
> unit for periodic sampling to be able to account for potential counter
> overflow.

Oh, the overflow case is mean. Doesn't the spec mandate the application 
to read at least every second? This is the case for the timestamp queries.

>
>
> It also might be worth keeping in mind that per draw queries will anyway
> trash the pipelining of work, since it's necessary to put stalls between
> the draw calls to avoid conflated metrics (not to do with the details of
> this driver) so use cases will probably be limited to those that just
> want the draw call numbers but don't mind ruining overall
> frame/application performance. Periodic sampling or swap-to-swap queries
> would be better suited to cases that should minimize their impact.

Yes, I agree that there will always be a cost, but with the design 
implemented in nouveau (which barely involves the CPU at all), the 
pipelining is almost unaffected. As in, monitoring every draw call with 
a different metric would lower the performance of glxgears (worst case I 
could think off) but still keep thousands of FPS.
>
>
>
>     To be honest, if it indeed is an HW bug, then the approach that
>     Samuel Pitoiset
>     and I used for Nouveau involving pushing an handle representing a
>     pre-computed configuration to the command buffer so as a software method
>     can be ask the kernel to reprogram the counters with as little idle
>     time as
>     possible, would be useless as waiting for the GPU to be idle would
>     usually not
>     take more than a few ms... which is nothing compared to waiting 100ms.
>
>
> Yeah, I think this is a really quite different programming model to what
> the OA unit is geared for, even if we can somehow knock out this 100ms
> MUX config delay.

Too bad :)

>
>
>
>     So, now, the elephant in the room, how can it take that long to
>     apply the
>     change? Are the OA registers double buffered (NVIDIA's are, so as we can
>     reconfigure and start monitoring multiple counters at the same time)?
>
>
> Based on my understanding of how the HW works internally I can see how
> some delay would be expected, but can't currently fathom why it would
> need to have this order of magnitude, and so the delay is currently
> simply based on experimentation where I was getting unit test failures
> at 10ms, for invalid looking reports, but the tests ran reliably at 100ms.
>
> OA configuration state isn't double buffered to allow configuration
> while in use.
>
>
>
>
>     Maybe this 100ms is the polling period and the HW does not allow
>     changing
>     the configuration in the middle of a polling session. In this case,
>     this delay
>     should be dependent on the polling frequency. But even then, I would
>     really
>     hope that the HW would allow us to tear down everything, reconfigure and
>     start polling again without waiting for the next tick. If not
>     possible, maybe we
>     can change the frequency for the polling clock to make the polling
>     event happen
>     sooner.
>
>
> The tests currently test periods from 160ns to 168 milliseconds while
> the delay required falls somewhere between 10 and 100 milliseconds. I
> think I'd expect the delay to be > all periods tested if this was the link.

Thanks, definitely the kind of information that is valuable for 
understanding this issue!

>
> Generally this seems unlikely to me, in part considering how the MUX
> isn't really part of the OA unit that handles periodic sampling. I
> wouldn't rule out some interaction though so some experimenting along
> these lines could be interesting.

That indeed makes it less likely. Interactions increase the BOM!

>
>
>
>     HW delays are usually a few microseconds, not milliseconds, that
>     really suggests
>     that something funny is happening and the HW design is not
>     understood properly.
>
>
> Yup.
>
> Although I understand more about the HW than I can write up here, I
> can't currently see why the HW should ever really take this long to
> apply a MUX config - although I can see why some delay would be required.
>
> It's on my list of things to try and get feedback/ideas on from the OA
> architect/HW engineers. I brought this up briefly some time ago but we
> didn't have time to go into details.

Sounds like a good idea!

>
>
>
>     If the documentation has nothing on this and the HW teams cannot
>     help, then I
>     suggest a little REing session
>
>
> There's no precisely documented delay requirement. Insofar as REing is
> the process of inferring how black box HW works through poking it with a
> stick and seeing how it reacts, then yep more of that may be necessary.
> At least in this case the HW isn't really a black box (maybe stain
> glass), where I hopefully have a fairly good sense of how the HW is
> designed and can prod folks closer to the HW for feedback/ideas.
>
> So far I haven't spent too long investigating this besides recently
> homing in on needing a delay here when my unit tests were failing.

ACK! Thanks for the info!

>
>
>     I really want to see this work land, but the way I see
>     it right now is that we cannot rely on it because of this bug. Maybe
>     fixing this bug
>     would require changing the architecture, so better address it before
>     landing the
>     patches.
>
>
> I think it's unlikely to change the architecture; rather we might just
> find some other things to frob that make the MUX config apply faster
> (e.g. clock gating issue); we find a way to get explicit feedback of
> completion so we can minimize the delay or a better understanding that
> lets us choose a shorter delay in most cases.

Yes, clock gating may be one issue here, even though it would be a funny 
hw design to clock gate the bus to a register...

>
> The driver is already usable with gputop with this delay and considering
> how config changes are typically associated with user interaction I
> wouldn't see this as a show stopper - even though it's not ideal. I
> think the assertions about it being unusable with GL, were a little
> overstated based on making frequent OA config changes which is not
> really how the interface is intended to be used.

Yeah, but a performance warning in mesa, I would be OK with this change. 
Thanks for taking the time to explain!
>
>
> Thanks for starting to take a look through the code.
>
> Kind Regards,
> - Robert

Martin
Martin Peres May 4, 2016, 9:09 a.m. UTC | #22
On 03/05/16 23:03, Robert Bragg wrote:
>
>
> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
> <mailto:robert@sixbynine.org>> wrote:
>
>     Sorry for the delay replying to this, I missed it.
>
>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr
>     <mailto:martin.peres@free.fr>> wrote:
>
>         On 20/04/16 17:23, Robert Bragg wrote:
>
>             Gen graphics hardware can be set up to periodically write
>             snapshots of
>             performance counters into a circular buffer via its Observation
>             Architecture and this patch exposes that capability to
>             userspace via the
>             i915 perf interface.
>
>             Cc: Chris Wilson <chris@chris-wilson.co.uk
>             <mailto:chris@chris-wilson.co.uk>>
>             Signed-off-by: Robert Bragg <robert@sixbynine.org
>             <mailto:robert@sixbynine.org>>
>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
>             <mailto:zhenyuw@linux.intel.com>>
>             ---
>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>               drivers/gpu/drm/i915/i915_perf.c        | 940
>             +++++++++++++++++++++++++++++++-
>               drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>               include/uapi/drm/i915_drm.h             |  70 ++-
>               5 files changed, 1408 insertions(+), 20 deletions(-)
>
>             +
>             +
>             +       /* It takes a fairly long time for a new MUX
>             configuration to
>             +        * be be applied after these register writes. This delay
>             +        * duration was derived empirically based on the
>             render_basic
>             +        * config but hopefully it covers the maximum
>             configuration
>             +        * latency...
>             +        */
>             +       mdelay(100);
>
>
>         With such a HW and SW design, how can we ever expose hope to get any
>         kind of performance when we are trying to monitor different
>         metrics on each
>         draw call? This may be acceptable for system monitoring, but it
>         is problematic
>         for the GL extensions :s
>
>
>         Since it seems like we are going for a perf API, it means that
>         for every change
>         of metrics, we need to flush the commands, wait for the GPU to
>         be done, then
>         program the new set of metrics via an IOCTL, wait 100 ms, and
>         then we may
>         resume rendering ... until the next change. We are talking about
>         a latency of
>         6-7 frames at 60 Hz here... this is non-negligeable...
>
>
>         I understand that we have a ton of counters and we may hide
>         latency by not
>         allowing using more than half of the counters for every draw
>         call or frame, but
>         even then, this 100ms delay is killing this approach altogether.
>
>
>
>
> So revisiting this to double check how things fail with my latest
> driver/tests without the delay, I apparently can't reproduce test
> failures without the delay any more...
>
> I think the explanation is that since first adding the delay to the
> driver I also made the the driver a bit more careful to not forward
> spurious reports that look invalid due to a zeroed report id field, and
> that mechanism keeps the unit tests happy, even though there are still
> some number of invalid reports generated if we don't wait.
>
> One problem with simply having no delay is that the driver prints an
> error if it sees an invalid reports so I get a lot of 'Skipping
> spurious, invalid OA report' dmesg spam. Also this was intended more as
> a last resort mechanism, and I wouldn't feel too happy about squashing
> the error message and potentially sweeping other error cases under the
> carpet.
>
> Experimenting to see if the delay can at least be reduced, I brought the
> delay up in millisecond increments and found that although I still see a
> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
> milliseconds its reduced quite a bit and at 15 milliseconds I don't seem
> to have any errors.
>
> 15 milliseconds is still a long time, but at least not as long as 100.

OK, so the issue does not come from the HW after all, great!

Now, my main question is, why are spurious events generated when 
changing the MUX's value? I can understand that we would need to ignore 
the reading that came right after the change, but other than this,  I am 
a bit at a loss.

I am a bit swamped with other tasks right now, but I would love to spend 
more time reviewing your code as I really want to see this upstream!

Martin
Robert Bragg May 4, 2016, 9:49 a.m. UTC | #23
On Wed, May 4, 2016 at 10:09 AM, Martin Peres <martin.peres@linux.intel.com>
wrote:

> On 03/05/16 23:03, Robert Bragg wrote:
>
>>
>>
>> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
>> <mailto:robert@sixbynine.org>> wrote:
>>
>>     Sorry for the delay replying to this, I missed it.
>>
>>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr
>>     <mailto:martin.peres@free.fr>> wrote:
>>
>>         On 20/04/16 17:23, Robert Bragg wrote:
>>
>>             Gen graphics hardware can be set up to periodically write
>>             snapshots of
>>             performance counters into a circular buffer via its
>> Observation
>>             Architecture and this patch exposes that capability to
>>             userspace via the
>>             i915 perf interface.
>>
>>             Cc: Chris Wilson <chris@chris-wilson.co.uk
>>             <mailto:chris@chris-wilson.co.uk>>
>>             Signed-off-by: Robert Bragg <robert@sixbynine.org
>>             <mailto:robert@sixbynine.org>>
>>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
>>             <mailto:zhenyuw@linux.intel.com>>
>>
>>             ---
>>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>>               drivers/gpu/drm/i915/i915_perf.c        | 940
>>             +++++++++++++++++++++++++++++++-
>>               drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>>               include/uapi/drm/i915_drm.h             |  70 ++-
>>               5 files changed, 1408 insertions(+), 20 deletions(-)
>>
>>             +
>>             +
>>             +       /* It takes a fairly long time for a new MUX
>>             configuration to
>>             +        * be be applied after these register writes. This
>> delay
>>             +        * duration was derived empirically based on the
>>             render_basic
>>             +        * config but hopefully it covers the maximum
>>             configuration
>>             +        * latency...
>>             +        */
>>             +       mdelay(100);
>>
>>
>>         With such a HW and SW design, how can we ever expose hope to get
>> any
>>         kind of performance when we are trying to monitor different
>>         metrics on each
>>         draw call? This may be acceptable for system monitoring, but it
>>         is problematic
>>         for the GL extensions :s
>>
>>
>>         Since it seems like we are going for a perf API, it means that
>>         for every change
>>         of metrics, we need to flush the commands, wait for the GPU to
>>         be done, then
>>         program the new set of metrics via an IOCTL, wait 100 ms, and
>>         then we may
>>         resume rendering ... until the next change. We are talking about
>>         a latency of
>>         6-7 frames at 60 Hz here... this is non-negligeable...
>>
>>
>>         I understand that we have a ton of counters and we may hide
>>         latency by not
>>         allowing using more than half of the counters for every draw
>>         call or frame, but
>>         even then, this 100ms delay is killing this approach altogether.
>>
>>
>>
>>
>> So revisiting this to double check how things fail with my latest
>> driver/tests without the delay, I apparently can't reproduce test
>> failures without the delay any more...
>>
>> I think the explanation is that since first adding the delay to the
>> driver I also made the the driver a bit more careful to not forward
>> spurious reports that look invalid due to a zeroed report id field, and
>> that mechanism keeps the unit tests happy, even though there are still
>> some number of invalid reports generated if we don't wait.
>>
>> One problem with simply having no delay is that the driver prints an
>> error if it sees an invalid reports so I get a lot of 'Skipping
>> spurious, invalid OA report' dmesg spam. Also this was intended more as
>> a last resort mechanism, and I wouldn't feel too happy about squashing
>> the error message and potentially sweeping other error cases under the
>> carpet.
>>
>> Experimenting to see if the delay can at least be reduced, I brought the
>> delay up in millisecond increments and found that although I still see a
>> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
>> milliseconds its reduced quite a bit and at 15 milliseconds I don't seem
>> to have any errors.
>>
>> 15 milliseconds is still a long time, but at least not as long as 100.
>>
>
> OK, so the issue does not come from the HW after all, great!
>

Erm, I'm not sure that's a conclusion we can make here...

The upshot here was really just reducing the delay from 100ms to 15ms.
Previously I arrived at a workable delay by jumping the delay in orders of
magnitude with 10ms failing, 100ms passing and I didn't try and refine it
further. Here I've looked at delays between 10 and 100ms.

The other thing is observing that because the kernel is checking for
invalid reports (generated by the hardware) before forwarding to userspace
the lack of a delay no longer triggers i-g-t failures because the invalid
data won't reach i-g-t any more - though the invalid reports are still a
thing to avoid.


> Now, my main question is, why are spurious events generated when changing
> the MUX's value? I can understand that we would need to ignore the reading
> that came right after the change, but other than this,  I am a bit at a
> loss.
>

The MUX selects 16 signals that the OA unit can turn into 16 separate
counters by basically counting the signal changes. (there's some fancy
fixed function logic that can affect this but that's the general idea).

If the MUX is in the middle of being re-programmed then some subset of
those 16 signals are for who knows what.

After programming the MUX we will go on to configure the OA unit and the
tests will enable periodic sampling which (if we have no delay) will sample
the OA counters that are currently being fed by undefined signals.

So as far as that goes it makes sense to me to expect bad data if we don't
wait for the MUX config to land properly. Something I don't really know is
how come we're seemingly lucky to have the reports be cleanly invalid with
a zero report-id, instead of just having junk data that would be harder to
recognise.


> I am a bit swamped with other tasks right now, but I would love to spend
> more time reviewing your code as I really want to see this upstream!
>

No worries.

I can hopefully send out my i-g-t tests this afternoon too which should
hopefully give us all the pieces to be able seriously consider hopefully
landing things soon.

Regards,
- Robert


> Martin
>
Robert Bragg May 4, 2016, 11:15 a.m. UTC | #24
On Wed, May 4, 2016 at 10:04 AM, Martin Peres <martin.peres@linux.intel.com>
wrote:

> On 03/05/16 22:34, Robert Bragg wrote:
>
>> Sorry for the delay replying to this, I missed it.
>>
>
> No worries!
>
>
>> On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr
>> <mailto:martin.peres@free.fr>> wrote:
>>
>>     On 20/04/16 17:23, Robert Bragg wrote:
>>
>>         Gen graphics hardware can be set up to periodically write
>>         snapshots of
>>         performance counters into a circular buffer via its Observation
>>         Architecture and this patch exposes that capability to userspace
>>         via the
>>         i915 perf interface.
>>
>>         Cc: Chris Wilson <chris@chris-wilson.co.uk
>>         <mailto:chris@chris-wilson.co.uk>>
>>         Signed-off-by: Robert Bragg <robert@sixbynine.org
>>         <mailto:robert@sixbynine.org>>
>>         Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
>>         <mailto:zhenyuw@linux.intel.com>>
>>
>>         ---
>>           drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>>           drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>>           drivers/gpu/drm/i915/i915_perf.c        | 940
>>         +++++++++++++++++++++++++++++++-
>>           drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
>>           include/uapi/drm/i915_drm.h             |  70 ++-
>>           5 files changed, 1408 insertions(+), 20 deletions(-)
>>
>>         +
>>         +
>>         +       /* It takes a fairly long time for a new MUX
>>         configuration to
>>         +        * be be applied after these register writes. This delay
>>         +        * duration was derived empirically based on the
>>         render_basic
>>         +        * config but hopefully it covers the maximum
>> configuration
>>         +        * latency...
>>         +        */
>>         +       mdelay(100);
>>
>>
>>     With such a HW and SW design, how can we ever expose hope to get any
>>     kind of performance when we are trying to monitor different metrics
>>     on each
>>     draw call? This may be acceptable for system monitoring, but it is
>>     problematic
>>     for the GL extensions :s
>>
>>
>>     Since it seems like we are going for a perf API, it means that for
>>     every change
>>     of metrics, we need to flush the commands, wait for the GPU to be
>>     done, then
>>     program the new set of metrics via an IOCTL, wait 100 ms, and then
>>     we may
>>     resume rendering ... until the next change. We are talking about a
>>     latency of
>>     6-7 frames at 60 Hz here... this is non-negligeable...
>>
>>
>>     I understand that we have a ton of counters and we may hide latency
>>     by not
>>     allowing using more than half of the counters for every draw call or
>>     frame, but
>>     even then, this 100ms delay is killing this approach altogether.
>>
>>
>>
>> Although I'm also really unhappy about introducing this delay recently,
>> the impact of the delay is typically amortized somewhat by keeping a
>> configuration open as long as possible.
>>
>> Even without this explicit delay here the OA unit isn't suited to being
>> reconfigured on a per draw call basis, though it is able to support per
>> draw call queries with the same config.
>>
>> The above assessment assumes wanting to change config between draw calls
>> which is not something this driver aims to support - as the HW isn't
>> really designed for that model.
>>
>> E.g. in the case of INTEL_performance_query, the backend keeps the i915
>> perf stream open until all OA based query objects are deleted - so you
>> have to be pretty explicit if you want to change config.
>>
>
> OK, I get your point. However, I still want to state that applications
> changing the set would see a disastrous effect as a 100 ms is enough to
> downclock both the CPU and GPU and that would dramatically alter the
> metrics. Should we make it clear somewhere, either in the
> INTEL_performance_query or as a warning in mesa_performance if changing the
> set while running? I would think the latter would be preferable as it could
> also cover the case of the AMD extension which, IIRC, does not talk about
> the performance cost of changing the metrics. With this caveat made clear,
> it seems reasonable.
>

Yeah a KHR_debug performance warning sounds like a good idea.


>
>
>> In case you aren't familiar with how the GL_INTEL_performance_query side
>> of things works for OA counters; one thing to be aware of is that
>> there's a separate MI_REPORT_PERF_COUNT command that Mesa writes either
>> side of a query which writes all the counters for the current OA config
>> (as configured via this i915 perf interface) to a buffer. In addition to
>> collecting reports via MI_REPORT_PERF_COUNT Mesa also configures the
>> unit for periodic sampling to be able to account for potential counter
>> overflow.
>>
>
> Oh, the overflow case is mean. Doesn't the spec mandate the application to
> read at least every second? This is the case for the timestamp queries.
>

For a Haswell GT3 system with 40EUs @ 1GHz some aggregate EU counters may
overflow their 32bits in approximately 40milliseconds. It should be pretty
unusual to see a draw call last that long, but not unimaginable. Might also
be a good draw call to focus on profiling too :-)

For Gen8+ a bunch of the A counters can be reported with 40bits to mitigate
this issue.


>
>
>>
>> It also might be worth keeping in mind that per draw queries will anyway
>> trash the pipelining of work, since it's necessary to put stalls between
>> the draw calls to avoid conflated metrics (not to do with the details of
>> this driver) so use cases will probably be limited to those that just
>> want the draw call numbers but don't mind ruining overall
>> frame/application performance. Periodic sampling or swap-to-swap queries
>> would be better suited to cases that should minimize their impact.
>>
>
> Yes, I agree that there will always be a cost, but with the design
> implemented in nouveau (which barely involves the CPU at all), the
> pipelining is almost unaffected. As in, monitoring every draw call with a
> different metric would lower the performance of glxgears (worst case I
> could think off) but still keep thousands of FPS.
>

I guess it just has different trade offs.

While it sounds like we have a typically higher cost to reconfigure OA (at
least if touching the MUX) once the config is fixed (which can be done
before measuring anything), then I guess the pipelining for queries might
be slightly better with MI_REPORT_PERF_COUNT commands than something
requiring interrupting + executing work on the cpu to switch config (even
if it's cheaper than an OA re-config). I guess nouveau would have the same
need to insert GPU pipeline stalls (just gpu syncing with gpu) to avoid
conflating neighbouring draw call metrics, and maybe the bubbles from those
that can swallow the latency of the software methods.

glxgears might not really exaggerate draw call pipeline stall issues with
only 6 cheap primitives per gear. glxgears hammers context switching more
so than drawing anything. I think a pessimal case would be an app that
depends on large numbers of draw calls per frame that each do enough real
work that stalling for their completion is also measurable.

Funnily enough enabling the OA unit with glxgears can be kind of
problematic for Gen8+ which automatically writes reports on context switch
due to the spam of generating all of those context switch reports.


>
>> The driver is already usable with gputop with this delay and considering
>> how config changes are typically associated with user interaction I
>> wouldn't see this as a show stopper - even though it's not ideal. I
>> think the assertions about it being unusable with GL, were a little
>> overstated based on making frequent OA config changes which is not
>> really how the interface is intended to be used.
>>
>
> Yeah, but a performance warning in mesa, I would be OK with this change.
> Thanks for taking the time to explain!


A performance warning sounds like a sensible idea yup.

Regards,
- Robert


>
>
>>
>> Thanks for starting to take a look through the code.
>>
>> Kind Regards,
>> - Robert
>>
>
> Martin
>
Daniel Vetter May 4, 2016, 12:24 p.m. UTC | #25
On Wed, May 04, 2016 at 10:49:53AM +0100, Robert Bragg wrote:
> On Wed, May 4, 2016 at 10:09 AM, Martin Peres <martin.peres@linux.intel.com>
> wrote:
> 
> > On 03/05/16 23:03, Robert Bragg wrote:
> >
> >>
> >>
> >> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
> >> <mailto:robert@sixbynine.org>> wrote:
> >>
> >>     Sorry for the delay replying to this, I missed it.
> >>
> >>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <martin.peres@free.fr
> >>     <mailto:martin.peres@free.fr>> wrote:
> >>
> >>         On 20/04/16 17:23, Robert Bragg wrote:
> >>
> >>             Gen graphics hardware can be set up to periodically write
> >>             snapshots of
> >>             performance counters into a circular buffer via its
> >> Observation
> >>             Architecture and this patch exposes that capability to
> >>             userspace via the
> >>             i915 perf interface.
> >>
> >>             Cc: Chris Wilson <chris@chris-wilson.co.uk
> >>             <mailto:chris@chris-wilson.co.uk>>
> >>             Signed-off-by: Robert Bragg <robert@sixbynine.org
> >>             <mailto:robert@sixbynine.org>>
> >>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
> >>             <mailto:zhenyuw@linux.intel.com>>
> >>
> >>             ---
> >>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
> >>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
> >>               drivers/gpu/drm/i915/i915_perf.c        | 940
> >>             +++++++++++++++++++++++++++++++-
> >>               drivers/gpu/drm/i915/i915_reg.h         | 338 ++++++++++++
> >>               include/uapi/drm/i915_drm.h             |  70 ++-
> >>               5 files changed, 1408 insertions(+), 20 deletions(-)
> >>
> >>             +
> >>             +
> >>             +       /* It takes a fairly long time for a new MUX
> >>             configuration to
> >>             +        * be be applied after these register writes. This
> >> delay
> >>             +        * duration was derived empirically based on the
> >>             render_basic
> >>             +        * config but hopefully it covers the maximum
> >>             configuration
> >>             +        * latency...
> >>             +        */
> >>             +       mdelay(100);
> >>
> >>
> >>         With such a HW and SW design, how can we ever expose hope to get
> >> any
> >>         kind of performance when we are trying to monitor different
> >>         metrics on each
> >>         draw call? This may be acceptable for system monitoring, but it
> >>         is problematic
> >>         for the GL extensions :s
> >>
> >>
> >>         Since it seems like we are going for a perf API, it means that
> >>         for every change
> >>         of metrics, we need to flush the commands, wait for the GPU to
> >>         be done, then
> >>         program the new set of metrics via an IOCTL, wait 100 ms, and
> >>         then we may
> >>         resume rendering ... until the next change. We are talking about
> >>         a latency of
> >>         6-7 frames at 60 Hz here... this is non-negligeable...
> >>
> >>
> >>         I understand that we have a ton of counters and we may hide
> >>         latency by not
> >>         allowing using more than half of the counters for every draw
> >>         call or frame, but
> >>         even then, this 100ms delay is killing this approach altogether.
> >>
> >>
> >>
> >>
> >> So revisiting this to double check how things fail with my latest
> >> driver/tests without the delay, I apparently can't reproduce test
> >> failures without the delay any more...
> >>
> >> I think the explanation is that since first adding the delay to the
> >> driver I also made the the driver a bit more careful to not forward
> >> spurious reports that look invalid due to a zeroed report id field, and
> >> that mechanism keeps the unit tests happy, even though there are still
> >> some number of invalid reports generated if we don't wait.
> >>
> >> One problem with simply having no delay is that the driver prints an
> >> error if it sees an invalid reports so I get a lot of 'Skipping
> >> spurious, invalid OA report' dmesg spam. Also this was intended more as
> >> a last resort mechanism, and I wouldn't feel too happy about squashing
> >> the error message and potentially sweeping other error cases under the
> >> carpet.
> >>
> >> Experimenting to see if the delay can at least be reduced, I brought the
> >> delay up in millisecond increments and found that although I still see a
> >> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
> >> milliseconds its reduced quite a bit and at 15 milliseconds I don't seem
> >> to have any errors.
> >>
> >> 15 milliseconds is still a long time, but at least not as long as 100.
> >>
> >
> > OK, so the issue does not come from the HW after all, great!
> >
> 
> Erm, I'm not sure that's a conclusion we can make here...
> 
> The upshot here was really just reducing the delay from 100ms to 15ms.
> Previously I arrived at a workable delay by jumping the delay in orders of
> magnitude with 10ms failing, 100ms passing and I didn't try and refine it
> further. Here I've looked at delays between 10 and 100ms.
> 
> The other thing is observing that because the kernel is checking for
> invalid reports (generated by the hardware) before forwarding to userspace
> the lack of a delay no longer triggers i-g-t failures because the invalid
> data won't reach i-g-t any more - though the invalid reports are still a
> thing to avoid.
> 
> 
> > Now, my main question is, why are spurious events generated when changing
> > the MUX's value? I can understand that we would need to ignore the reading
> > that came right after the change, but other than this,  I am a bit at a
> > loss.
> >
> 
> The MUX selects 16 signals that the OA unit can turn into 16 separate
> counters by basically counting the signal changes. (there's some fancy
> fixed function logic that can affect this but that's the general idea).
> 
> If the MUX is in the middle of being re-programmed then some subset of
> those 16 signals are for who knows what.
> 
> After programming the MUX we will go on to configure the OA unit and the
> tests will enable periodic sampling which (if we have no delay) will sample
> the OA counters that are currently being fed by undefined signals.
> 
> So as far as that goes it makes sense to me to expect bad data if we don't
> wait for the MUX config to land properly. Something I don't really know is
> how come we're seemingly lucky to have the reports be cleanly invalid with
> a zero report-id, instead of just having junk data that would be harder to
> recognise.

Yeah this mdelay story sounds realy scary. Few random comments:
- msleep instead of mdelay please
- no dmesg noise above debug level for stuff that we know can happen -
  dmesg noise counts as igt failures
- reading 0 sounds more like bad synchronization. Have you tried quiescing
  the entire gpu (to make sure nothing is happen) and disabling OA, then
  updating, and then restarting? At least on a very quick look I didn't
  spot that. Random delays freak me out a bit, but wouldn't be surprised
  if really needed.

Cheers, Daniel
Robert Bragg May 4, 2016, 1:24 p.m. UTC | #26
On Wed, May 4, 2016 at 1:24 PM, Daniel Vetter <daniel@ffwll.ch> wrote:

> On Wed, May 04, 2016 at 10:49:53AM +0100, Robert Bragg wrote:
> > On Wed, May 4, 2016 at 10:09 AM, Martin Peres <
> martin.peres@linux.intel.com>
> > wrote:
> >
> > > On 03/05/16 23:03, Robert Bragg wrote:
> > >
> > >>
> > >>
> > >> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
> > >> <mailto:robert@sixbynine.org>> wrote:
> > >>
> > >>     Sorry for the delay replying to this, I missed it.
> > >>
> > >>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <
> martin.peres@free.fr
> > >>     <mailto:martin.peres@free.fr>> wrote:
> > >>
> > >>         On 20/04/16 17:23, Robert Bragg wrote:
> > >>
> > >>             Gen graphics hardware can be set up to periodically write
> > >>             snapshots of
> > >>             performance counters into a circular buffer via its
> > >> Observation
> > >>             Architecture and this patch exposes that capability to
> > >>             userspace via the
> > >>             i915 perf interface.
> > >>
> > >>             Cc: Chris Wilson <chris@chris-wilson.co.uk
> > >>             <mailto:chris@chris-wilson.co.uk>>
> > >>             Signed-off-by: Robert Bragg <robert@sixbynine.org
> > >>             <mailto:robert@sixbynine.org>>
> > >>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
> > >>             <mailto:zhenyuw@linux.intel.com>>
> > >>
> > >>             ---
> > >>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
> > >>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
> > >>               drivers/gpu/drm/i915/i915_perf.c        | 940
> > >>             +++++++++++++++++++++++++++++++-
> > >>               drivers/gpu/drm/i915/i915_reg.h         | 338
> ++++++++++++
> > >>               include/uapi/drm/i915_drm.h             |  70 ++-
> > >>               5 files changed, 1408 insertions(+), 20 deletions(-)
> > >>
> > >>             +
> > >>             +
> > >>             +       /* It takes a fairly long time for a new MUX
> > >>             configuration to
> > >>             +        * be be applied after these register writes. This
> > >> delay
> > >>             +        * duration was derived empirically based on the
> > >>             render_basic
> > >>             +        * config but hopefully it covers the maximum
> > >>             configuration
> > >>             +        * latency...
> > >>             +        */
> > >>             +       mdelay(100);
> > >>
> > >>
> > >>         With such a HW and SW design, how can we ever expose hope to
> get
> > >> any
> > >>         kind of performance when we are trying to monitor different
> > >>         metrics on each
> > >>         draw call? This may be acceptable for system monitoring, but
> it
> > >>         is problematic
> > >>         for the GL extensions :s
> > >>
> > >>
> > >>         Since it seems like we are going for a perf API, it means that
> > >>         for every change
> > >>         of metrics, we need to flush the commands, wait for the GPU to
> > >>         be done, then
> > >>         program the new set of metrics via an IOCTL, wait 100 ms, and
> > >>         then we may
> > >>         resume rendering ... until the next change. We are talking
> about
> > >>         a latency of
> > >>         6-7 frames at 60 Hz here... this is non-negligeable...
> > >>
> > >>
> > >>         I understand that we have a ton of counters and we may hide
> > >>         latency by not
> > >>         allowing using more than half of the counters for every draw
> > >>         call or frame, but
> > >>         even then, this 100ms delay is killing this approach
> altogether.
> > >>
> > >>
> > >>
> > >>
> > >> So revisiting this to double check how things fail with my latest
> > >> driver/tests without the delay, I apparently can't reproduce test
> > >> failures without the delay any more...
> > >>
> > >> I think the explanation is that since first adding the delay to the
> > >> driver I also made the the driver a bit more careful to not forward
> > >> spurious reports that look invalid due to a zeroed report id field,
> and
> > >> that mechanism keeps the unit tests happy, even though there are still
> > >> some number of invalid reports generated if we don't wait.
> > >>
> > >> One problem with simply having no delay is that the driver prints an
> > >> error if it sees an invalid reports so I get a lot of 'Skipping
> > >> spurious, invalid OA report' dmesg spam. Also this was intended more
> as
> > >> a last resort mechanism, and I wouldn't feel too happy about squashing
> > >> the error message and potentially sweeping other error cases under the
> > >> carpet.
> > >>
> > >> Experimenting to see if the delay can at least be reduced, I brought
> the
> > >> delay up in millisecond increments and found that although I still
> see a
> > >> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
> > >> milliseconds its reduced quite a bit and at 15 milliseconds I don't
> seem
> > >> to have any errors.
> > >>
> > >> 15 milliseconds is still a long time, but at least not as long as 100.
> > >>
> > >
> > > OK, so the issue does not come from the HW after all, great!
> > >
> >
> > Erm, I'm not sure that's a conclusion we can make here...
> >
> > The upshot here was really just reducing the delay from 100ms to 15ms.
> > Previously I arrived at a workable delay by jumping the delay in orders
> of
> > magnitude with 10ms failing, 100ms passing and I didn't try and refine it
> > further. Here I've looked at delays between 10 and 100ms.
> >
> > The other thing is observing that because the kernel is checking for
> > invalid reports (generated by the hardware) before forwarding to
> userspace
> > the lack of a delay no longer triggers i-g-t failures because the invalid
> > data won't reach i-g-t any more - though the invalid reports are still a
> > thing to avoid.
> >
> >
> > > Now, my main question is, why are spurious events generated when
> changing
> > > the MUX's value? I can understand that we would need to ignore the
> reading
> > > that came right after the change, but other than this,  I am a bit at a
> > > loss.
> > >
> >
> > The MUX selects 16 signals that the OA unit can turn into 16 separate
> > counters by basically counting the signal changes. (there's some fancy
> > fixed function logic that can affect this but that's the general idea).
> >
> > If the MUX is in the middle of being re-programmed then some subset of
> > those 16 signals are for who knows what.
> >
> > After programming the MUX we will go on to configure the OA unit and the
> > tests will enable periodic sampling which (if we have no delay) will
> sample
> > the OA counters that are currently being fed by undefined signals.
> >
> > So as far as that goes it makes sense to me to expect bad data if we
> don't
> > wait for the MUX config to land properly. Something I don't really know
> is
> > how come we're seemingly lucky to have the reports be cleanly invalid
> with
> > a zero report-id, instead of just having junk data that would be harder
> to
> > recognise.
>
> Yeah this mdelay story sounds realy scary. Few random comments:
> - msleep instead of mdelay please
>

yup this was a mistake I'd forgotten to fix in this series, but is fixed in
the last series I sent after chris noticed too.

actually in my latest (yesterday after experimenting further with the delay
requirements) I'm using usleep_range for a delay between 15 and 20
milliseconds which seems to be enough.


> - no dmesg noise above debug level for stuff that we know can happen -
>   dmesg noise counts as igt failures
>

okey. I don't think I have anything above debug level, unless things are
going badly wrong.

Just double checking though has made me think twice about a WARN_ON in
gen7_oa_read for !oa_buffer_addr, which would be a bad situation but should
either be removed (never expected), be a BUG_ON (since the code would deref
NULL anyway) or more gracefully return an error if seen.

I currently have some DRM_DRIVER_DEBUG errors for cases where userspace
messes up what properties it gives to open a stream - hopefully that sound
ok? I've found it quite helpful to have a readable error for otherwise
vague EINVAL type errors.

I have a debug message I print if we see an invalid HW report, which
*shouldn't* happen but can happen (e.g. if the MUX delay or tail margin
aren't well tuned) and it's helpful to have the feedback, in case we end up
in a situation where we see this kind of message too frequently which might
indicate an issue to investigate.


> - reading 0 sounds more like bad synchronization.


I suppose I haven't thoroughly considered if we should return zero in any
case  - normally that would imply EOF so we get to choose what that implies
here. I don't think the driver should ever return 0 from read() currently.

A few concious choices re: read() return values have been:

- never ever return partial records (or rather even if a partial record
were literally copied into the userspace buffer, but an error were hit in
the middle of copying a full sample then that record wouldn't be accounted
for in the byte count returned.)

- Don't throw away records successfully copied, due to a later error. This
simplifies error handling paths internally and reporting
EAGAIN/ENOSPC/EFAULT errors and means data isn't lost. The precedence for
what we return is 1) did we successfully copy some reports? report bytes
copied for complete records. 2) did we encounter an error? report that if
so. 3) return -EAGAIN. (though for a blocking fd this will be handled
internally).



> Have you tried quiescing

the entire gpu (to make sure nothing is happen) and disabling OA, then
>   updating, and then restarting? At least on a very quick look I didn't
>   spot that. Random delays freak me out a bit, but wouldn't be surprised
>   if really needed.
>

Experimenting yesterday, it seems I can at least reduce the delay to around
15ms (granted that's still pretty huge), and it's also workable to have
userspace sleep for this time (despite the mdelay I originally went with)

Haven't tried this, but yeah could be interesting to experiment if the MUX
config lands faster in different situation such as when the HW is idle.

Thanks,
- Robert


>
> Cheers, Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>
Robert Bragg May 4, 2016, 1:33 p.m. UTC | #27
On Wed, May 4, 2016 at 2:24 PM, Robert Bragg <robert@sixbynine.org> wrote:

>
>
> On Wed, May 4, 2016 at 1:24 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>
>> On Wed, May 04, 2016 at 10:49:53AM +0100, Robert Bragg wrote:
>> > On Wed, May 4, 2016 at 10:09 AM, Martin Peres <
>> martin.peres@linux.intel.com>
>> > wrote:
>> >
>> > > On 03/05/16 23:03, Robert Bragg wrote:
>> > >
>> > >>
>> > >>
>> > >> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
>> > >> <mailto:robert@sixbynine.org>> wrote:
>> > >>
>> > >>     Sorry for the delay replying to this, I missed it.
>> > >>
>> > >>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <
>> martin.peres@free.fr
>> > >>     <mailto:martin.peres@free.fr>> wrote:
>> > >>
>> > >>         On 20/04/16 17:23, Robert Bragg wrote:
>> > >>
>> > >>             Gen graphics hardware can be set up to periodically write
>> > >>             snapshots of
>> > >>             performance counters into a circular buffer via its
>> > >> Observation
>> > >>             Architecture and this patch exposes that capability to
>> > >>             userspace via the
>> > >>             i915 perf interface.
>> > >>
>> > >>             Cc: Chris Wilson <chris@chris-wilson.co.uk
>> > >>             <mailto:chris@chris-wilson.co.uk>>
>> > >>             Signed-off-by: Robert Bragg <robert@sixbynine.org
>> > >>             <mailto:robert@sixbynine.org>>
>> > >>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
>> > >>             <mailto:zhenyuw@linux.intel.com>>
>> > >>
>> > >>             ---
>> > >>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
>> > >>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
>> > >>               drivers/gpu/drm/i915/i915_perf.c        | 940
>> > >>             +++++++++++++++++++++++++++++++-
>> > >>               drivers/gpu/drm/i915/i915_reg.h         | 338
>> ++++++++++++
>> > >>               include/uapi/drm/i915_drm.h             |  70 ++-
>> > >>               5 files changed, 1408 insertions(+), 20 deletions(-)
>> > >>
>> > >>             +
>> > >>             +
>> > >>             +       /* It takes a fairly long time for a new MUX
>> > >>             configuration to
>> > >>             +        * be be applied after these register writes.
>> This
>> > >> delay
>> > >>             +        * duration was derived empirically based on the
>> > >>             render_basic
>> > >>             +        * config but hopefully it covers the maximum
>> > >>             configuration
>> > >>             +        * latency...
>> > >>             +        */
>> > >>             +       mdelay(100);
>> > >>
>> > >>
>> > >>         With such a HW and SW design, how can we ever expose hope to
>> get
>> > >> any
>> > >>         kind of performance when we are trying to monitor different
>> > >>         metrics on each
>> > >>         draw call? This may be acceptable for system monitoring, but
>> it
>> > >>         is problematic
>> > >>         for the GL extensions :s
>> > >>
>> > >>
>> > >>         Since it seems like we are going for a perf API, it means
>> that
>> > >>         for every change
>> > >>         of metrics, we need to flush the commands, wait for the GPU
>> to
>> > >>         be done, then
>> > >>         program the new set of metrics via an IOCTL, wait 100 ms, and
>> > >>         then we may
>> > >>         resume rendering ... until the next change. We are talking
>> about
>> > >>         a latency of
>> > >>         6-7 frames at 60 Hz here... this is non-negligeable...
>> > >>
>> > >>
>> > >>         I understand that we have a ton of counters and we may hide
>> > >>         latency by not
>> > >>         allowing using more than half of the counters for every draw
>> > >>         call or frame, but
>> > >>         even then, this 100ms delay is killing this approach
>> altogether.
>> > >>
>> > >>
>> > >>
>> > >>
>> > >> So revisiting this to double check how things fail with my latest
>> > >> driver/tests without the delay, I apparently can't reproduce test
>> > >> failures without the delay any more...
>> > >>
>> > >> I think the explanation is that since first adding the delay to the
>> > >> driver I also made the the driver a bit more careful to not forward
>> > >> spurious reports that look invalid due to a zeroed report id field,
>> and
>> > >> that mechanism keeps the unit tests happy, even though there are
>> still
>> > >> some number of invalid reports generated if we don't wait.
>> > >>
>> > >> One problem with simply having no delay is that the driver prints an
>> > >> error if it sees an invalid reports so I get a lot of 'Skipping
>> > >> spurious, invalid OA report' dmesg spam. Also this was intended more
>> as
>> > >> a last resort mechanism, and I wouldn't feel too happy about
>> squashing
>> > >> the error message and potentially sweeping other error cases under
>> the
>> > >> carpet.
>> > >>
>> > >> Experimenting to see if the delay can at least be reduced, I brought
>> the
>> > >> delay up in millisecond increments and found that although I still
>> see a
>> > >> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
>> > >> milliseconds its reduced quite a bit and at 15 milliseconds I don't
>> seem
>> > >> to have any errors.
>> > >>
>> > >> 15 milliseconds is still a long time, but at least not as long as
>> 100.
>> > >>
>> > >
>> > > OK, so the issue does not come from the HW after all, great!
>> > >
>> >
>> > Erm, I'm not sure that's a conclusion we can make here...
>> >
>> > The upshot here was really just reducing the delay from 100ms to 15ms.
>> > Previously I arrived at a workable delay by jumping the delay in orders
>> of
>> > magnitude with 10ms failing, 100ms passing and I didn't try and refine
>> it
>> > further. Here I've looked at delays between 10 and 100ms.
>> >
>> > The other thing is observing that because the kernel is checking for
>> > invalid reports (generated by the hardware) before forwarding to
>> userspace
>> > the lack of a delay no longer triggers i-g-t failures because the
>> invalid
>> > data won't reach i-g-t any more - though the invalid reports are still a
>> > thing to avoid.
>> >
>> >
>> > > Now, my main question is, why are spurious events generated when
>> changing
>> > > the MUX's value? I can understand that we would need to ignore the
>> reading
>> > > that came right after the change, but other than this,  I am a bit at
>> a
>> > > loss.
>> > >
>> >
>> > The MUX selects 16 signals that the OA unit can turn into 16 separate
>> > counters by basically counting the signal changes. (there's some fancy
>> > fixed function logic that can affect this but that's the general idea).
>> >
>> > If the MUX is in the middle of being re-programmed then some subset of
>> > those 16 signals are for who knows what.
>> >
>> > After programming the MUX we will go on to configure the OA unit and the
>> > tests will enable periodic sampling which (if we have no delay) will
>> sample
>> > the OA counters that are currently being fed by undefined signals.
>> >
>> > So as far as that goes it makes sense to me to expect bad data if we
>> don't
>> > wait for the MUX config to land properly. Something I don't really know
>> is
>> > how come we're seemingly lucky to have the reports be cleanly invalid
>> with
>> > a zero report-id, instead of just having junk data that would be harder
>> to
>> > recognise.
>>
>> Yeah this mdelay story sounds realy scary. Few random comments:
>> - msleep instead of mdelay please
>>
>
> yup this was a mistake I'd forgotten to fix in this series, but is fixed
> in the last series I sent after chris noticed too.
>
> actually in my latest (yesterday after experimenting further with the
> delay requirements) I'm using usleep_range for a delay between 15 and 20
> milliseconds which seems to be enough.
>
>
>> - no dmesg noise above debug level for stuff that we know can happen -
>>   dmesg noise counts as igt failures
>>
>
> okey. I don't think I have anything above debug level, unless things are
> going badly wrong.
>
> Just double checking though has made me think twice about a WARN_ON in
> gen7_oa_read for !oa_buffer_addr, which would be a bad situation but should
> either be removed (never expected), be a BUG_ON (since the code would deref
> NULL anyway) or more gracefully return an error if seen.
>
> I currently have some DRM_DRIVER_DEBUG errors for cases where userspace
> messes up what properties it gives to open a stream - hopefully that sound
> ok? I've found it quite helpful to have a readable error for otherwise
> vague EINVAL type errors.
>
> I have a debug message I print if we see an invalid HW report, which
> *shouldn't* happen but can happen (e.g. if the MUX delay or tail margin
> aren't well tuned) and it's helpful to have the feedback, in case we end up
> in a situation where we see this kind of message too frequently which might
> indicate an issue to investigate.
>
>
>> - reading 0 sounds more like bad synchronization.
>
>
> I suppose I haven't thoroughly considered if we should return zero in any
> case  - normally that would imply EOF so we get to choose what that implies
> here. I don't think the driver should ever return 0 from read() currently.
>
> A few concious choices re: read() return values have been:
>
> - never ever return partial records (or rather even if a partial record
> were literally copied into the userspace buffer, but an error were hit in
> the middle of copying a full sample then that record wouldn't be accounted
> for in the byte count returned.)
>
> - Don't throw away records successfully copied, due to a later error. This
> simplifies error handling paths internally and reporting
> EAGAIN/ENOSPC/EFAULT errors and means data isn't lost. The precedence for
> what we return is 1) did we successfully copy some reports? report bytes
> copied for complete records. 2) did we encounter an error? report that if
> so. 3) return -EAGAIN. (though for a blocking fd this will be handled
> internally).
>
>
>
>> Have you tried quiescing
>
> the entire gpu (to make sure nothing is happen) and disabling OA, then
>>   updating, and then restarting? At least on a very quick look I didn't
>>   spot that. Random delays freak me out a bit, but wouldn't be surprised
>>   if really needed.
>>
>
> Experimenting yesterday, it seems I can at least reduce the delay to
> around 15ms (granted that's still pretty huge), and it's also workable to
> have userspace sleep for this time (despite the mdelay I originally went
> with)
>
> Haven't tried this, but yeah could be interesting to experiment if the MUX
> config lands faster in different situation such as when the HW is idle.
>

Hmm, maybe a stretch, but 15ms is perhaps coincidentally close to the
vblank period, the MUX relates to a fabric across the whole gpu... not
totally in-plausible there could be an interaction there too. another one
to experiment with.

- Robert


>
> Thanks,
> - Robert
>
>
>>
>> Cheers, Daniel
>> --
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
>>
>
>
Daniel Vetter May 4, 2016, 1:51 p.m. UTC | #28
On Wed, May 04, 2016 at 02:24:14PM +0100, Robert Bragg wrote:
> On Wed, May 4, 2016 at 1:24 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> 
> > On Wed, May 04, 2016 at 10:49:53AM +0100, Robert Bragg wrote:
> > > On Wed, May 4, 2016 at 10:09 AM, Martin Peres <
> > martin.peres@linux.intel.com>
> > > wrote:
> > >
> > > > On 03/05/16 23:03, Robert Bragg wrote:
> > > >
> > > >>
> > > >>
> > > >> On Tue, May 3, 2016 at 8:34 PM, Robert Bragg <robert@sixbynine.org
> > > >> <mailto:robert@sixbynine.org>> wrote:
> > > >>
> > > >>     Sorry for the delay replying to this, I missed it.
> > > >>
> > > >>     On Sat, Apr 23, 2016 at 11:34 AM, Martin Peres <
> > martin.peres@free.fr
> > > >>     <mailto:martin.peres@free.fr>> wrote:
> > > >>
> > > >>         On 20/04/16 17:23, Robert Bragg wrote:
> > > >>
> > > >>             Gen graphics hardware can be set up to periodically write
> > > >>             snapshots of
> > > >>             performance counters into a circular buffer via its
> > > >> Observation
> > > >>             Architecture and this patch exposes that capability to
> > > >>             userspace via the
> > > >>             i915 perf interface.
> > > >>
> > > >>             Cc: Chris Wilson <chris@chris-wilson.co.uk
> > > >>             <mailto:chris@chris-wilson.co.uk>>
> > > >>             Signed-off-by: Robert Bragg <robert@sixbynine.org
> > > >>             <mailto:robert@sixbynine.org>>
> > > >>             Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com
> > > >>             <mailto:zhenyuw@linux.intel.com>>
> > > >>
> > > >>             ---
> > > >>               drivers/gpu/drm/i915/i915_drv.h         |  56 +-
> > > >>               drivers/gpu/drm/i915/i915_gem_context.c |  24 +-
> > > >>               drivers/gpu/drm/i915/i915_perf.c        | 940
> > > >>             +++++++++++++++++++++++++++++++-
> > > >>               drivers/gpu/drm/i915/i915_reg.h         | 338
> > ++++++++++++
> > > >>               include/uapi/drm/i915_drm.h             |  70 ++-
> > > >>               5 files changed, 1408 insertions(+), 20 deletions(-)
> > > >>
> > > >>             +
> > > >>             +
> > > >>             +       /* It takes a fairly long time for a new MUX
> > > >>             configuration to
> > > >>             +        * be be applied after these register writes. This
> > > >> delay
> > > >>             +        * duration was derived empirically based on the
> > > >>             render_basic
> > > >>             +        * config but hopefully it covers the maximum
> > > >>             configuration
> > > >>             +        * latency...
> > > >>             +        */
> > > >>             +       mdelay(100);
> > > >>
> > > >>
> > > >>         With such a HW and SW design, how can we ever expose hope to
> > get
> > > >> any
> > > >>         kind of performance when we are trying to monitor different
> > > >>         metrics on each
> > > >>         draw call? This may be acceptable for system monitoring, but
> > it
> > > >>         is problematic
> > > >>         for the GL extensions :s
> > > >>
> > > >>
> > > >>         Since it seems like we are going for a perf API, it means that
> > > >>         for every change
> > > >>         of metrics, we need to flush the commands, wait for the GPU to
> > > >>         be done, then
> > > >>         program the new set of metrics via an IOCTL, wait 100 ms, and
> > > >>         then we may
> > > >>         resume rendering ... until the next change. We are talking
> > about
> > > >>         a latency of
> > > >>         6-7 frames at 60 Hz here... this is non-negligeable...
> > > >>
> > > >>
> > > >>         I understand that we have a ton of counters and we may hide
> > > >>         latency by not
> > > >>         allowing using more than half of the counters for every draw
> > > >>         call or frame, but
> > > >>         even then, this 100ms delay is killing this approach
> > altogether.
> > > >>
> > > >>
> > > >>
> > > >>
> > > >> So revisiting this to double check how things fail with my latest
> > > >> driver/tests without the delay, I apparently can't reproduce test
> > > >> failures without the delay any more...
> > > >>
> > > >> I think the explanation is that since first adding the delay to the
> > > >> driver I also made the the driver a bit more careful to not forward
> > > >> spurious reports that look invalid due to a zeroed report id field,
> > and
> > > >> that mechanism keeps the unit tests happy, even though there are still
> > > >> some number of invalid reports generated if we don't wait.
> > > >>
> > > >> One problem with simply having no delay is that the driver prints an
> > > >> error if it sees an invalid reports so I get a lot of 'Skipping
> > > >> spurious, invalid OA report' dmesg spam. Also this was intended more
> > as
> > > >> a last resort mechanism, and I wouldn't feel too happy about squashing
> > > >> the error message and potentially sweeping other error cases under the
> > > >> carpet.
> > > >>
> > > >> Experimenting to see if the delay can at least be reduced, I brought
> > the
> > > >> delay up in millisecond increments and found that although I still
> > see a
> > > >> lot of spurious reports only waiting 1 or 5 milliseconds, at 10
> > > >> milliseconds its reduced quite a bit and at 15 milliseconds I don't
> > seem
> > > >> to have any errors.
> > > >>
> > > >> 15 milliseconds is still a long time, but at least not as long as 100.
> > > >>
> > > >
> > > > OK, so the issue does not come from the HW after all, great!
> > > >
> > >
> > > Erm, I'm not sure that's a conclusion we can make here...
> > >
> > > The upshot here was really just reducing the delay from 100ms to 15ms.
> > > Previously I arrived at a workable delay by jumping the delay in orders
> > of
> > > magnitude with 10ms failing, 100ms passing and I didn't try and refine it
> > > further. Here I've looked at delays between 10 and 100ms.
> > >
> > > The other thing is observing that because the kernel is checking for
> > > invalid reports (generated by the hardware) before forwarding to
> > userspace
> > > the lack of a delay no longer triggers i-g-t failures because the invalid
> > > data won't reach i-g-t any more - though the invalid reports are still a
> > > thing to avoid.
> > >
> > >
> > > > Now, my main question is, why are spurious events generated when
> > changing
> > > > the MUX's value? I can understand that we would need to ignore the
> > reading
> > > > that came right after the change, but other than this,  I am a bit at a
> > > > loss.
> > > >
> > >
> > > The MUX selects 16 signals that the OA unit can turn into 16 separate
> > > counters by basically counting the signal changes. (there's some fancy
> > > fixed function logic that can affect this but that's the general idea).
> > >
> > > If the MUX is in the middle of being re-programmed then some subset of
> > > those 16 signals are for who knows what.
> > >
> > > After programming the MUX we will go on to configure the OA unit and the
> > > tests will enable periodic sampling which (if we have no delay) will
> > sample
> > > the OA counters that are currently being fed by undefined signals.
> > >
> > > So as far as that goes it makes sense to me to expect bad data if we
> > don't
> > > wait for the MUX config to land properly. Something I don't really know
> > is
> > > how come we're seemingly lucky to have the reports be cleanly invalid
> > with
> > > a zero report-id, instead of just having junk data that would be harder
> > to
> > > recognise.
> >
> > Yeah this mdelay story sounds realy scary. Few random comments:
> > - msleep instead of mdelay please
> >
> 
> yup this was a mistake I'd forgotten to fix in this series, but is fixed in
> the last series I sent after chris noticed too.
> 
> actually in my latest (yesterday after experimenting further with the delay
> requirements) I'm using usleep_range for a delay between 15 and 20
> milliseconds which seems to be enough.
> 
> 
> > - no dmesg noise above debug level for stuff that we know can happen -
> >   dmesg noise counts as igt failures
> >
> 
> okey. I don't think I have anything above debug level, unless things are
> going badly wrong.
> 
> Just double checking though has made me think twice about a WARN_ON in
> gen7_oa_read for !oa_buffer_addr, which would be a bad situation but should
> either be removed (never expected), be a BUG_ON (since the code would deref
> NULL anyway) or more gracefully return an error if seen.

WARN_ON + bail out, or BUG_ON. Silently fixing up without failing loud in
dmesg is imo the wrong approach for something that should never happen.

> I currently have some DRM_DRIVER_DEBUG errors for cases where userspace
> messes up what properties it gives to open a stream - hopefully that sound
> ok? I've found it quite helpful to have a readable error for otherwise
> vague EINVAL type errors.

Yeah, as long as it's debug-only it's perfectly fine. We actually try to
have such a line for each EINVAL, since it's so useful (but then userspace
always hits the one case we've missed to document with debug output!).

> I have a debug message I print if we see an invalid HW report, which
> *shouldn't* happen but can happen (e.g. if the MUX delay or tail margin
> aren't well tuned) and it's helpful to have the feedback, in case we end up
> in a situation where we see this kind of message too frequently which might
> indicate an issue to investigate.

That's fine too.

> > - reading 0 sounds more like bad synchronization.
> 
> 
> I suppose I haven't thoroughly considered if we should return zero in any
> case  - normally that would imply EOF so we get to choose what that implies
> here. I don't think the driver should ever return 0 from read() currently.
> 
> A few concious choices re: read() return values have been:
> 
> - never ever return partial records (or rather even if a partial record
> were literally copied into the userspace buffer, but an error were hit in
> the middle of copying a full sample then that record wouldn't be accounted
> for in the byte count returned.)
> 
> - Don't throw away records successfully copied, due to a later error. This
> simplifies error handling paths internally and reporting
> EAGAIN/ENOSPC/EFAULT errors and means data isn't lost. The precedence for
> what we return is 1) did we successfully copy some reports? report bytes
> copied for complete records. 2) did we encounter an error? report that if
> so. 3) return -EAGAIN. (though for a blocking fd this will be handled
> internally).
> 
> 
> 
> > Have you tried quiescing
> 
> the entire gpu (to make sure nothing is happen) and disabling OA, then
> >   updating, and then restarting? At least on a very quick look I didn't
> >   spot that. Random delays freak me out a bit, but wouldn't be surprised
> >   if really needed.
> >
> 
> Experimenting yesterday, it seems I can at least reduce the delay to around
> 15ms (granted that's still pretty huge), and it's also workable to have
> userspace sleep for this time (despite the mdelay I originally went with)
> 
> Haven't tried this, but yeah could be interesting to experiment if the MUX
> config lands faster in different situation such as when the HW is idle.

In either case I think it'd be good to excessively document what you've
discovered. Maybe even split out the msleep into a separate patch, so that
the commit message with all the details is easy to find again in the
future. Because 2 months down the road someone will read this and go wtf
;-)

Cheers, Daniel
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 5e959f3..972ae6c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1708,8 +1708,13 @@  struct intel_wm_config {
 	bool sprites_scaled;
 };
 
+struct i915_oa_format {
+	u32 format;
+	int size;
+};
+
 struct i915_oa_reg {
-	u32 addr;
+	i915_reg_t addr;
 	u32 value;
 };
 
@@ -1725,6 +1730,7 @@  struct i915_perf_stream {
 	struct list_head link;
 
 	u32 sample_flags;
+	int sample_size;
 
 	struct intel_context *ctx;
 	bool enabled;
@@ -1786,6 +1792,20 @@  struct i915_perf_stream {
 	void (*destroy)(struct i915_perf_stream *stream);
 };
 
+struct i915_oa_ops {
+	void (*init_oa_buffer)(struct drm_i915_private *dev_priv);
+	int (*enable_metric_set)(struct drm_i915_private *dev_priv);
+	void (*disable_metric_set)(struct drm_i915_private *dev_priv);
+	void (*oa_enable)(struct drm_i915_private *dev_priv);
+	void (*oa_disable)(struct drm_i915_private *dev_priv);
+	void (*update_oacontrol)(struct drm_i915_private *dev_priv);
+	void (*update_hw_ctx_id_locked)(struct drm_i915_private *dev_priv,
+					u32 ctx_id);
+	int (*read)(struct i915_perf_stream *stream,
+		    struct i915_perf_read_state *read_state);
+	bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv);
+};
+
 struct drm_i915_private {
 	struct drm_device *dev;
 	struct kmem_cache *objects;
@@ -2059,16 +2079,46 @@  struct drm_i915_private {
 
 	struct {
 		bool initialized;
+
 		struct mutex lock;
 		struct list_head streams;
 
+		spinlock_t hook_lock;
+
 		struct {
-			u32 metrics_set;
+			struct i915_perf_stream *exclusive_stream;
+
+			u32 specific_ctx_id;
+
+			struct hrtimer poll_check_timer;
+			wait_queue_head_t poll_wq;
+
+			bool periodic;
+			int period_exponent;
+			int timestamp_frequency;
+
+			int tail_margin;
+
+			int metrics_set;
 
 			const struct i915_oa_reg *mux_regs;
 			int mux_regs_len;
 			const struct i915_oa_reg *b_counter_regs;
 			int b_counter_regs_len;
+
+			struct {
+				struct drm_i915_gem_object *obj;
+				u32 gtt_offset;
+				u8 *addr;
+				int format;
+				int format_size;
+			} oa_buffer;
+
+			u32 gen7_latched_oastatus1;
+
+			struct i915_oa_ops ops;
+			const struct i915_oa_format *oa_formats;
+			int n_builtin_sets;
 		} oa;
 	} perf;
 
@@ -3429,6 +3479,8 @@  int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
 
 int i915_perf_open_ioctl(struct drm_device *dev, void *data,
 			 struct drm_file *file);
+void i915_oa_context_pin_notify(struct drm_i915_private *dev_priv,
+				struct intel_context *context);
 
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index e5acc39..ed5665f 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -133,6 +133,23 @@  static int get_context_size(struct drm_device *dev)
 	return ret;
 }
 
+static int i915_gem_context_pin_state(struct drm_device *dev,
+				      struct intel_context *ctx)
+{
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	ret = i915_gem_obj_ggtt_pin(ctx->legacy_hw_ctx.rcs_state,
+				    get_context_alignment(dev), 0);
+	if (ret)
+		return ret;
+
+	i915_oa_context_pin_notify(dev->dev_private, ctx);
+
+	return 0;
+}
+
 static void i915_gem_context_clean(struct intel_context *ctx)
 {
 	struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
@@ -287,8 +304,7 @@  i915_gem_create_context(struct drm_device *dev,
 		 * be available. To avoid this we always pin the default
 		 * context.
 		 */
-		ret = i915_gem_obj_ggtt_pin(ctx->legacy_hw_ctx.rcs_state,
-					    get_context_alignment(dev), 0);
+		ret = i915_gem_context_pin_state(dev, ctx);
 		if (ret) {
 			DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
 			goto err_destroy;
@@ -671,9 +687,7 @@  static int do_rcs_switch(struct drm_i915_gem_request *req)
 		return 0;
 
 	/* Trying to pin first makes error handling easier. */
-	ret = i915_gem_obj_ggtt_pin(to->legacy_hw_ctx.rcs_state,
-				    get_context_alignment(engine->dev),
-				    0);
+	ret = i915_gem_context_pin_state(engine->dev, to);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 2143401..5e58520 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -25,14 +25,830 @@ 
 #include <linux/sizes.h>
 
 #include "i915_drv.h"
+#include "intel_ringbuffer.h"
+#include "intel_lrc.h"
+#include "i915_oa_hsw.h"
+
+/* Must be a power of two */
+#define OA_BUFFER_SIZE		SZ_16M
+#define OA_TAKEN(tail, head)	((tail - head) & (OA_BUFFER_SIZE - 1))
+
+/* There's a HW race condition between OA unit tail pointer register updates and
+ * writes to memory whereby the tail pointer can sometimes get ahead of what's
+ * been written out to the OA buffer so far.
+ *
+ * Although this can be observed explicitly by checking for a zeroed report-id
+ * field in tail reports, it seems preferable to account for this earlier e.g.
+ * as part of the _oa_buffer_is_empty checks to minimize -EAGAIN polling cycles
+ * in this situation.
+ *
+ * To give time for the most recent reports to land before they may be copied to
+ * userspace, the driver operates as if the tail pointer effectively lags behind
+ * the HW tail pointer by 'tail_margin' bytes. The margin in bytes is calculated
+ * based on this constant in nanoseconds, the current OA sampling exponent
+ * and current report size.
+ *
+ * There is also a fallback check while reading to simply skip over reports with
+ * a zeroed report-id.
+ */
+#define OA_TAIL_MARGIN_NSEC	100000ULL
+
+/* frequency for checking whether the OA unit has written new reports to the
+ * circular OA buffer...
+ */
+#define POLL_FREQUENCY 200
+#define POLL_PERIOD (NSEC_PER_SEC / POLL_FREQUENCY)
+
+/* The maximum exponent the hardware accepts is 63 (essentially it selects one
+ * of the 64bit timestamp bits to trigger reports from) but there's currently
+ * no known use case for sampling as infrequently as once per 47 thousand years.
+ *
+ * Since the timestamps included in OA reports are only 32bits it seems
+ * reasonable to limit the OA exponent where it's still possible to account for
+ * overflow in OA report timestamps.
+ */
+#define OA_EXPONENT_MAX 31
+
+/* XXX: beware if future OA HW adds new report formats that the current
+ * code assumes all reports have a power-of-two size and ~(size - 1) can
+ * be used as a mask to align the OA tail pointer.
+ */
+static struct i915_oa_format hsw_oa_formats[I915_OA_FORMAT_MAX] = {
+	[I915_OA_FORMAT_A13]	    = { 0, 64 },
+	[I915_OA_FORMAT_A29]	    = { 1, 128 },
+	[I915_OA_FORMAT_A13_B8_C8]  = { 2, 128 },
+	/* A29_B8_C8 Disallowed as 192 bytes doesn't factor into buffer size */
+	[I915_OA_FORMAT_B4_C8]	    = { 4, 64 },
+	[I915_OA_FORMAT_A45_B8_C8]  = { 5, 256 },
+	[I915_OA_FORMAT_B4_C8_A16]  = { 6, 128 },
+	[I915_OA_FORMAT_C4_B8]	    = { 7, 64 },
+};
+
+#define SAMPLE_OA_REPORT      (1<<0)
 
 struct perf_open_properties {
 	u32 sample_flags;
 
 	u64 single_context:1;
 	u64 ctx_handle;
+
+	/* OA sampling state */
+	int metrics_set;
+	int oa_format;
+	bool oa_periodic;
+	int oa_period_exponent;
 };
 
+/* NB: This is either called via fops or the poll check hrtimer (atomic ctx)
+ *
+ * It's safe to read OA config state here unlocked, assuming that this is only
+ * called while the stream is enabled, while the global OA configuration can't
+ * be modified.
+ *
+ * Note: we don't lock around the head/tail reads even though there's the slim
+ * possibility of read() fop errors forcing a re-init of the OA buffer
+ * pointers.  A race here could result in a false positive !empty status which
+ * is acceptable.
+ */
+static bool gen7_oa_buffer_is_empty_fop_unlocked(struct drm_i915_private *dev_priv)
+{
+	int report_size = dev_priv->perf.oa.oa_buffer.format_size;
+	u32 oastatus2 = I915_READ(GEN7_OASTATUS2);
+	u32 oastatus1 = I915_READ(GEN7_OASTATUS1);
+	u32 head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
+	u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
+
+	return OA_TAKEN(tail, head) <
+		dev_priv->perf.oa.tail_margin + report_size;
+}
+
+/**
+ * Appends a status record to a userspace read() buffer.
+ */
+static int append_oa_status(struct i915_perf_stream *stream,
+			    struct i915_perf_read_state *read_state,
+			    enum drm_i915_perf_record_type type)
+{
+	struct drm_i915_perf_record_header header = { type, 0, sizeof(header) };
+
+	if ((read_state->count - read_state->read) < header.size)
+		return -ENOSPC;
+
+	if (copy_to_user(read_state->buf, &header, sizeof(header)))
+		return -EFAULT;
+
+	read_state->buf += header.size;
+	read_state->read += header.size;
+
+	return 0;
+}
+
+/**
+ * Copies single OA report into userspace read() buffer.
+ */
+static int append_oa_sample(struct i915_perf_stream *stream,
+			    struct i915_perf_read_state *read_state,
+			    const u8 *report)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+	int report_size = dev_priv->perf.oa.oa_buffer.format_size;
+	struct drm_i915_perf_record_header header;
+	u32 sample_flags = stream->sample_flags;
+	char __user *buf = read_state->buf;
+
+	header.type = DRM_I915_PERF_RECORD_SAMPLE;
+	header.pad = 0;
+	header.size = stream->sample_size;
+
+	if ((read_state->count - read_state->read) < header.size)
+		return -ENOSPC;
+
+	if (copy_to_user(buf, &header, sizeof(header)))
+		return -EFAULT;
+	buf += sizeof(header);
+
+	if (sample_flags & SAMPLE_OA_REPORT) {
+		if (copy_to_user(buf, report, report_size))
+			return -EFAULT;
+	}
+
+	read_state->buf += header.size;
+	read_state->read += header.size;
+
+	return 0;
+}
+
+/**
+ * Copies all buffered OA reports into userspace read() buffer.
+ * @head_ptr: (inout): the head pointer before and after appending
+ *
+ * Returns 0 on success, negative error code on failure.
+ *
+ * Notably any error condition resulting in a short read (-ENOSPC or
+ * -EFAULT) will be returned even though one or more records may
+ * have been successfully copied. In this case the error may be
+ * squashed before returning to userspace.
+ */
+static int gen7_append_oa_reports(struct i915_perf_stream *stream,
+				  struct i915_perf_read_state *read_state,
+				  u32 *head_ptr,
+				  u32 tail)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+	int report_size = dev_priv->perf.oa.oa_buffer.format_size;
+	u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.addr;
+	int tail_margin = dev_priv->perf.oa.tail_margin;
+	u32 mask = (OA_BUFFER_SIZE - 1);
+	u32 head;
+	u32 taken;
+	int ret = 0;
+
+	BUG_ON(!stream->enabled);
+
+	head = *head_ptr - dev_priv->perf.oa.oa_buffer.gtt_offset;
+	tail -= dev_priv->perf.oa.oa_buffer.gtt_offset;
+
+	/* The OA unit is expected to wrap the tail pointer according to the OA
+	 * buffer size and since we should never write a misaligned head
+	 * pointer we don't expect to read one back either...
+	 */
+	if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE ||
+	    head % report_size) {
+		DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart",
+			  head, tail);
+		dev_priv->perf.oa.ops.oa_disable(dev_priv);
+		dev_priv->perf.oa.ops.oa_enable(dev_priv);
+		*head_ptr = I915_READ(GEN7_OASTATUS2) &
+			GEN7_OASTATUS2_HEAD_MASK;
+		return -EIO;
+	}
+
+
+	/* The tail pointer increases in 64 byte increments, not in report_size
+	 * steps...
+	 */
+	tail &= ~(report_size - 1);
+
+	/* Move the tail pointer back by the current tail_margin to account for
+	 * the possibility that the latest reports may not have really landed
+	 * in memory yet...
+	 */
+
+	if (OA_TAKEN(tail, head) < report_size + tail_margin)
+		return -EAGAIN;
+
+	tail -= tail_margin;
+	tail &= mask;
+
+	for (/* none */;
+	     (taken = OA_TAKEN(tail, head));
+	     head = (head + report_size) & mask) {
+		u8 *report = oa_buf_base + head;
+		u32 *report32 = (void *)report;
+
+		/* All the report sizes factor neatly into the buffer
+		 * size so we never expect to see a report split
+		 * between the beginning and end of the buffer.
+		 *
+		 * Given the initial alignment check a misalignment
+		 * here would imply a driver bug that would result
+		 * in an overrun.
+		 */
+		BUG_ON((OA_BUFFER_SIZE - head) < report_size);
+
+		/* The report-ID field for periodic samples includes
+		 * some undocumented flags related to what triggered
+		 * the report and is never expected to be zero so we
+		 * can check that the report isn't invalid before
+		 * copying it to userspace...
+		 */
+		if (report32[0] == 0) {
+			DRM_ERROR("Skipping spurious, invalid OA report\n");
+			continue;
+		}
+
+		ret = append_oa_sample(stream, read_state, report);
+		if (ret)
+			break;
+
+		/* The above report-id field sanity check is based on
+		 * the assumption that the OA buffer is initially
+		 * zeroed and we reset the field after copying so the
+		 * check is still meaningful once old reports start
+		 * being overwritten.
+		 */
+		report32[0] = 0;
+	}
+
+	*head_ptr = dev_priv->perf.oa.oa_buffer.gtt_offset + head;
+
+	return ret;
+}
+
+static int gen7_oa_read(struct i915_perf_stream *stream,
+			struct i915_perf_read_state *read_state)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+	int report_size = dev_priv->perf.oa.oa_buffer.format_size;
+	u32 oastatus2;
+	u32 oastatus1;
+	u32 head;
+	u32 tail;
+	int ret;
+
+	WARN_ON(!dev_priv->perf.oa.oa_buffer.addr);
+
+	oastatus2 = I915_READ(GEN7_OASTATUS2);
+	oastatus1 = I915_READ(GEN7_OASTATUS1);
+
+	head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
+	tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
+
+	/* XXX: On Haswell we don't have a safe way to clear oastatus1
+	 * bits while the OA unit is enabled (while the tail pointer
+	 * may be updated asynchronously) so we ignore status bits
+	 * that have already been reported to userspace.
+	 */
+	oastatus1 &= ~dev_priv->perf.oa.gen7_latched_oastatus1;
+
+	/* We treat OABUFFER_OVERFLOW as a significant error:
+	 *
+	 * - The status can be interpreted to mean that the buffer is
+	 *   currently full (with a higher precedence than OA_TAKEN()
+	 *   which will start to report a near-empty buffer after an
+	 *   overflow) but it's awkward that we can't clear the status
+	 *   on Haswell, so without a reset we won't be able to catch
+	 *   the state again.
+	 *
+	 * - Since it also implies the HW has started overwriting old
+	 *   reports it may also affect our sanity checks for invalid
+	 *   reports when copying to userspace that assume new reports
+	 *   are being written to cleared memory.
+	 *
+	 * - In the future we may want to introduce a flight recorder
+	 *   mode where the driver will automatically maintain a safe
+	 *   guard band between head/tail, avoiding this overflow
+	 *   condition, but we avoid the added driver complexity for
+	 *   now.
+	 */
+	if (unlikely(oastatus1 & GEN7_OASTATUS1_OABUFFER_OVERFLOW)) {
+		ret = append_oa_status(stream, read_state,
+				       DRM_I915_PERF_RECORD_OA_BUFFER_LOST);
+		if (ret)
+			return ret;
+
+		DRM_ERROR("OA buffer overflow: force restart");
+
+		dev_priv->perf.oa.ops.oa_disable(dev_priv);
+		dev_priv->perf.oa.ops.oa_enable(dev_priv);
+
+		oastatus2 = I915_READ(GEN7_OASTATUS2);
+		oastatus1 = I915_READ(GEN7_OASTATUS1);
+
+		head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK;
+		tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
+	}
+
+	if (unlikely(oastatus1 & GEN7_OASTATUS1_REPORT_LOST)) {
+		ret = append_oa_status(stream, read_state,
+				       DRM_I915_PERF_RECORD_OA_REPORT_LOST);
+		if (ret)
+			return ret;
+		dev_priv->perf.oa.gen7_latched_oastatus1 |=
+			GEN7_OASTATUS1_REPORT_LOST;
+	}
+
+	ret = gen7_append_oa_reports(stream, read_state, &head, tail);
+
+	/* All the report sizes are a power of two and the
+	 * head should always be incremented by some multiple
+	 * of the report size.
+	 *
+	 * A warning here, but notably if we later read back a
+	 * misaligned pointer we will treat that as a bug since
+	 * it could lead to a buffer overrun.
+	 */
+	WARN_ONCE(head & (report_size - 1),
+		  "i915: Writing misaligned OA head pointer");
+
+	/* Note: we update the head pointer here even if an error
+	 * was returned since the error may represent a short read
+	 * where some some reports were successfully copied.
+	 */
+	I915_WRITE(GEN7_OASTATUS2,
+		   ((head & GEN7_OASTATUS2_HEAD_MASK) |
+		    OA_MEM_SELECT_GGTT));
+
+	return ret;
+}
+
+static bool i915_oa_can_read(struct i915_perf_stream *stream)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	return !dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv);
+}
+
+static int i915_oa_wait_unlocked(struct i915_perf_stream *stream)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	/* Note: the oa_buffer_is_empty() condition is ok to run unlocked as it
+	 * just performs mmio reads of the OA buffer head + tail pointers and
+	 * it's assumed we're handling some operation that implies the stream
+	 * can't be destroyed until completion (such as a read()) that ensures
+	 * the device + OA buffer can't disappear
+	 */
+	return wait_event_interruptible(dev_priv->perf.oa.poll_wq,
+					!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv));
+}
+
+static void i915_oa_poll_wait(struct i915_perf_stream *stream,
+			      struct file *file,
+			      poll_table *wait)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	poll_wait(file, &dev_priv->perf.oa.poll_wq, wait);
+}
+
+static int i915_oa_read(struct i915_perf_stream *stream,
+			struct i915_perf_read_state *read_state)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	return dev_priv->perf.oa.ops.read(stream, read_state);
+}
+
+static void
+free_oa_buffer(struct drm_i915_private *i915)
+{
+	mutex_lock(&i915->dev->struct_mutex);
+
+	vunmap(i915->perf.oa.oa_buffer.addr);
+	i915_gem_object_ggtt_unpin(i915->perf.oa.oa_buffer.obj);
+	drm_gem_object_unreference(&i915->perf.oa.oa_buffer.obj->base);
+
+	i915->perf.oa.oa_buffer.obj = NULL;
+	i915->perf.oa.oa_buffer.gtt_offset = 0;
+	i915->perf.oa.oa_buffer.addr = NULL;
+
+	mutex_unlock(&i915->dev->struct_mutex);
+}
+
+static void i915_oa_stream_destroy(struct i915_perf_stream *stream)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	BUG_ON(stream != dev_priv->perf.oa.exclusive_stream);
+
+	dev_priv->perf.oa.ops.disable_metric_set(dev_priv);
+
+	free_oa_buffer(dev_priv);
+
+	intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+	intel_runtime_pm_put(dev_priv);
+
+	dev_priv->perf.oa.exclusive_stream = NULL;
+}
+
+static void *vmap_oa_buffer(struct drm_i915_gem_object *obj)
+{
+	int i;
+	void *addr = NULL;
+	struct sg_page_iter sg_iter;
+	struct page **pages;
+
+	pages = drm_malloc_ab(obj->base.size >> PAGE_SHIFT, sizeof(*pages));
+	if (pages == NULL) {
+		DRM_DEBUG_DRIVER("Failed to get space for pages\n");
+		goto finish;
+	}
+
+	i = 0;
+	for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
+		pages[i] = sg_page_iter_page(&sg_iter);
+		i++;
+	}
+
+	addr = vmap(pages, i, 0, PAGE_KERNEL);
+	if (addr == NULL) {
+		DRM_DEBUG_DRIVER("Failed to vmap pages\n");
+		goto finish;
+	}
+
+finish:
+	if (pages)
+		drm_free_large(pages);
+	return addr;
+}
+
+static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
+{
+	/* Pre-DevBDW: OABUFFER must be set with counters off,
+	 * before OASTATUS1, but after OASTATUS2
+	 */
+	I915_WRITE(GEN7_OASTATUS2, dev_priv->perf.oa.oa_buffer.gtt_offset |
+		   OA_MEM_SELECT_GGTT); /* head */
+	I915_WRITE(GEN7_OABUFFER, dev_priv->perf.oa.oa_buffer.gtt_offset);
+	I915_WRITE(GEN7_OASTATUS1, dev_priv->perf.oa.oa_buffer.gtt_offset |
+		   OABUFFER_SIZE_16M); /* tail */
+
+	/* On Haswell we have to track which OASTATUS1 flags we've
+	 * already seen since they can't be cleared while periodic
+	 * sampling is enabled.
+	 */
+	dev_priv->perf.oa.gen7_latched_oastatus1 = 0;
+
+	/* We have a sanity check in gen7_append_oa_reports() that
+	 * looks at the report-id field to make sure it's non-zero
+	 * which relies on the assumption that new reports are
+	 * being written to zeroed memory...
+	 */
+	memset(dev_priv->perf.oa.oa_buffer.addr, 0, SZ_16M);
+}
+
+static int alloc_oa_buffer(struct drm_i915_private *dev_priv)
+{
+	struct drm_i915_gem_object *bo;
+	int ret;
+
+	BUG_ON(dev_priv->perf.oa.oa_buffer.obj);
+
+	ret = i915_mutex_lock_interruptible(dev_priv->dev);
+	if (ret)
+		return ret;
+
+	bo = i915_gem_alloc_object(dev_priv->dev, OA_BUFFER_SIZE);
+	if (bo == NULL) {
+		DRM_ERROR("Failed to allocate OA buffer\n");
+		ret = -ENOMEM;
+		goto unlock;
+	}
+	dev_priv->perf.oa.oa_buffer.obj = bo;
+
+	ret = i915_gem_object_set_cache_level(bo, I915_CACHE_LLC);
+	if (ret)
+		goto err_unref;
+
+	/* PreHSW required 512K alignment, HSW requires 16M */
+	ret = i915_gem_obj_ggtt_pin(bo, SZ_16M, 0);
+	if (ret)
+		goto err_unref;
+
+	dev_priv->perf.oa.oa_buffer.gtt_offset = i915_gem_obj_ggtt_offset(bo);
+	dev_priv->perf.oa.oa_buffer.addr = vmap_oa_buffer(bo);
+
+	dev_priv->perf.oa.ops.init_oa_buffer(dev_priv);
+
+	DRM_DEBUG_DRIVER("OA Buffer initialized, gtt offset = 0x%x, vaddr = %p",
+			 dev_priv->perf.oa.oa_buffer.gtt_offset,
+			 dev_priv->perf.oa.oa_buffer.addr);
+
+	goto unlock;
+
+err_unref:
+	drm_gem_object_unreference(&bo->base);
+
+unlock:
+	mutex_unlock(&dev_priv->dev->struct_mutex);
+	return ret;
+}
+
+static void config_oa_regs(struct drm_i915_private *dev_priv,
+			   const struct i915_oa_reg *regs,
+			   int n_regs)
+{
+	int i;
+
+	for (i = 0; i < n_regs; i++) {
+		const struct i915_oa_reg *reg = regs + i;
+
+		I915_WRITE(reg->addr, reg->value);
+	}
+}
+
+static int hsw_enable_metric_set(struct drm_i915_private *dev_priv)
+{
+	int ret = i915_oa_select_metric_set_hsw(dev_priv);
+
+	if (ret)
+		return ret;
+
+	I915_WRITE(GDT_CHICKEN_BITS, GT_NOA_ENABLE);
+
+	/* PRM:
+	 *
+	 * OA unit is using “crclk” for its functionality. When trunk
+	 * level clock gating takes place, OA clock would be gated,
+	 * unable to count the events from non-render clock domain.
+	 * Render clock gating must be disabled when OA is enabled to
+	 * count the events from non-render domain. Unit level clock
+	 * gating for RCS should also be disabled.
+	 */
+	I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) &
+				    ~GEN7_DOP_CLOCK_GATE_ENABLE));
+	I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) |
+				  GEN6_CSUNIT_CLOCK_GATE_DISABLE));
+
+	config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs,
+		       dev_priv->perf.oa.mux_regs_len);
+
+	/* It takes a fairly long time for a new MUX configuration to
+	 * be be applied after these register writes. This delay
+	 * duration was derived empirically based on the render_basic
+	 * config but hopefully it covers the maximum configuration
+	 * latency...
+	 */
+	mdelay(100);
+
+	config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs,
+		       dev_priv->perf.oa.b_counter_regs_len);
+
+	return 0;
+}
+
+static void hsw_disable_metric_set(struct drm_i915_private *dev_priv)
+{
+	I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) &
+				  ~GEN6_CSUNIT_CLOCK_GATE_DISABLE));
+	I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) |
+				    GEN7_DOP_CLOCK_GATE_ENABLE));
+
+	I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) &
+				      ~GT_NOA_ENABLE));
+}
+
+static void gen7_update_oacontrol_locked(struct drm_i915_private *dev_priv)
+{
+	assert_spin_locked(&dev_priv->perf.hook_lock);
+
+	if (dev_priv->perf.oa.exclusive_stream->enabled) {
+		unsigned long ctx_id = 0;
+
+		if (dev_priv->perf.oa.exclusive_stream->ctx)
+			ctx_id = dev_priv->perf.oa.specific_ctx_id;
+
+		if (dev_priv->perf.oa.exclusive_stream->ctx == NULL || ctx_id) {
+			bool periodic = dev_priv->perf.oa.periodic;
+			u32 period_exponent = dev_priv->perf.oa.period_exponent;
+			u32 report_format = dev_priv->perf.oa.oa_buffer.format;
+
+			I915_WRITE(GEN7_OACONTROL,
+				   (ctx_id & GEN7_OACONTROL_CTX_MASK) |
+				   (period_exponent <<
+				    GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
+				   (periodic ?
+				    GEN7_OACONTROL_TIMER_ENABLE : 0) |
+				   (report_format <<
+				    GEN7_OACONTROL_FORMAT_SHIFT) |
+				   (ctx_id ?
+				    GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
+				   GEN7_OACONTROL_ENABLE);
+			return;
+		}
+	}
+
+	I915_WRITE(GEN7_OACONTROL, 0);
+}
+
+static void gen7_oa_enable(struct drm_i915_private *dev_priv)
+{
+	unsigned long flags;
+
+	/* Reset buf pointers so we don't forward reports from before now. */
+	gen7_init_oa_buffer(dev_priv);
+
+	spin_lock_irqsave(&dev_priv->perf.hook_lock, flags);
+	gen7_update_oacontrol_locked(dev_priv);
+	spin_unlock_irqrestore(&dev_priv->perf.hook_lock, flags);
+}
+
+static void i915_oa_stream_enable(struct i915_perf_stream *stream)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	dev_priv->perf.oa.ops.oa_enable(dev_priv);
+
+	if (dev_priv->perf.oa.periodic)
+		hrtimer_start(&dev_priv->perf.oa.poll_check_timer,
+			      ns_to_ktime(POLL_PERIOD),
+			      HRTIMER_MODE_REL_PINNED);
+}
+
+static void gen7_oa_disable(struct drm_i915_private *dev_priv)
+{
+	I915_WRITE(GEN7_OACONTROL, 0);
+}
+
+static void i915_oa_stream_disable(struct i915_perf_stream *stream)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+
+	dev_priv->perf.oa.ops.oa_disable(dev_priv);
+
+	if (dev_priv->perf.oa.periodic)
+		hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer);
+}
+
+static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent)
+{
+	return 1000000000ULL * (2ULL << exponent) /
+		dev_priv->perf.oa.timestamp_frequency;
+}
+
+static int i915_oa_stream_init(struct i915_perf_stream *stream,
+			       struct drm_i915_perf_open_param *param,
+			       struct perf_open_properties *props)
+{
+	struct drm_i915_private *dev_priv = stream->dev_priv;
+	int format_size;
+	int ret;
+
+	if (!(props->sample_flags & SAMPLE_OA_REPORT)) {
+		DRM_ERROR("Only OA report sampling supported\n");
+		return -EINVAL;
+	}
+
+	if (!dev_priv->perf.oa.ops.init_oa_buffer) {
+		DRM_ERROR("OA unit not supported\n");
+		return -ENODEV;
+	}
+
+	/* To avoid the complexity of having to accurately filter
+	 * counter reports and marshal to the appropriate client
+	 * we currently only allow exclusive access
+	 */
+	if (dev_priv->perf.oa.exclusive_stream) {
+		DRM_ERROR("OA unit already in use\n");
+		return -EBUSY;
+	}
+
+	if (!props->metrics_set) {
+		DRM_ERROR("OA metric set not specified\n");
+		return -EINVAL;
+	}
+
+	if (!props->oa_format) {
+		DRM_ERROR("OA report format not specified\n");
+		return -EINVAL;
+	}
+
+	stream->sample_size = sizeof(struct drm_i915_perf_record_header);
+
+	format_size = dev_priv->perf.oa.oa_formats[props->oa_format].size;
+
+	stream->sample_flags |= SAMPLE_OA_REPORT;
+	stream->sample_size += format_size;
+
+	dev_priv->perf.oa.oa_buffer.format_size = format_size;
+	BUG_ON(dev_priv->perf.oa.oa_buffer.format_size == 0);
+
+	dev_priv->perf.oa.oa_buffer.format =
+		dev_priv->perf.oa.oa_formats[props->oa_format].format;
+
+	dev_priv->perf.oa.metrics_set = props->metrics_set;
+
+	dev_priv->perf.oa.periodic = props->oa_periodic;
+	if (dev_priv->perf.oa.periodic) {
+		u64 period_ns = oa_exponent_to_ns(dev_priv,
+						  props->oa_period_exponent);
+
+		dev_priv->perf.oa.period_exponent = props->oa_period_exponent;
+
+		/* See comment for OA_TAIL_MARGIN_NSEC for details
+		 * about this tail_margin...
+		 */
+		dev_priv->perf.oa.tail_margin =
+			((OA_TAIL_MARGIN_NSEC / period_ns) + 1) * format_size;
+	}
+
+	ret = alloc_oa_buffer(dev_priv);
+	if (ret)
+		return ret;
+
+	/* PRM - observability performance counters:
+	 *
+	 *   OACONTROL, performance counter enable, note:
+	 *
+	 *   "When this bit is set, in order to have coherent counts,
+	 *   RC6 power state and trunk clock gating must be disabled.
+	 *   This can be achieved by programming MMIO registers as
+	 *   0xA094=0 and 0xA090[31]=1"
+	 *
+	 *   In our case we are expecting that taking pm + FORCEWAKE
+	 *   references will effectively disable RC6.
+	 */
+	intel_runtime_pm_get(dev_priv);
+	intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+
+	ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv);
+	if (ret) {
+		intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+		intel_runtime_pm_put(dev_priv);
+		free_oa_buffer(dev_priv);
+		return ret;
+	}
+
+	stream->destroy = i915_oa_stream_destroy;
+	stream->enable = i915_oa_stream_enable;
+	stream->disable = i915_oa_stream_disable;
+	stream->can_read = i915_oa_can_read;
+	stream->wait_unlocked = i915_oa_wait_unlocked;
+	stream->poll_wait = i915_oa_poll_wait;
+	stream->read = i915_oa_read;
+
+	/* On Haswell we have to track which OASTATUS1 flags we've already
+	 * seen since they can't be cleared while periodic sampling is enabled.
+	 */
+	dev_priv->perf.oa.gen7_latched_oastatus1 = 0;
+
+	dev_priv->perf.oa.exclusive_stream = stream;
+
+	return 0;
+}
+
+static void gen7_update_hw_ctx_id_locked(struct drm_i915_private *dev_priv,
+					 u32 ctx_id)
+{
+	assert_spin_locked(&dev_priv->perf.hook_lock);
+
+	dev_priv->perf.oa.specific_ctx_id = ctx_id;
+	gen7_update_oacontrol_locked(dev_priv);
+}
+
+static void i915_oa_context_pin_notify_locked(struct drm_i915_private *dev_priv,
+					      struct intel_context *context)
+{
+	assert_spin_locked(&dev_priv->perf.hook_lock);
+
+	if (i915.enable_execlists ||
+	    dev_priv->perf.oa.ops.update_hw_ctx_id_locked == NULL)
+		return;
+
+	if (dev_priv->perf.oa.exclusive_stream &&
+	    dev_priv->perf.oa.exclusive_stream->ctx == context) {
+		struct drm_i915_gem_object *obj =
+			context->legacy_hw_ctx.rcs_state;
+		u32 ctx_id = i915_gem_obj_ggtt_offset(obj);
+
+		dev_priv->perf.oa.ops.update_hw_ctx_id_locked(dev_priv, ctx_id);
+	}
+}
+
+void i915_oa_context_pin_notify(struct drm_i915_private *dev_priv,
+				struct intel_context *context)
+{
+	unsigned long flags;
+
+	if (!dev_priv->perf.initialized)
+		return;
+
+	spin_lock_irqsave(&dev_priv->perf.hook_lock, flags);
+	i915_oa_context_pin_notify_locked(dev_priv, context);
+	spin_unlock_irqrestore(&dev_priv->perf.hook_lock, flags);
+}
+
 static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream,
 				     struct file *file,
 				     char __user *buf,
@@ -42,7 +858,10 @@  static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream,
 	struct i915_perf_read_state state = { count, 0, buf };
 	int ret = stream->read(stream, &state);
 
-	if ((ret == -ENOSPC || ret == -EFAULT) && state.read)
+	/* Squash any internal error status in any case where we've
+	 * successfully copied some data so the data isn't lost.
+	 */
+	if (state.read)
 		ret = 0;
 
 	if (ret)
@@ -60,6 +879,13 @@  static ssize_t i915_perf_read(struct file *file,
 	struct drm_i915_private *dev_priv = stream->dev_priv;
 	ssize_t ret;
 
+	/* To ensure it's handled consistently we simply treat all reads of a
+	 * disabled stream as an error. In particular it might otherwise lead
+	 * to a deadlock for blocking file descriptors...
+	 */
+	if (!stream->enabled)
+		return -EIO;
+
 	if (!(file->f_flags & O_NONBLOCK)) {
 		/* There's the small chance of false positives from
 		 * stream->wait_unlocked.
@@ -87,6 +913,20 @@  static ssize_t i915_perf_read(struct file *file,
 	return ret;
 }
 
+static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer)
+{
+	struct drm_i915_private *dev_priv =
+		container_of(hrtimer, typeof(*dev_priv),
+			     perf.oa.poll_check_timer);
+
+	if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv))
+		wake_up(&dev_priv->perf.oa.poll_wq);
+
+	hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD));
+
+	return HRTIMER_RESTART;
+}
+
 static unsigned int i915_perf_poll_locked(struct i915_perf_stream *stream,
 					  struct file *file,
 					  poll_table *wait)
@@ -277,18 +1117,18 @@  int i915_perf_open_ioctl_locked(struct drm_device *dev,
 		goto err_ctx;
 	}
 
-	stream->sample_flags = props->sample_flags;
 	stream->dev_priv = dev_priv;
 	stream->ctx = specific_ctx;
 
-	/*
-	 * TODO: support sampling something
-	 *
-	 * For now this is as far as we can go.
+	ret = i915_oa_stream_init(stream, param, props);
+	if (ret)
+		goto err_alloc;
+
+	/* we avoid simply assigning stream->sample_flags = props->sample_flags
+	 * to have _stream_init check the combination of sample flags more
+	 * thoroughly, but still this is the expected result at this point.
 	 */
-	DRM_ERROR("Unsupported i915 perf stream configuration\n");
-	ret = -EINVAL;
-	goto err_alloc;
+	BUG_ON(stream->sample_flags != props->sample_flags);
 
 	list_add(&stream->link, &dev_priv->perf.streams);
 
@@ -373,7 +1213,56 @@  static int read_properties_unlocked(struct drm_i915_private *dev_priv,
 			props->single_context = 1;
 			props->ctx_handle = value;
 			break;
-
+		case DRM_I915_PERF_PROP_SAMPLE_OA:
+			props->sample_flags |= SAMPLE_OA_REPORT;
+			break;
+		case DRM_I915_PERF_PROP_OA_METRICS_SET:
+			if (value == 0 ||
+			    value > dev_priv->perf.oa.n_builtin_sets) {
+				DRM_ERROR("Unknown OA metric set ID");
+				return -EINVAL;
+			}
+			props->metrics_set = value;
+			break;
+		case DRM_I915_PERF_PROP_OA_FORMAT:
+			if (value == 0 || value >= I915_OA_FORMAT_MAX) {
+				DRM_ERROR("Invalid OA report format\n");
+				return -EINVAL;
+			}
+			if (!dev_priv->perf.oa.oa_formats[value].size) {
+				DRM_ERROR("Invalid OA report format\n");
+				return -EINVAL;
+			}
+			props->oa_format = value;
+			break;
+		case DRM_I915_PERF_PROP_OA_EXPONENT:
+			if (value > OA_EXPONENT_MAX) {
+				DRM_ERROR("OA timer exponent too high (> %u)\n",
+					  OA_EXPONENT_MAX);
+				return -EINVAL;
+			}
+
+			/* NB: The exponent represents a period as follows:
+			 *
+			 *   80ns * 2^(period_exponent + 1)
+			 *
+			 * Theoretically we can program the OA unit to sample
+			 * every 160ns but don't allow that by default unless
+			 * root.
+			 *
+			 * Referring to perf's
+			 * kernel.perf_event_max_sample_rate for a precedent
+			 * (100000 by default); with an OA exponent of 6 we get
+			 * a period of 10.240 microseconds -just under 100000Hz
+			 */
+			if (value < 6 && !capable(CAP_SYS_ADMIN)) {
+				DRM_ERROR("Sampling period too high without root privileges\n");
+				return -EACCES;
+			}
+
+			props->oa_periodic = true;
+			props->oa_period_exponent = value;
+			break;
 		case DRM_I915_PERF_PROP_MAX:
 			BUG();
 		}
@@ -424,8 +1313,37 @@  void i915_perf_init(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 
+	if (!IS_HASWELL(dev))
+		return;
+
+	hrtimer_init(&dev_priv->perf.oa.poll_check_timer,
+		     CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb;
+	init_waitqueue_head(&dev_priv->perf.oa.poll_wq);
+
 	INIT_LIST_HEAD(&dev_priv->perf.streams);
 	mutex_init(&dev_priv->perf.lock);
+	spin_lock_init(&dev_priv->perf.hook_lock);
+
+	dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer;
+	dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set;
+	dev_priv->perf.oa.ops.disable_metric_set = hsw_disable_metric_set;
+	dev_priv->perf.oa.ops.oa_enable = gen7_oa_enable;
+	dev_priv->perf.oa.ops.oa_disable = gen7_oa_disable;
+	dev_priv->perf.oa.ops.update_hw_ctx_id_locked =
+		gen7_update_hw_ctx_id_locked;
+	dev_priv->perf.oa.ops.read = gen7_oa_read;
+	dev_priv->perf.oa.ops.oa_buffer_is_empty =
+		gen7_oa_buffer_is_empty_fop_unlocked;
+
+	dev_priv->perf.oa.timestamp_frequency = 12500000;
+
+	dev_priv->perf.oa.oa_formats = hsw_oa_formats;
+
+	dev_priv->perf.oa.n_builtin_sets =
+		i915_oa_n_builtin_metric_sets_hsw;
+
+	dev_priv->perf.oa.oa_formats = hsw_oa_formats;
 
 	dev_priv->perf.initialized = true;
 }
@@ -437,7 +1355,7 @@  void i915_perf_fini(struct drm_device *dev)
 	if (!dev_priv->perf.initialized)
 		return;
 
-	/* Currently nothing to clean up */
+	dev_priv->perf.oa.ops.init_oa_buffer = NULL;
 
 	dev_priv->perf.initialized = false;
 }
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index de1e9a0..22bf23c 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -594,6 +594,343 @@  static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define HSW_CS_GPR_UDW(n)               _MMIO(0x2600 + (n) * 8 + 4)
 
 #define GEN7_OACONTROL _MMIO(0x2360)
+#define  GEN7_OACONTROL_CTX_MASK	    0xFFFFF000
+#define  GEN7_OACONTROL_TIMER_PERIOD_MASK   0x3F
+#define  GEN7_OACONTROL_TIMER_PERIOD_SHIFT  6
+#define  GEN7_OACONTROL_TIMER_ENABLE	    (1<<5)
+#define  GEN7_OACONTROL_FORMAT_A13	    (0<<2)
+#define  GEN7_OACONTROL_FORMAT_A29	    (1<<2)
+#define  GEN7_OACONTROL_FORMAT_A13_B8_C8    (2<<2)
+#define  GEN7_OACONTROL_FORMAT_A29_B8_C8    (3<<2)
+#define  GEN7_OACONTROL_FORMAT_B4_C8	    (4<<2)
+#define  GEN7_OACONTROL_FORMAT_A45_B8_C8    (5<<2)
+#define  GEN7_OACONTROL_FORMAT_B4_C8_A16    (6<<2)
+#define  GEN7_OACONTROL_FORMAT_C4_B8	    (7<<2)
+#define  GEN7_OACONTROL_FORMAT_SHIFT	    2
+#define  GEN7_OACONTROL_PER_CTX_ENABLE	    (1<<1)
+#define  GEN7_OACONTROL_ENABLE		    (1<<0)
+
+#define GEN8_OACTXID _MMIO(0x2364)
+
+#define GEN8_OACONTROL _MMIO(0x2B00)
+#define  GEN8_OA_REPORT_FORMAT_A12	    (0<<2)
+#define  GEN8_OA_REPORT_FORMAT_A12_B8_C8    (2<<2)
+#define  GEN8_OA_REPORT_FORMAT_A36_B8_C8    (5<<2)
+#define  GEN8_OA_REPORT_FORMAT_C4_B8	    (7<<2)
+#define  GEN8_OA_REPORT_FORMAT_SHIFT	    2
+#define  GEN8_OA_SPECIFIC_CONTEXT_ENABLE    (1<<1)
+#define  GEN8_OA_COUNTER_ENABLE             (1<<0)
+
+#define GEN8_OACTXCONTROL _MMIO(0x2360)
+#define  GEN8_OA_TIMER_PERIOD_MASK	    0x3F
+#define  GEN8_OA_TIMER_PERIOD_SHIFT	    2
+#define  GEN8_OA_TIMER_ENABLE		    (1<<1)
+#define  GEN8_OA_COUNTER_RESUME		    (1<<0)
+
+#define GEN7_OABUFFER _MMIO(0x23B0) /* R/W */
+#define  GEN7_OABUFFER_OVERRUN_DISABLE	    (1<<3)
+#define  GEN7_OABUFFER_EDGE_TRIGGER	    (1<<2)
+#define  GEN7_OABUFFER_STOP_RESUME_ENABLE   (1<<1)
+#define  GEN7_OABUFFER_RESUME		    (1<<0)
+
+#define GEN8_OABUFFER _MMIO(0x2b14)
+
+#define GEN7_OASTATUS1 _MMIO(0x2364)
+#define  GEN7_OASTATUS1_TAIL_MASK	    0xffffffc0
+#define  GEN7_OASTATUS1_COUNTER_OVERFLOW    (1<<2)
+#define  GEN7_OASTATUS1_OABUFFER_OVERFLOW   (1<<1)
+#define  GEN7_OASTATUS1_REPORT_LOST	    (1<<0)
+
+#define GEN7_OASTATUS2 _MMIO(0x2368)
+#define GEN7_OASTATUS2_HEAD_MASK    0xffffffc0
+
+#define GEN8_OASTATUS _MMIO(0x2b08)
+#define  GEN8_OASTATUS_OVERRUN_STATUS	    (1<<3)
+#define  GEN8_OASTATUS_COUNTER_OVERFLOW     (1<<2)
+#define  GEN8_OASTATUS_OABUFFER_OVERFLOW    (1<<1)
+#define  GEN8_OASTATUS_REPORT_LOST	    (1<<0)
+
+#define GEN8_OAHEADPTR _MMIO(0x2B0C)
+#define GEN8_OATAILPTR _MMIO(0x2B10)
+
+#define OABUFFER_SIZE_128K  (0<<3)
+#define OABUFFER_SIZE_256K  (1<<3)
+#define OABUFFER_SIZE_512K  (2<<3)
+#define OABUFFER_SIZE_1M    (3<<3)
+#define OABUFFER_SIZE_2M    (4<<3)
+#define OABUFFER_SIZE_4M    (5<<3)
+#define OABUFFER_SIZE_8M    (6<<3)
+#define OABUFFER_SIZE_16M   (7<<3)
+
+#define OA_MEM_SELECT_GGTT  (1<<0)
+
+#define EU_PERF_CNTL0	    _MMIO(0xe458)
+
+#define GDT_CHICKEN_BITS    _MMIO(0x9840)
+#define GT_NOA_ENABLE	    0x00000080
+
+/*
+ * OA Boolean state
+ */
+
+#define OAREPORTTRIG1 _MMIO(0x2740)
+#define OAREPORTTRIG1_THRESHOLD_MASK 0xffff
+#define OAREPORTTRIG1_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */
+
+#define OAREPORTTRIG2 _MMIO(0x2744)
+#define OAREPORTTRIG2_INVERT_A_0  (1<<0)
+#define OAREPORTTRIG2_INVERT_A_1  (1<<1)
+#define OAREPORTTRIG2_INVERT_A_2  (1<<2)
+#define OAREPORTTRIG2_INVERT_A_3  (1<<3)
+#define OAREPORTTRIG2_INVERT_A_4  (1<<4)
+#define OAREPORTTRIG2_INVERT_A_5  (1<<5)
+#define OAREPORTTRIG2_INVERT_A_6  (1<<6)
+#define OAREPORTTRIG2_INVERT_A_7  (1<<7)
+#define OAREPORTTRIG2_INVERT_A_8  (1<<8)
+#define OAREPORTTRIG2_INVERT_A_9  (1<<9)
+#define OAREPORTTRIG2_INVERT_A_10 (1<<10)
+#define OAREPORTTRIG2_INVERT_A_11 (1<<11)
+#define OAREPORTTRIG2_INVERT_A_12 (1<<12)
+#define OAREPORTTRIG2_INVERT_A_13 (1<<13)
+#define OAREPORTTRIG2_INVERT_A_14 (1<<14)
+#define OAREPORTTRIG2_INVERT_A_15 (1<<15)
+#define OAREPORTTRIG2_INVERT_B_0  (1<<16)
+#define OAREPORTTRIG2_INVERT_B_1  (1<<17)
+#define OAREPORTTRIG2_INVERT_B_2  (1<<18)
+#define OAREPORTTRIG2_INVERT_B_3  (1<<19)
+#define OAREPORTTRIG2_INVERT_C_0  (1<<20)
+#define OAREPORTTRIG2_INVERT_C_1  (1<<21)
+#define OAREPORTTRIG2_INVERT_D_0  (1<<22)
+#define OAREPORTTRIG2_THRESHOLD_ENABLE	    (1<<23)
+#define OAREPORTTRIG2_REPORT_TRIGGER_ENABLE (1<<31)
+
+#define OAREPORTTRIG3 _MMIO(0x2748)
+#define OAREPORTTRIG3_NOA_SELECT_MASK	    0xf
+#define OAREPORTTRIG3_NOA_SELECT_8_SHIFT    0
+#define OAREPORTTRIG3_NOA_SELECT_9_SHIFT    4
+#define OAREPORTTRIG3_NOA_SELECT_10_SHIFT   8
+#define OAREPORTTRIG3_NOA_SELECT_11_SHIFT   12
+#define OAREPORTTRIG3_NOA_SELECT_12_SHIFT   16
+#define OAREPORTTRIG3_NOA_SELECT_13_SHIFT   20
+#define OAREPORTTRIG3_NOA_SELECT_14_SHIFT   24
+#define OAREPORTTRIG3_NOA_SELECT_15_SHIFT   28
+
+#define OAREPORTTRIG4 _MMIO(0x274c)
+#define OAREPORTTRIG4_NOA_SELECT_MASK	    0xf
+#define OAREPORTTRIG4_NOA_SELECT_0_SHIFT    0
+#define OAREPORTTRIG4_NOA_SELECT_1_SHIFT    4
+#define OAREPORTTRIG4_NOA_SELECT_2_SHIFT    8
+#define OAREPORTTRIG4_NOA_SELECT_3_SHIFT    12
+#define OAREPORTTRIG4_NOA_SELECT_4_SHIFT    16
+#define OAREPORTTRIG4_NOA_SELECT_5_SHIFT    20
+#define OAREPORTTRIG4_NOA_SELECT_6_SHIFT    24
+#define OAREPORTTRIG4_NOA_SELECT_7_SHIFT    28
+
+#define OAREPORTTRIG5 _MMIO(0x2750)
+#define OAREPORTTRIG5_THRESHOLD_MASK 0xffff
+#define OAREPORTTRIG5_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */
+
+#define OAREPORTTRIG6 _MMIO(0x2754)
+#define OAREPORTTRIG6_INVERT_A_0  (1<<0)
+#define OAREPORTTRIG6_INVERT_A_1  (1<<1)
+#define OAREPORTTRIG6_INVERT_A_2  (1<<2)
+#define OAREPORTTRIG6_INVERT_A_3  (1<<3)
+#define OAREPORTTRIG6_INVERT_A_4  (1<<4)
+#define OAREPORTTRIG6_INVERT_A_5  (1<<5)
+#define OAREPORTTRIG6_INVERT_A_6  (1<<6)
+#define OAREPORTTRIG6_INVERT_A_7  (1<<7)
+#define OAREPORTTRIG6_INVERT_A_8  (1<<8)
+#define OAREPORTTRIG6_INVERT_A_9  (1<<9)
+#define OAREPORTTRIG6_INVERT_A_10 (1<<10)
+#define OAREPORTTRIG6_INVERT_A_11 (1<<11)
+#define OAREPORTTRIG6_INVERT_A_12 (1<<12)
+#define OAREPORTTRIG6_INVERT_A_13 (1<<13)
+#define OAREPORTTRIG6_INVERT_A_14 (1<<14)
+#define OAREPORTTRIG6_INVERT_A_15 (1<<15)
+#define OAREPORTTRIG6_INVERT_B_0  (1<<16)
+#define OAREPORTTRIG6_INVERT_B_1  (1<<17)
+#define OAREPORTTRIG6_INVERT_B_2  (1<<18)
+#define OAREPORTTRIG6_INVERT_B_3  (1<<19)
+#define OAREPORTTRIG6_INVERT_C_0  (1<<20)
+#define OAREPORTTRIG6_INVERT_C_1  (1<<21)
+#define OAREPORTTRIG6_INVERT_D_0  (1<<22)
+#define OAREPORTTRIG6_THRESHOLD_ENABLE	    (1<<23)
+#define OAREPORTTRIG6_REPORT_TRIGGER_ENABLE (1<<31)
+
+#define OAREPORTTRIG7 _MMIO(0x2758)
+#define OAREPORTTRIG7_NOA_SELECT_MASK	    0xf
+#define OAREPORTTRIG7_NOA_SELECT_8_SHIFT    0
+#define OAREPORTTRIG7_NOA_SELECT_9_SHIFT    4
+#define OAREPORTTRIG7_NOA_SELECT_10_SHIFT   8
+#define OAREPORTTRIG7_NOA_SELECT_11_SHIFT   12
+#define OAREPORTTRIG7_NOA_SELECT_12_SHIFT   16
+#define OAREPORTTRIG7_NOA_SELECT_13_SHIFT   20
+#define OAREPORTTRIG7_NOA_SELECT_14_SHIFT   24
+#define OAREPORTTRIG7_NOA_SELECT_15_SHIFT   28
+
+#define OAREPORTTRIG8 _MMIO(0x275c)
+#define OAREPORTTRIG8_NOA_SELECT_MASK	    0xf
+#define OAREPORTTRIG8_NOA_SELECT_0_SHIFT    0
+#define OAREPORTTRIG8_NOA_SELECT_1_SHIFT    4
+#define OAREPORTTRIG8_NOA_SELECT_2_SHIFT    8
+#define OAREPORTTRIG8_NOA_SELECT_3_SHIFT    12
+#define OAREPORTTRIG8_NOA_SELECT_4_SHIFT    16
+#define OAREPORTTRIG8_NOA_SELECT_5_SHIFT    20
+#define OAREPORTTRIG8_NOA_SELECT_6_SHIFT    24
+#define OAREPORTTRIG8_NOA_SELECT_7_SHIFT    28
+
+#define OASTARTTRIG1 _MMIO(0x2710)
+#define OASTARTTRIG1_THRESHOLD_COUNT_MASK_MBZ 0xffff0000
+#define OASTARTTRIG1_THRESHOLD_MASK	      0xffff
+
+#define OASTARTTRIG2 _MMIO(0x2714)
+#define OASTARTTRIG2_INVERT_A_0 (1<<0)
+#define OASTARTTRIG2_INVERT_A_1 (1<<1)
+#define OASTARTTRIG2_INVERT_A_2 (1<<2)
+#define OASTARTTRIG2_INVERT_A_3 (1<<3)
+#define OASTARTTRIG2_INVERT_A_4 (1<<4)
+#define OASTARTTRIG2_INVERT_A_5 (1<<5)
+#define OASTARTTRIG2_INVERT_A_6 (1<<6)
+#define OASTARTTRIG2_INVERT_A_7 (1<<7)
+#define OASTARTTRIG2_INVERT_A_8 (1<<8)
+#define OASTARTTRIG2_INVERT_A_9 (1<<9)
+#define OASTARTTRIG2_INVERT_A_10 (1<<10)
+#define OASTARTTRIG2_INVERT_A_11 (1<<11)
+#define OASTARTTRIG2_INVERT_A_12 (1<<12)
+#define OASTARTTRIG2_INVERT_A_13 (1<<13)
+#define OASTARTTRIG2_INVERT_A_14 (1<<14)
+#define OASTARTTRIG2_INVERT_A_15 (1<<15)
+#define OASTARTTRIG2_INVERT_B_0 (1<<16)
+#define OASTARTTRIG2_INVERT_B_1 (1<<17)
+#define OASTARTTRIG2_INVERT_B_2 (1<<18)
+#define OASTARTTRIG2_INVERT_B_3 (1<<19)
+#define OASTARTTRIG2_INVERT_C_0 (1<<20)
+#define OASTARTTRIG2_INVERT_C_1 (1<<21)
+#define OASTARTTRIG2_INVERT_D_0 (1<<22)
+#define OASTARTTRIG2_THRESHOLD_ENABLE	    (1<<23)
+#define OASTARTTRIG2_START_TRIG_FLAG_MBZ    (1<<24)
+#define OASTARTTRIG2_EVENT_SELECT_0  (1<<28)
+#define OASTARTTRIG2_EVENT_SELECT_1  (1<<29)
+#define OASTARTTRIG2_EVENT_SELECT_2  (1<<30)
+#define OASTARTTRIG2_EVENT_SELECT_3  (1<<31)
+
+#define OASTARTTRIG3 _MMIO(0x2718)
+#define OASTARTTRIG3_NOA_SELECT_MASK	   0xf
+#define OASTARTTRIG3_NOA_SELECT_8_SHIFT    0
+#define OASTARTTRIG3_NOA_SELECT_9_SHIFT    4
+#define OASTARTTRIG3_NOA_SELECT_10_SHIFT   8
+#define OASTARTTRIG3_NOA_SELECT_11_SHIFT   12
+#define OASTARTTRIG3_NOA_SELECT_12_SHIFT   16
+#define OASTARTTRIG3_NOA_SELECT_13_SHIFT   20
+#define OASTARTTRIG3_NOA_SELECT_14_SHIFT   24
+#define OASTARTTRIG3_NOA_SELECT_15_SHIFT   28
+
+#define OASTARTTRIG4 _MMIO(0x271c)
+#define OASTARTTRIG4_NOA_SELECT_MASK	    0xf
+#define OASTARTTRIG4_NOA_SELECT_0_SHIFT    0
+#define OASTARTTRIG4_NOA_SELECT_1_SHIFT    4
+#define OASTARTTRIG4_NOA_SELECT_2_SHIFT    8
+#define OASTARTTRIG4_NOA_SELECT_3_SHIFT    12
+#define OASTARTTRIG4_NOA_SELECT_4_SHIFT    16
+#define OASTARTTRIG4_NOA_SELECT_5_SHIFT    20
+#define OASTARTTRIG4_NOA_SELECT_6_SHIFT    24
+#define OASTARTTRIG4_NOA_SELECT_7_SHIFT    28
+
+#define OASTARTTRIG5 _MMIO(0x2720)
+#define OASTARTTRIG5_THRESHOLD_COUNT_MASK_MBZ 0xffff0000
+#define OASTARTTRIG5_THRESHOLD_MASK	      0xffff
+
+#define OASTARTTRIG6 _MMIO(0x2724)
+#define OASTARTTRIG6_INVERT_A_0 (1<<0)
+#define OASTARTTRIG6_INVERT_A_1 (1<<1)
+#define OASTARTTRIG6_INVERT_A_2 (1<<2)
+#define OASTARTTRIG6_INVERT_A_3 (1<<3)
+#define OASTARTTRIG6_INVERT_A_4 (1<<4)
+#define OASTARTTRIG6_INVERT_A_5 (1<<5)
+#define OASTARTTRIG6_INVERT_A_6 (1<<6)
+#define OASTARTTRIG6_INVERT_A_7 (1<<7)
+#define OASTARTTRIG6_INVERT_A_8 (1<<8)
+#define OASTARTTRIG6_INVERT_A_9 (1<<9)
+#define OASTARTTRIG6_INVERT_A_10 (1<<10)
+#define OASTARTTRIG6_INVERT_A_11 (1<<11)
+#define OASTARTTRIG6_INVERT_A_12 (1<<12)
+#define OASTARTTRIG6_INVERT_A_13 (1<<13)
+#define OASTARTTRIG6_INVERT_A_14 (1<<14)
+#define OASTARTTRIG6_INVERT_A_15 (1<<15)
+#define OASTARTTRIG6_INVERT_B_0 (1<<16)
+#define OASTARTTRIG6_INVERT_B_1 (1<<17)
+#define OASTARTTRIG6_INVERT_B_2 (1<<18)
+#define OASTARTTRIG6_INVERT_B_3 (1<<19)
+#define OASTARTTRIG6_INVERT_C_0 (1<<20)
+#define OASTARTTRIG6_INVERT_C_1 (1<<21)
+#define OASTARTTRIG6_INVERT_D_0 (1<<22)
+#define OASTARTTRIG6_THRESHOLD_ENABLE	    (1<<23)
+#define OASTARTTRIG6_START_TRIG_FLAG_MBZ    (1<<24)
+#define OASTARTTRIG6_EVENT_SELECT_4  (1<<28)
+#define OASTARTTRIG6_EVENT_SELECT_5  (1<<29)
+#define OASTARTTRIG6_EVENT_SELECT_6  (1<<30)
+#define OASTARTTRIG6_EVENT_SELECT_7  (1<<31)
+
+#define OASTARTTRIG7 _MMIO(0x2728)
+#define OASTARTTRIG7_NOA_SELECT_MASK	   0xf
+#define OASTARTTRIG7_NOA_SELECT_8_SHIFT    0
+#define OASTARTTRIG7_NOA_SELECT_9_SHIFT    4
+#define OASTARTTRIG7_NOA_SELECT_10_SHIFT   8
+#define OASTARTTRIG7_NOA_SELECT_11_SHIFT   12
+#define OASTARTTRIG7_NOA_SELECT_12_SHIFT   16
+#define OASTARTTRIG7_NOA_SELECT_13_SHIFT   20
+#define OASTARTTRIG7_NOA_SELECT_14_SHIFT   24
+#define OASTARTTRIG7_NOA_SELECT_15_SHIFT   28
+
+#define OASTARTTRIG8 _MMIO(0x272c)
+#define OASTARTTRIG8_NOA_SELECT_MASK	   0xf
+#define OASTARTTRIG8_NOA_SELECT_0_SHIFT    0
+#define OASTARTTRIG8_NOA_SELECT_1_SHIFT    4
+#define OASTARTTRIG8_NOA_SELECT_2_SHIFT    8
+#define OASTARTTRIG8_NOA_SELECT_3_SHIFT    12
+#define OASTARTTRIG8_NOA_SELECT_4_SHIFT    16
+#define OASTARTTRIG8_NOA_SELECT_5_SHIFT    20
+#define OASTARTTRIG8_NOA_SELECT_6_SHIFT    24
+#define OASTARTTRIG8_NOA_SELECT_7_SHIFT    28
+
+/* CECX_0 */
+#define OACEC_COMPARE_LESS_OR_EQUAL	6
+#define OACEC_COMPARE_NOT_EQUAL		5
+#define OACEC_COMPARE_LESS_THAN		4
+#define OACEC_COMPARE_GREATER_OR_EQUAL	3
+#define OACEC_COMPARE_EQUAL		2
+#define OACEC_COMPARE_GREATER_THAN	1
+#define OACEC_COMPARE_ANY_EQUAL		0
+
+#define OACEC_COMPARE_VALUE_MASK    0xffff
+#define OACEC_COMPARE_VALUE_SHIFT   3
+
+#define OACEC_SELECT_NOA	(0<<19)
+#define OACEC_SELECT_PREV	(1<<19)
+#define OACEC_SELECT_BOOLEAN	(2<<19)
+
+/* CECX_1 */
+#define OACEC_MASK_MASK		    0xffff
+#define OACEC_CONSIDERATIONS_MASK   0xffff
+#define OACEC_CONSIDERATIONS_SHIFT  16
+
+#define OACEC0_0 _MMIO(0x2770)
+#define OACEC0_1 _MMIO(0x2774)
+#define OACEC1_0 _MMIO(0x2778)
+#define OACEC1_1 _MMIO(0x277c)
+#define OACEC2_0 _MMIO(0x2780)
+#define OACEC2_1 _MMIO(0x2784)
+#define OACEC3_0 _MMIO(0x2788)
+#define OACEC3_1 _MMIO(0x278c)
+#define OACEC4_0 _MMIO(0x2790)
+#define OACEC4_1 _MMIO(0x2794)
+#define OACEC5_0 _MMIO(0x2798)
+#define OACEC5_1 _MMIO(0x279c)
+#define OACEC6_0 _MMIO(0x27a0)
+#define OACEC6_1 _MMIO(0x27a4)
+#define OACEC7_0 _MMIO(0x27a8)
+#define OACEC7_1 _MMIO(0x27ac)
+
 
 #define _GEN7_PIPEA_DE_LOAD_SL	0x70068
 #define _GEN7_PIPEB_DE_LOAD_SL	0x71068
@@ -6900,6 +7237,7 @@  enum skl_disp_power_wells {
 # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE		(1 << 11)
 
 #define GEN6_UCGCTL3				_MMIO(0x9408)
+# define GEN6_OACSUNIT_CLOCK_GATE_DISABLE		(1 << 20)
 
 #define GEN7_UCGCTL4				_MMIO(0x940c)
 #define  GEN7_L3BANK2X_CLOCK_GATE_DISABLE	(1<<25)
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 962cc96..d974b71 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -1172,6 +1172,18 @@  struct drm_i915_gem_context_param {
 	__u64 value;
 };
 
+enum drm_i915_oa_format {
+	I915_OA_FORMAT_A13 = 1,
+	I915_OA_FORMAT_A29,
+	I915_OA_FORMAT_A13_B8_C8,
+	I915_OA_FORMAT_B4_C8,
+	I915_OA_FORMAT_A45_B8_C8,
+	I915_OA_FORMAT_B4_C8_A16,
+	I915_OA_FORMAT_C4_B8,
+
+	I915_OA_FORMAT_MAX	    /* non-ABI */
+};
+
 enum drm_i915_perf_property_id {
 	/**
 	 * Open the stream for a specific context handle (as used with
@@ -1180,6 +1192,32 @@  enum drm_i915_perf_property_id {
 	 */
 	DRM_I915_PERF_PROP_CTX_HANDLE = 1,
 
+	/**
+	 * A value of 1 requests the inclusion of raw OA unit reports as
+	 * part of stream samples.
+	 */
+	DRM_I915_PERF_PROP_SAMPLE_OA,
+
+	/**
+	 * The value specifies which set of OA unit metrics should be
+	 * be configured, defining the contents of any OA unit reports.
+	 */
+	DRM_I915_PERF_PROP_OA_METRICS_SET,
+
+	/**
+	 * The value specifies the size and layout of OA unit reports.
+	 */
+	DRM_I915_PERF_PROP_OA_FORMAT,
+
+	/**
+	 * Specifying this property implicitly requests periodic OA unit
+	 * sampling and (at least on Haswell) the sampling frequency is derived
+	 * from this exponent as follows:
+	 *
+	 *   80ns * 2^(period_exponent + 1)
+	 */
+	DRM_I915_PERF_PROP_OA_EXPONENT,
+
 	DRM_I915_PERF_PROP_MAX /* non-ABI */
 };
 
@@ -1199,7 +1237,22 @@  struct drm_i915_perf_open_param {
 	__u64 __user properties_ptr;
 };
 
+/**
+ * Enable data capture for a stream that was either opened in a disabled state
+ * via I915_PERF_FLAG_DIABLED or was later disabled via I915_PERF_IOCTL_DISABLE.
+ *
+ * It is intended to be cheaper to disable and enable a stream than it may be
+ * to close and re-open a stream with the same configuration.
+ *
+ * It's undefined whether any pending data for the stream will be lost.
+ */
 #define I915_PERF_IOCTL_ENABLE	_IO('i', 0x0)
+
+/**
+ * Disable data capture for a stream.
+ *
+ * It is an error to try and read a stream that is disabled.
+ */
 #define I915_PERF_IOCTL_DISABLE	_IO('i', 0x1)
 
 /**
@@ -1223,17 +1276,30 @@  enum drm_i915_perf_record_type {
 	 * every sample.
 	 *
 	 * The order of these sample properties given by userspace has no
-	 * affect on the ordering of data within a sample. The order will be
+	 * affect on the ordering of data within a sample. The order is
 	 * documented here.
 	 *
 	 * struct {
 	 *     struct drm_i915_perf_record_header header;
 	 *
-	 *     TODO: itemize extensible sample data here
+	 *     { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA
 	 * };
 	 */
 	DRM_I915_PERF_RECORD_SAMPLE = 1,
 
+	/*
+	 * Indicates that one or more OA reports were not written by the
+	 * hardware. This can happen for example if an MI_REPORT_PERF_COUNT
+	 * command collides with periodic sampling - which would be more likely
+	 * at higher sampling frequencies.
+	 */
+	DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2,
+
+	/**
+	 * An error occurred that resulted in all pending OA reports being lost.
+	 */
+	DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3,
+
 	DRM_I915_PERF_RECORD_MAX /* non-ABI */
 };