<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Mar 27, 2017 at 7:12 PM, Matthew Auld <span dir="ltr"><<a href="mailto:matthew.william.auld@gmail.com" target="_blank">matthew.william.auld@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On 03/23, Robert Bragg wrote:<br>
> Enables access to OA unit metrics for BDW, CHV, SKL and BXT which all<br>
> share (more-or-less) the same OA unit design.<br>
><br>
> Of particular note in comparison to Haswell: some OA unit HW config<br>
> state has become per-context state and as a consequence it is somewhat<br>
> more complicated to manage synchronous state changes from the cpu while<br>
> there's no guarantee of what context (if any) is currently actively<br>
> running on the gpu.<br>
><br>
> The periodic sampling frequency which can be particularly useful for<br>
> system-wide analysis (as opposed to command stream synchronised<br>
> MI_REPORT_PERF_COUNT commands) is perhaps the most surprising state to<br>
> have become per-context save and restored (while the OABUFFER<br>
> destination is still a shared, system-wide resource).<br>
><br>
> This support for gen8+ takes care to consider a number of timing<br>
> challenges involved in synchronously updating per-context state<br>
> primarily by programming all config state from the cpu and updating all<br>
> current and saved contexts synchronously while the OA unit is still<br>
> disabled.<br>
><br>
> The driver intentionally avoids depending on command streamer<br>
> programming to update OA state considering the lack of synchronization<br>
> between the automatic loading of OACTXCONTROL state (that includes the<br>
> periodic sampling state and enable state) on context restore and the<br>
> parsing of any general purpose BB the driver can control. I.e. this<br>
> implementation is careful to avoid the possibility of a context restore<br>
> temporarily enabling any out-of-date periodic sampling state. In<br>
> addition to the risk of transiently-out-of-date state being loaded<br>
> automatically; there are also internal HW latencies involved in the<br>
> loading of MUX configurations which would be difficult to account for<br>
> from the command streamer (and we only want to enable the unit when once<br>
> the MUX configuration is complete).<br>
><br>
> Since the Gen8+ OA unit design no longer supports clock gating the unit<br>
> off for a single given context (which effectively stopped any progress<br>
> of counters while any other context was running) and instead supports<br>
> tagging OA reports with a context ID for filtering on the CPU, it means<br>
> we can no longer hide the system-wide progress of counters from a<br>
> non-privileged application only interested in metrics for its own<br>
> context. Although we could theoretically try and subtract the progress<br>
> of other contexts before forwarding reports via read() we aren't in a<br>
> position to filter reports captured via MI_REPORT_PERF_COUNT commands.<br>
> As a result, for Gen8+, we always require the<br>
> dev.i915.perf_stream_paranoid to be unset for any access to OA metrics<br>
> if not root.<br>
><br>
> Signed-off-by: Robert Bragg <<a href="mailto:robert@sixbynine.org">robert@sixbynine.org</a>><br>
> ---<br>
>  drivers/gpu/drm/i915/i915_drv.<wbr>h         |   29 +-<br>
>  drivers/gpu/drm/i915/i915_gem_<wbr>context.h |    1 +<br>
>  drivers/gpu/drm/i915/i915_<wbr>perf.c        | 1034 ++++++++++++++++++++++++++++--<wbr>-<br>
>  drivers/gpu/drm/i915/i915_reg.<wbr>h         |   22 +<br>
>  drivers/gpu/drm/i915/intel_<wbr>lrc.c        |    5 +<br>
>  include/uapi/drm/i915_drm.h             |   19 +-<br>
>  6 files changed, 1029 insertions(+), 81 deletions(-)<br>
><br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>drv.h b/drivers/gpu/drm/i915/i915_<wbr>drv.h<br>
> index c4156a8a5dc0..190e699d5851 100644<br>
> --- a/drivers/gpu/drm/i915/i915_<wbr>drv.h<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>drv.h<br>
> @@ -731,6 +731,7 @@ intel_uncore_forcewake_for_<wbr>reg(struct drm_i915_private *dev_priv,<br>
>                              i915_reg_t reg, unsigned int op);<br>
><br>
>  struct intel_uncore_funcs {<br>
> +     int (*wait_for_rcs_busy)(struct drm_i915_private *dev_priv);<br>
</div></div>No longer needed.<br></blockquote><div><br></div><div>Ah, yup, removed now.<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
>       void (*force_wake_get)(struct drm_i915_private *dev_priv,<br>
>                              enum forcewake_domains domains);<br>
>       void (*force_wake_put)(struct drm_i915_private *dev_priv,<br>
> @@ -2084,9 +2085,17 @@ struct i915_oa_ops {<br>
>       void (*init_oa_buffer)(struct drm_i915_private *dev_priv);<br>
><br>
>       /**<br>
> -      * @enable_metric_set: Applies any MUX configuration to set up the<br>
> -      * Boolean and Custom (B/C) counters that are part of the counter<br>
> -      * reports being sampled. May apply system constraints such as<br>
> +      * @select_metric_set: The auto generated code that checks whether a<br>
> +      * requested OA config is applicable to the system and if so sets up<br>
> +      * the mux, oa and flex eu register config pointers according to the<br>
> +      * current dev_priv->perf.oa.metrics_set.<br>
> +      */<br>
> +     int (*select_metric_set)(struct drm_i915_private *dev_priv);<br>
> +<br>
> +     /**<br>
> +      * @enable_metric_set: Selects and applies any MUX configuration to set<br>
> +      * up the Boolean and Custom (B/C) counters that are part of the<br>
> +      * counter reports being sampled. May apply system constraints such as<br>
>        * disabling EU clock gating as required.<br>
>        */<br>
>       int (*enable_metric_set)(struct drm_i915_private *dev_priv);<br>
> @@ -2492,6 +2501,7 @@ struct drm_i915_private {<br>
>                       struct {<br>
>                               struct i915_vma *vma;<br>
>                               u8 *vaddr;<br>
> +                             u32 last_ctx_id;<br>
>                               int format;<br>
>                               int format_size;<br>
><br>
> @@ -2561,6 +2571,14 @@ struct drm_i915_private {<br>
>                       } oa_buffer;<br>
><br>
>                       u32 gen7_latched_oastatus1;<br>
> +                     u32 ctx_oactxctrl_off;<br>
> +                     u32 ctx_flexeu0_off;<br>
</div></div>Could we use _offset, for a second I thought were actually turning something off...<br></blockquote><div><br></div><div>Yeah, sounds better.<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
> +                     /* The RPT_ID/reason field for Gen8+ includes a bit<br>
> +                      * to determine if the CTX ID in the report is valid<br>
> +                      * but the specific bit differs between Gen 8 and 9<br>
> +                      */<br>
> +                     u32 gen8_valid_ctx_bit;<br>
><br>
>                       struct i915_oa_ops ops;<br>
>                       const struct i915_oa_format *oa_formats;<br>
> @@ -2871,6 +2889,8 @@ intel_info(const struct drm_i915_private *dev_priv)<br>
>  #define IS_KBL_ULX(dev_priv) (INTEL_DEVID(dev_priv) == 0x590E || \<br>
>                                INTEL_DEVID(dev_priv) == 0x5915 || \<br>
>                                INTEL_DEVID(dev_priv) == 0x591E)<br>
> +#define IS_SKL_GT2(dev_priv) (IS_SKYLAKE(dev_priv) && \<br>
> +                              (INTEL_DEVID(dev_priv) & 0x00F0) == 0x0010)<br>
>  #define IS_SKL_GT3(dev_priv) (IS_SKYLAKE(dev_priv) && \<br>
>                                (INTEL_DEVID(dev_priv) & 0x00F0) == 0x0020)<br>
>  #define IS_SKL_GT4(dev_priv) (IS_SKYLAKE(dev_priv) && \<br>
> @@ -3622,6 +3642,9 @@ i915_gem_context_lookup_<wbr>timeline(struct i915_gem_context *ctx,<br>
><br>
>  int i915_perf_open_ioctl(struct drm_device *dev, void *data,<br>
>                        struct drm_file *file);<br>
> +void i915_oa_update_reg_state(<wbr>struct intel_engine_cs *engine,<br>
> +                           struct i915_gem_context *ctx,<br>
> +                           uint32_t *reg_state);<br>
><br>
>  /* i915_gem_evict.c */<br>
>  int __must_check i915_gem_evict_something(<wbr>struct i915_address_space *vm,<br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>gem_context.h b/drivers/gpu/drm/i915/i915_<wbr>gem_context.h<br>
> index 4af2ab94558b..3f4ce73bea43 100644<br>
> --- a/drivers/gpu/drm/i915/i915_<wbr>gem_context.h<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>gem_context.h<br>
> @@ -151,6 +151,7 @@ struct i915_gem_context {<br>
>               u64 lrc_desc;<br>
>               int pin_count;<br>
>               bool initialised;<br>
> +             atomic_t oa_state_dirty;<br>
>       } engine[I915_NUM_ENGINES];<br>
><br>
>       /** ring_size: size for allocating the per-engine ring buffer */<br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>perf.c b/drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
> index 36d07ca68029..dc5f0121e305 100644<br>
> --- a/drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>perf.c<br>
> @@ -196,6 +196,12 @@<br>
><br>
>  #include "i915_drv.h"<br>
>  #include "i915_oa_hsw.h"<br>
> +#include "i915_oa_bdw.h"<br>
> +#include "i915_oa_chv.h"<br>
> +#include "i915_oa_sklgt2.h"<br>
> +#include "i915_oa_sklgt3.h"<br>
> +#include "i915_oa_sklgt4.h"<br>
> +#include "i915_oa_bxt.h"<br>
><br>
>  /* HW requires this to be a power of two, between 128k and 16M, though driver<br>
>   * is currently generally designed assuming the largest 16M size is used such<br>
> @@ -272,6 +278,13 @@ static u32 i915_perf_stream_paranoid = true;<br>
><br>
>  #define INVALID_CTX_ID 0xffffffff<br>
><br>
> +/* On Gen8+ automatically triggered OA reports include a 'reason' field... */<br>
> +#define OAREPORT_REASON_MASK           0x3f<br>
> +#define OAREPORT_REASON_SHIFT          19<br>
> +#define OAREPORT_REASON_TIMER          (1<<0)<br>
> +#define OAREPORT_REASON_CTX_SWITCH     (1<<3)<br>
> +#define OAREPORT_REASON_CLK_RATIO      (1<<5)<br>
</div></div>Would the expectation be that userspace would peek at the reason field?<br>
If so do we not want to throw this stuff in i915_drm.h?<br></blockquote><div><br></div><div>I consider this part of the hardware interface documented in the PRMs for userspace to reference, not software ABI. Maybe a grey area but it doesn't seem to me that it should be in i915_drm.h.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
><br>
>  /* For sysctl proc_dointvec_minmax of i915_oa_max_sample_rate<br>
>   *<br>
> @@ -303,6 +316,13 @@ static struct i915_oa_format hsw_oa_formats[I915_OA_FORMAT_<wbr>MAX] = {<br>
>       [I915_OA_FORMAT_C4_B8]      = { 7, 64 },<br>
>  };<br>
><br>
> +static struct i915_oa_format gen8_plus_oa_formats[I915_OA_<wbr>FORMAT_MAX] = {<br>
> +     [I915_OA_FORMAT_A12]                = { 0, 64 },<br>
> +     [I915_OA_FORMAT_A12_B8_C8]          = { 2, 128 },<br>
> +     [I915_OA_FORMAT_A32u40_A4u32_<wbr>B8_C8] = { 5, 256 },<br>
> +     [I915_OA_FORMAT_C4_B8]              = { 7, 64 },<br>
> +};<br>
> +<br>
>  #define SAMPLE_OA_REPORT      (1<<0)<br>
><br>
>  /**<br>
> @@ -333,6 +353,122 @@ struct perf_open_properties {<br>
>  };<br>
><br>
>  /**<br>
> + * gen8_oa_buffer_check_unlocked - check for data and update tail ptr state<br>
> + * @dev_priv: i915 device instance<br>
> + *<br>
> + * This is either called via fops (for blocking reads in user ctx) or the poll<br>
> + * check hrtimer (atomic ctx) to check the OA buffer tail pointer and check<br>
> + * if there is data available for userspace to read.<br>
> + *<br>
> + * This function is central to providing a workaround for the OA unit tail<br>
> + * pointer having a race with respect to what data is visible to the CPU.<br>
> + * It is responsible for reading tail pointers from the hardware and giving<br>
> + * the pointers time to 'age' before they are made available for reading.<br>
> + * (See description of OA_TAIL_MARGIN_NSEC above for further details.)<br>
> + *<br>
> + * Besides returning true when there is data available to read() this function<br>
> + * also has the side effect of updating the oa_buffer.tails[], .aging_timestamp<br>
> + * and .aged_tail_idx state used for reading.<br>
> + *<br>
> + * Note: It's safe to read OA config state here unlocked, assuming that this is<br>
> + * only called while the stream is enabled, while the global OA configuration<br>
> + * can't be modified.<br>
> + *<br>
> + * Returns: %true if the OA buffer contains data, else %false<br>
> + */<br>
> +static bool gen8_oa_buffer_check_unlocked(<wbr>struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     int report_size = dev_priv->perf.oa.oa_buffer.<wbr>format_size;<br>
> +     unsigned long flags;<br>
> +     unsigned int aged_idx;<br>
> +     u32 head, hw_tail, aged_tail, aging_tail;<br>
> +     u64 now;<br>
> +<br>
> +     /* We have to consider the (unlikely) possibility that read() errors<br>
> +      * could result in an OA buffer reset which might reset the head,<br>
> +      * tails[] and aged_tail state.<br>
> +      */<br>
> +     spin_lock_irqsave(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock, flags);<br>
> +<br>
> +     /* NB: The head we observe here might effectively be a little out of<br>
> +      * date (between head and tails[aged_idx].offset if there is currently<br>
> +      * a read() in progress.<br>
> +      */<br>
> +     head = dev_priv->perf.oa.oa_buffer.<wbr>head;<br>
> +<br>
> +     aged_idx = dev_priv->perf.oa.oa_buffer.<wbr>aged_tail_idx;<br>
> +     aged_tail = dev_priv->perf.oa.oa_buffer.<wbr>tails[aged_idx].offset;<br>
> +     aging_tail = dev_priv->perf.oa.oa_buffer.<wbr>tails[!aged_idx].offset;<br>
> +<br>
> +     hw_tail = I915_READ(GEN8_OATAILPTR) & GEN8_OATAILPTR_MASK;<br>
</div></div>You didn't fancy turning this into a vfunc or something, the gen7<br>
oa_buffer_check is identical except for the tail register business?<br></blockquote><div><br></div><div>Yeah, it had definitly crossed my mind to do this, and I'd  been considering doing this as a follow up, but it's probably worth just doing upfront instead. It's not great to have a copy of this fiddly workaround code.<br><br></div><div>Okey, just made the change locally and will send out in  a bit.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
> +     /* The tail pointer increases in 64 byte increments,<br>
> +      * not in report_size steps...<br>
> +      */<br>
> +     hw_tail &= ~(report_size - 1);<br>
> +<br>
> +     now = ktime_get_mono_fast_ns();<br>
> +<br>
> +     /* Update the aged tail<br>
> +      *<br>
> +      * Flip the tail pointer available for read()s once the aging tail is<br>
> +      * old enough to trust that the corresponding data will be visible to<br>
> +      * the CPU...<br>
> +      *<br>
> +      * Do this before updating the aging pointer in case we may be able to<br>
> +      * immediately start aging a new pointer too (if new data has become<br>
> +      * available) without needing to wait for a later hrtimer callback.<br>
> +      */<br>
> +     if (aging_tail != INVALID_TAIL_PTR &&<br>
> +         ((now - dev_priv->perf.oa.oa_buffer.<wbr>aging_timestamp) ><br>
> +          OA_TAIL_MARGIN_NSEC)) {<br>
> +<br>
> +             aged_idx ^= 1;<br>
> +             dev_priv->perf.oa.oa_buffer.<wbr>aged_tail_idx = aged_idx;<br>
> +<br>
> +             aged_tail = aging_tail;<br>
> +<br>
> +             /* Mark that we need a new pointer to start aging... */<br>
> +             dev_priv->perf.oa.oa_buffer.<wbr>tails[!aged_idx].offset = INVALID_TAIL_PTR;<br>
> +             aging_tail = INVALID_TAIL_PTR;<br>
> +     }<br>
> +<br>
> +     /* Update the aging tail<br>
> +      *<br>
> +      * We throttle aging tail updates until we have a new tail that<br>
> +      * represents >= one report more data than is already available for<br>
> +      * reading. This ensures there will be enough data for a successful<br>
> +      * read once this new pointer has aged and ensures we will give the new<br>
> +      * pointer time to age.<br>
> +      */<br>
> +     if (aging_tail == INVALID_TAIL_PTR &&<br>
> +         (aged_tail == INVALID_TAIL_PTR ||<br>
> +          OA_TAKEN(hw_tail, aged_tail) >= report_size)) {<br>
> +             struct i915_vma *vma = dev_priv->perf.oa.oa_buffer.<wbr>vma;<br>
> +             u32 gtt_offset = i915_ggtt_offset(vma);<br>
> +<br>
> +             /* Be paranoid and do a bounds check on the pointer read back<br>
> +              * from hardware, just in case some spurious hardware condition<br>
> +              * could put the tail out of bounds...<br>
> +              */<br>
> +             if (hw_tail >= gtt_offset &&<br>
> +                 hw_tail < (gtt_offset + OA_BUFFER_SIZE)) {<br>
> +                     dev_priv->perf.oa.oa_buffer.<wbr>tails[!aged_idx].offset =<br>
> +                             aging_tail = hw_tail;<br>
> +                     dev_priv->perf.oa.oa_buffer.<wbr>aging_timestamp = now;<br>
> +             } else {<br>
> +                     DRM_ERROR("Ignoring spurious out of range OA buffer tail pointer = %u\n",<br>
> +                               hw_tail);<br>
> +             }<br>
> +     }<br>
> +<br>
> +     spin_unlock_irqrestore(&dev_<wbr>priv->perf.oa.oa_buffer.ptr_<wbr>lock, flags);<br>
> +<br>
> +     return aged_tail == INVALID_TAIL_PTR ?<br>
> +             false : OA_TAKEN(aged_tail, head) >= report_size;<br>
> +}<br>
> +<br>
> +/**<br>
>   * gen7_oa_buffer_check_unlocked - check for data and update tail ptr state<br>
>   * @dev_priv: i915 device instance<br>
>   *<br>
> @@ -553,6 +689,284 @@ static int append_oa_sample(struct i915_perf_stream *stream,<br>
>   *<br>
>   * Returns: 0 on success, negative error code on failure.<br>
>   */<br>
> +static int gen8_append_oa_reports(struct i915_perf_stream *stream,<br>
> +                               char __user *buf,<br>
> +                               size_t count,<br>
> +                               size_t *offset)<br>
> +{<br>
> +     struct drm_i915_private *dev_priv = stream->dev_priv;<br>
> +     int report_size = dev_priv->perf.oa.oa_buffer.<wbr>format_size;<br>
> +     u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.<wbr>vaddr;<br>
> +     u32 gtt_offset = i915_ggtt_offset(dev_priv-><wbr>perf.oa.oa_buffer.vma);<br>
> +     u32 mask = (OA_BUFFER_SIZE - 1);<br>
> +     size_t start_offset = *offset;<br>
> +     unsigned long flags;<br>
> +     unsigned int aged_tail_idx;<br>
> +     u32 head, tail;<br>
> +     u32 taken;<br>
> +     int ret = 0;<br>
> +<br>
> +     if (WARN_ON(!stream->enabled))<br>
> +             return -EIO;<br>
> +<br>
> +     spin_lock_irqsave(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock, flags);<br>
> +<br>
> +     head = dev_priv->perf.oa.oa_buffer.<wbr>head;<br>
> +     aged_tail_idx = dev_priv->perf.oa.oa_buffer.<wbr>aged_tail_idx;<br>
> +     tail = dev_priv->perf.oa.oa_buffer.<wbr>tails[aged_tail_idx].offset;<br>
> +<br>
> +     spin_unlock_irqrestore(&dev_<wbr>priv->perf.oa.oa_buffer.ptr_<wbr>lock, flags);<br>
> +<br>
> +     /* An invalid tail pointer here means we're still waiting for the poll<br>
> +      * hrtimer callback to give us a pointer<br>
> +      */<br>
> +     if (tail == INVALID_TAIL_PTR)<br>
> +             return -EAGAIN;<br>
> +<br>
> +     /* NB: oa_buffer.head/tail include the gtt_offset which we don't want<br>
> +      * while indexing relative to oa_buf_base.<br>
> +      */<br>
> +     head -= gtt_offset;<br>
> +     tail -= gtt_offset;<br>
> +<br>
> +     /* An out of bounds or misaligned head or tail pointer implies a driver<br>
> +      * bug since we validate + align the tail pointers we read from the<br>
> +      * hardware and we are in full control of the head pointer which should<br>
> +      * only be incremented by multiples of the report size (notably also<br>
> +      * all a power of two).<br>
> +      */<br>
> +     if (WARN_ONCE(head > OA_BUFFER_SIZE || head % report_size ||<br>
> +                   tail > OA_BUFFER_SIZE || tail % report_size,<br>
> +                   "Inconsistent OA buffer pointers: head = %u, tail = %u\n",<br>
> +                   head, tail))<br>
> +             return -EIO;<br>
> +<br>
> +<br>
> +     for (/* none */;<br>
> +          (taken = OA_TAKEN(tail, head));<br>
> +          head = (head + report_size) & mask) {<br>
> +             u8 *report = oa_buf_base + head;<br>
> +             u32 *report32 = (void *)report;<br>
> +             u32 ctx_id;<br>
> +             u32 reason;<br>
> +<br>
> +             /* All the report sizes factor neatly into the buffer<br>
> +              * size so we never expect to see a report split<br>
> +              * between the beginning and end of the buffer.<br>
> +              *<br>
> +              * Given the initial alignment check a misalignment<br>
> +              * here would imply a driver bug that would result<br>
> +              * in an overrun.<br>
> +              */<br>
> +             if (WARN_ON((OA_BUFFER_SIZE - head) < report_size)) {<br>
> +                     DRM_ERROR("Spurious OA head ptr: non-integral report offset\n");<br>
> +                     break;<br>
> +             }<br>
> +<br>
> +             /* The reason field includes flags identifying what<br>
> +              * triggered this specific report (mostly timer<br>
> +              * triggered or e.g. due to a context switch).<br>
> +              *<br>
> +              * This field is never expected to be zero so we can<br>
> +              * check that the report isn't invalid before copying<br>
> +              * it to userspace...<br>
> +              */<br>
> +             reason = ((report32[0] >> OAREPORT_REASON_SHIFT) &<br>
> +                       OAREPORT_REASON_MASK);<br>
> +             if (reason == 0) {<br>
> +                     if (__ratelimit(&dev_priv->perf.<wbr>oa.spurious_report_rs))<br>
> +                             DRM_NOTE("Skipping spurious, invalid OA report\n");<br>
> +                     continue;<br>
> +             }<br>
> +<br>
> +             /* XXX: Just keep the lower 21 bits for now since I'm not<br>
> +              * entirely sure if the HW touches any of the higher bits in<br>
> +              * this field<br>
> +              */<br>
> +             ctx_id = report32[2] & 0x1fffff;<br>
> +<br>
> +             /* Squash whatever is in the CTX_ID field if it's<br>
> +              * marked as invalid to be sure we avoid<br>
> +              * false-positive, single-context filtering below...<br>
> +              */<br>
> +             if (!(report32[0] & dev_priv->perf.oa.gen8_valid_<wbr>ctx_bit))<br>
> +                     ctx_id = report32[2] = INVALID_CTX_ID;<br>
> +<br>
> +             /* NB: For Gen 8 the OA unit no longer supports clock gating<br>
> +              * off for a specific context and the kernel can't securely<br>
> +              * stop the counters from updating as system-wide / global<br>
> +              * values.<br>
> +              *<br>
> +              * Automatic reports now include a context ID so reports can be<br>
> +              * filtered on the cpu but it's not worth trying to<br>
> +              * automatically subtract/hide counter progress for other<br>
> +              * contexts while filtering since we can't stop userspace<br>
> +              * issuing MI_REPORT_PERF_COUNT commands which would still<br>
> +              * provide a side-band view of the real values.<br>
> +              *<br>
> +              * To allow userspace (such as Mesa/GL_INTEL_performance_<wbr>query)<br>
> +              * to normalize counters for a single filtered context then it<br>
> +              * needs be forwarded bookend context-switch reports so that it<br>
> +              * can track switches in between MI_REPORT_PERF_COUNT commands<br>
> +              * and can itself subtract/ignore the progress of counters<br>
> +              * associated with other contexts. Note that the hardware<br>
> +              * automatically triggers reports when switching to a new<br>
> +              * context which are tagged with the ID of the newly active<br>
> +              * context. To avoid the complexity (and likely fragility) of<br>
> +              * reading ahead while parsing reports to try and minimize<br>
> +              * forwarding redundant context switch reports (i.e. between<br>
> +              * other, unrelated contexts) we simply elect to forward them<br>
> +              * all.<br>
> +              *<br>
> +              * We don't rely solely on the reason field to identify context<br>
> +              * switches since it's not-uncommon for periodic samples to<br>
> +              * identify a switch before any 'context switch' report.<br>
> +              */<br>
> +             if (!dev_priv->perf.oa.exclusive_<wbr>stream->ctx ||<br>
> +                 dev_priv->perf.oa.specific_<wbr>ctx_id == ctx_id ||<br>
> +                 (dev_priv->perf.oa.oa_buffer.<wbr>last_ctx_id ==<br>
> +                  dev_priv->perf.oa.specific_<wbr>ctx_id) ||<br>
> +                 reason & OAREPORT_REASON_CTX_SWITCH) {<br>
> +<br>
> +                     /* While filtering for a single context we avoid<br>
> +                      * leaking the IDs of other contexts.<br>
> +                      */<br>
> +                     if (dev_priv->perf.oa.exclusive_<wbr>stream->ctx &&<br>
> +                         dev_priv->perf.oa.specific_<wbr>ctx_id != ctx_id) {<br>
> +                             report32[2] = INVALID_CTX_ID;<br>
> +                     }<br>
> +<br>
> +                     ret = append_oa_sample(stream, buf, count, offset,<br>
> +                                            report);<br>
> +                     if (ret)<br>
> +                             break;<br>
> +<br>
> +                     dev_priv->perf.oa.oa_buffer.<wbr>last_ctx_id = ctx_id;<br>
> +             }<br>
> +<br>
> +             /* The above reason field sanity check is based on<br>
> +              * the assumption that the OA buffer is initially<br>
> +              * zeroed and we reset the field after copying so the<br>
> +              * check is still meaningful once old reports start<br>
> +              * being overwritten.<br>
> +              */<br>
> +             report32[0] = 0;<br>
> +     }<br>
> +<br>
> +     if (start_offset != *offset) {<br>
> +             spin_lock_irqsave(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock, flags);<br>
> +<br>
> +             /* We removed the gtt_offset for the copy loop above, indexing<br>
> +              * relative to oa_buf_base so put back here...<br>
> +              */<br>
> +             head += gtt_offset;<br>
> +<br>
> +             I915_WRITE(GEN8_OAHEADPTR, head & GEN8_OAHEADPTR_MASK);<br>
> +             dev_priv->perf.oa.oa_buffer.<wbr>head = head;<br>
> +<br>
> +             spin_unlock_irqrestore(&dev_<wbr>priv->perf.oa.oa_buffer.ptr_<wbr>lock, flags);<br>
> +     }<br>
> +<br>
> +     return ret;<br>
> +}<br>
> +<br>
> +/**<br>
> + * gen8_oa_read - copy status records then buffered OA reports<br>
> + * @stream: An i915-perf stream opened for OA metrics<br>
> + * @buf: destination buffer given by userspace<br>
> + * @count: the number of bytes userspace wants to read<br>
> + * @offset: (inout): the current position for writing into @buf<br>
> + *<br>
> + * Checks OA unit status registers and if necessary appends corresponding<br>
> + * status records for userspace (such as for a buffer full condition) and then<br>
> + * initiate appending any buffered OA reports.<br>
> + *<br>
> + * Updates @offset according to the number of bytes successfully copied into<br>
> + * the userspace buffer.<br>
> + *<br>
> + * NB: some data may be successfully copied to the userspace buffer<br>
> + * even if an error is returned, and this is reflected in the<br>
> + * updated @read_state.<br>
</div></div>updated @read_state ?<br></blockquote><div> </div><div>Ah, the api used to return the offset via a @read_state structure, this should be 'updated @offset'<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> + *<br>
> + * Returns: zero on success or a negative error code<br>
> + */<br>
> +static int gen8_oa_read(struct i915_perf_stream *stream,<br>
> +                     char __user *buf,<br>
> +                     size_t count,<br>
> +                     size_t *offset)<br>
> +{<br>
> +     struct drm_i915_private *dev_priv = stream->dev_priv;<br>
> +     u32 oastatus;<br>
> +     int ret;<br>
> +<br>
> +     if (WARN_ON(!dev_priv->perf.oa.<wbr>oa_buffer.vaddr))<br>
> +             return -EIO;<br>
> +<br>
> +     oastatus = I915_READ(GEN8_OASTATUS);<br>
> +<br>
> +     /* We treat OABUFFER_OVERFLOW as a significant error:<br>
> +      *<br>
> +      * Although theoretically we could handle this more gracefully<br>
> +      * sometimes, some Gens don't correctly suppress certain<br>
> +      * automatically triggered reports in this condition and so we<br>
> +      * have to assume that old reports are now being trampled<br>
> +      * over.<br>
> +      *<br>
> +      * Considering how we don't currently give userspace control<br>
> +      * over the OA buffer size and always configure a large 16MB<br>
> +      * buffer, then a buffer overflow does anyway likely indicate<br>
> +      * that something has gone quite badly wrong.<br>
> +      */<br>
> +     if (oastatus & GEN8_OASTATUS_OABUFFER_<wbr>OVERFLOW) {<br>
> +             ret = append_oa_status(stream, buf, count, offset,<br>
> +                                    DRM_I915_PERF_RECORD_OA_<wbr>BUFFER_LOST);<br>
> +             if (ret)<br>
> +                     return ret;<br>
> +<br>
> +             DRM_DEBUG("OA buffer overflow (exponent = %d): force restart\n",<br>
> +                       dev_priv->perf.oa.period_<wbr>exponent);<br>
> +<br>
> +             dev_priv->perf.oa.ops.oa_<wbr>disable(dev_priv);<br>
> +             dev_priv->perf.oa.ops.oa_<wbr>enable(dev_priv);<br>
> +<br>
> +             /* Note: .oa_enable() is expected to re-init the oabuffer<br>
> +              * and reset GEN8_OASTATUS for us<br>
> +              */<br>
> +             oastatus = I915_READ(GEN8_OASTATUS);<br>
> +     }<br>
> +<br>
> +     if (oastatus & GEN8_OASTATUS_REPORT_LOST) {<br>
> +             ret = append_oa_status(stream, buf, count, offset,<br>
> +                                    DRM_I915_PERF_RECORD_OA_<wbr>REPORT_LOST);<br>
> +             if (ret == 0) {<br>
> +                     I915_WRITE(GEN8_OASTATUS,<br>
> +                                oastatus & ~GEN8_OASTATUS_REPORT_LOST);<br>
> +             }<br>
> +     }<br>
> +<br>
> +     return gen8_append_oa_reports(stream, buf, count, offset);<br>
> +}<br>
> +<br>
> +/**<br>
> + * Copies all buffered OA reports into userspace read() buffer.<br>
> + * @stream: An i915-perf stream opened for OA metrics<br>
> + * @buf: destination buffer given by userspace<br>
> + * @count: the number of bytes userspace wants to read<br>
> + * @offset: (inout): the current position for writing into @buf<br>
> + *<br>
> + * Notably any error condition resulting in a short read (-%ENOSPC or<br>
> + * -%EFAULT) will be returned even though one or more records may<br>
> + * have been successfully copied. In this case it's up to the caller<br>
> + * to decide if the error should be squashed before returning to<br>
> + * userspace.<br>
> + *<br>
> + * Note: reports are consumed from the head, and appended to the<br>
> + * tail, so the tail chases the head?... If you think that's mad<br>
> + * and back-to-front you're not alone, but this follows the<br>
> + * Gen PRM naming convention.<br>
> + *<br>
> + * Returns: 0 on success, negative error code on failure.<br>
> + */<br>
>  static int gen7_append_oa_reports(struct i915_perf_stream *stream,<br>
>                                 char __user *buf,<br>
>                                 size_t count,<br>
> @@ -733,7 +1147,8 @@ static int gen7_oa_read(struct i915_perf_stream *stream,<br>
>               if (ret)<br>
>                       return ret;<br>
><br>
> -             DRM_DEBUG("OA buffer overflow: force restart\n");<br>
> +             DRM_DEBUG("OA buffer overflow (exponent = %d): force restart\n",<br>
> +                       dev_priv->perf.oa.period_<wbr>exponent);<br>
><br>
>               dev_priv->perf.oa.ops.oa_<wbr>disable(dev_priv);<br>
>               dev_priv->perf.oa.ops.oa_<wbr>enable(dev_priv);<br>
> @@ -833,33 +1248,37 @@ static int i915_oa_read(struct i915_perf_stream *stream,<br>
>  static int oa_get_render_ctx_id(struct i915_perf_stream *stream)<br>
>  {<br>
>       struct drm_i915_private *dev_priv = stream->dev_priv;<br>
> -     struct intel_engine_cs *engine = dev_priv->engine[RCS];<br>
> -     int ret;<br>
><br>
> -     ret = i915_mutex_lock_interruptible(<wbr>&dev_priv->drm);<br>
> -     if (ret)<br>
> -             return ret;<br>
> +     if (i915.enable_execlists)<br>
> +             dev_priv->perf.oa.specific_<wbr>ctx_id = stream->ctx->hw_id;<br>
> +     else {<br>
> +             struct intel_engine_cs *engine = dev_priv->engine[RCS];<br>
> +             int ret;<br>
><br>
> -     /* As the ID is the gtt offset of the context's vma we pin<br>
> -      * the vma to ensure the ID remains fixed.<br>
> -      *<br>
> -      * NB: implied RCS engine...<br>
> -      */<br>
> -     ret = engine->context_pin(engine, stream->ctx);<br>
> -     if (ret)<br>
> -             goto unlock;<br>
> +             ret = i915_mutex_lock_interruptible(<wbr>&dev_priv->drm);<br>
> +             if (ret)<br>
> +                     return ret;<br>
><br>
> -     /* Explicitly track the ID (instead of calling i915_ggtt_offset()<br>
> -      * on the fly) considering the difference with gen8+ and<br>
> -      * execlists<br>
> -      */<br>
> -     dev_priv->perf.oa.specific_<wbr>ctx_id =<br>
> -             i915_ggtt_offset(stream->ctx-><wbr>engine[engine->id].state);<br>
> +             /* As the ID is the gtt offset of the context's vma we pin<br>
> +              * the vma to ensure the ID remains fixed.<br>
> +              */<br>
> +             ret = engine->context_pin(engine, stream->ctx);<br>
> +             if (ret) {<br>
> +                     mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +                     return ret;<br>
> +             }<br>
><br>
> -unlock:<br>
> -     mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +             /* Explicitly track the ID (instead of calling<br>
> +              * i915_ggtt_offset() on the fly) considering the difference<br>
> +              * with gen8+ and execlists<br>
> +              */<br>
> +             dev_priv->perf.oa.specific_<wbr>ctx_id =<br>
> +                     i915_ggtt_offset(stream->ctx-><wbr>engine[engine->id].state);<br>
><br>
> -     return ret;<br>
> +             mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +     }<br>
> +<br>
> +     return 0;<br>
>  }<br>
><br>
>  /**<br>
> @@ -874,12 +1293,16 @@ static void oa_put_render_ctx_id(struct i915_perf_stream *stream)<br>
>       struct drm_i915_private *dev_priv = stream->dev_priv;<br>
>       struct intel_engine_cs *engine = dev_priv->engine[RCS];<br>
><br>
> -     mutex_lock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +     if (i915.enable_execlists) {<br>
> +             dev_priv->perf.oa.specific_<wbr>ctx_id = INVALID_CTX_ID;<br>
> +     } else {<br>
> +             mutex_lock(&dev_priv->drm.<wbr>struct_mutex);<br>
><br>
> -     dev_priv->perf.oa.specific_<wbr>ctx_id = INVALID_CTX_ID;<br>
> -     engine->context_unpin(engine, stream->ctx);<br>
> +             dev_priv->perf.oa.specific_<wbr>ctx_id = INVALID_CTX_ID;<br>
> +             engine->context_unpin(engine, stream->ctx);<br>
><br>
> -     mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +             mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +     }<br>
>  }<br>
><br>
>  static void<br>
> @@ -964,6 +1387,56 @@ static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)<br>
>       dev_priv->perf.oa.pollin = false;<br>
>  }<br>
><br>
> +static void gen8_init_oa_buffer(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     u32 gtt_offset = i915_ggtt_offset(dev_priv-><wbr>perf.oa.oa_buffer.vma);<br>
> +     unsigned long flags;<br>
> +<br>
> +     spin_lock_irqsave(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock, flags);<br>
> +<br>
> +     I915_WRITE(GEN8_OASTATUS, 0);<br>
> +     I915_WRITE(GEN8_OAHEADPTR, gtt_offset);<br>
> +     dev_priv->perf.oa.oa_buffer.<wbr>head = gtt_offset;<br>
> +<br>
> +     I915_WRITE(GEN8_OABUFFER_UDW, 0);<br>
> +<br>
> +     /* PRM says:<br>
> +      *<br>
> +      *  "This MMIO must be set before the OATAILPTR<br>
> +      *  register and after the OAHEADPTR register. This is<br>
> +      *  to enable proper functionality of the overflow<br>
> +      *  bit."<br>
> +      */<br>
> +     I915_WRITE(GEN8_OABUFFER, gtt_offset |<br>
> +                OABUFFER_SIZE_16M | OA_MEM_SELECT_GGTT);<br>
> +     I915_WRITE(GEN8_OATAILPTR, gtt_offset & GEN8_OATAILPTR_MASK);<br>
> +<br>
> +     /* Mark that we need updated tail pointers to read from... */<br>
> +     dev_priv->perf.oa.oa_buffer.<wbr>tails[0].offset = INVALID_TAIL_PTR;<br>
> +     dev_priv->perf.oa.oa_buffer.<wbr>tails[1].offset = INVALID_TAIL_PTR;<br>
> +<br>
> +     spin_unlock_irqrestore(&dev_<wbr>priv->perf.oa.oa_buffer.ptr_<wbr>lock, flags);<br>
> +<br>
> +<br>
> +     /* NB: although the OA buffer will initially be allocated<br>
> +      * zeroed via shmfs (and so this memset is redundant when<br>
> +      * first allocating), we may re-init the OA buffer, either<br>
> +      * when re-enabling a stream or in error/reset paths.<br>
> +      *<br>
> +      * The reason we clear the buffer for each re-init is for the<br>
> +      * sanity check in gen8_append_oa_reports() that looks at the<br>
> +      * reason field to make sure it's non-zero which relies on<br>
> +      * the assumption that new reports are being written to zeroed<br>
> +      * memory...<br>
> +      */<br>
> +     memset(dev_priv->perf.oa.oa_<wbr>buffer.vaddr, 0, OA_BUFFER_SIZE);<br>
> +<br>
> +     /* Maybe make ->pollin per-stream state if we support multiple<br>
> +      * concurrent streams in the future.<br>
> +      */<br>
> +     dev_priv->perf.oa.pollin = false;<br>
> +}<br>
> +<br>
>  static int alloc_oa_buffer(struct drm_i915_private *dev_priv)<br>
>  {<br>
>       struct drm_i915_gem_object *bo;<br>
> @@ -1108,6 +1581,200 @@ static void hsw_disable_metric_set(struct drm_i915_private *dev_priv)<br>
>                                     ~GT_NOA_ENABLE));<br>
>  }<br>
><br>
> +/*<br>
> + * From Broadwell PRM, 3D-Media-GPGPU -> Register State Context<br>
> + *<br>
> + * MMIO reads or writes to any of the registers listed in the<br>
> + * “Register State Context image” subsections through HOST/IA<br>
> + * MMIO interface for debug purposes must follow the steps below:<br>
> + *<br>
> + * - SW should set the Force Wakeup bit to prevent GT from entering C6.<br>
> + * - Write 0x2050[31:0] = 0x00010001 (disable sequence).<br>
> + * - Disable IDLE messaging in CS (Write 0x2050[31:0] = 0x00010001).<br>
> + * - BDW:  Poll/Wait for register bits of 0x22AC[6:0] turn to 0x30 value.<br>
> + * - SKL+: Poll/Wait for register bits of 0x22A4[6:0] turn to 0x30 value.<br>
> + * - Read/Write to desired MMIO registers.<br>
> + * - Enable IDLE messaging in CS (Write 0x2050[31:0] = 0x00010000).<br>
> + * - Force Wakeup bit should be reset to enable C6 entry.<br>
> + *<br>
> + * XXX: don't nest or overlap calls to this API, it has no ref<br>
> + * counting to track how many entities require the RCS to be<br>
> + * blocked from being idle.<br>
> + */<br>
> +static int gen8_begin_ctx_mmio(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     i915_reg_t fsm_reg = dev_priv->info.gen > 8 ?<br>
> +             GEN9_RCS_FE_FSM2 : GEN6_RCS_PWR_FSM;<br>
> +     int ret = 0;<br>
> +<br>
> +     /* There's only no active context while idle in execlist mode<br>
> +      * (though we shouldn't be using this in any other case)<br>
> +      */<br>
> +     if (WARN_ON(!i915.enable_<wbr>execlists))<br>
> +             return ret;<br>
</div></div>return -EINVAL ?<br></blockquote><div><br></div><div>Ah yup.<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
> +     intel_uncore_forcewake_get(<wbr>dev_priv, FORCEWAKE_RENDER);<br>
> +<br>
> +     I915_WRITE(GEN6_RC_SLEEP_PSMI_<wbr>CONTROL,<br>
> +                _MASKED_BIT_ENABLE(GEN6_PSMI_<wbr>SLEEP_MSG_DISABLE));<br>
> +<br>
> +     /* Note: we don't currently have a good handle on the maximum<br>
> +      * latency for this wake up so while we only need to hold rcs<br>
> +      * busy from process context we at least keep the waiting<br>
> +      * interruptible...<br>
> +      */<br>
> +     ret = wait_for((I915_READ(fsm_reg) & 0x3f) == 0x30, 50);<br>
> +     if (ret)<br>
> +         intel_uncore_forcewake_put(<wbr>dev_priv, FORCEWAKE_RENDER);<br>
> +<br>
> +     return ret;<br>
> +}<br>
> +<br>
> +static void gen8_end_ctx_mmio(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     if (WARN_ON(!i915.enable_<wbr>execlists))<br>
> +             return;<br>
> +<br>
> +     I915_WRITE(GEN6_RC_SLEEP_PSMI_<wbr>CONTROL,<br>
> +                _MASKED_BIT_DISABLE(GEN6_PSMI_<wbr>SLEEP_MSG_DISABLE));<br>
> +     intel_uncore_forcewake_put(<wbr>dev_priv, FORCEWAKE_RENDER);<br>
> +}<br>
> +<br>
> +/* Manages updating the per-context aspects of the OA stream<br>
> + * configuration across all contexts.<br>
> + *<br>
> + * The awkward consideration here is that OACTXCONTROL controls the<br>
> + * exponent for periodic sampling which is primarily used for system<br>
> + * wide profiling where we'd like a consistent sampling period even in<br>
> + * the face of context switches.<br>
> + *<br>
> + * Our approach of updating the register state context (as opposed to<br>
> + * say using a workaround batch buffer) ensures that the hardware<br>
> + * won't automatically reload an out-of-date timer exponent even<br>
> + * transiently before a WA BB could be parsed.<br>
> + *<br>
> + * This function needs to:<br>
> + * - Ensure the currently running context's per-context OA state is<br>
> + *   updated<br>
> + * - Ensure that all existing contexts will have the correct per-context<br>
> + *   OA state if they are scheduled for use.<br>
> + * - Ensure any new contexts will be initialized with the correct<br>
> + *   per-context OA state.<br>
> + *<br>
> + * Note: it's only the RCS/Render context that has any OA state.<br>
> + */<br>
> +static int configure_all_contexts(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     struct i915_gem_context *ctx;<br>
> +     int ret;<br>
> +<br>
> +     ret = i915_mutex_lock_interruptible(<wbr>&dev_priv->drm);<br>
> +     if (ret)<br>
> +             return ret;<br>
> +<br>
> +     /* Since execlist submission may be happening asynchronously here then<br>
> +      * we first mark existing contexts dirty before we update the current<br>
> +      * context so if any switches happen in the middle we can expect<br>
> +      * that the act of scheduling will have itself ensured a consistent<br>
> +      * OA state update.<br>
> +      */<br>
> +     list_for_each_entry(ctx, &dev_priv->context_list, link) {<br>
> +             /* The actual update of the register state context will happen<br>
> +              * the next time this logical ring is submitted. (See<br>
> +              * i915_oa_update_reg_state() which hooks into<br>
> +              * execlists_update_context())<br>
> +              */<br>
> +             atomic_set(&ctx->engine[RCS].<wbr>oa_state_dirty, 1);<br>
> +     }<br>
> +<br>
> +     mutex_unlock(&dev_priv->drm.<wbr>struct_mutex);<br>
> +<br>
> +     /* Now update the current context.<br>
> +      *<br>
> +      * Note: Using MMIO to update per-context registers requires<br>
> +      * some extra care...<br>
> +      */<br>
> +     ret = gen8_begin_ctx_mmio(dev_priv);<br>
> +     if (ret) {<br>
> +             DRM_ERROR("Failed to bring RCS out of idle to update current ctx OA state");<br>
</div></div>Missing newline for DRM_ERROR.<br></blockquote><div><br></div><div>fixed<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +             return ret;<br>
> +     }<br>
> +<br>
> +     I915_WRITE(GEN8_OACTXCONTROL, ((dev_priv->perf.oa.period_<wbr>exponent <<<br>
> +                                     GEN8_OA_TIMER_PERIOD_SHIFT) |<br>
> +                                   (dev_priv->perf.oa.periodic ?<br>
> +                                    GEN8_OA_TIMER_ENABLE : 0) |<br>
> +                                   GEN8_OA_COUNTER_RESUME));<br>
> +<br>
> +     config_oa_regs(dev_priv, dev_priv->perf.oa.flex_regs,<br>
> +                     dev_priv->perf.oa.flex_regs_<wbr>len);<br>
> +<br>
> +     gen8_end_ctx_mmio(dev_priv);<br>
> +<br>
> +     return 0;<br>
> +}<br>
> +<br>
> +static int gen8_enable_metric_set(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     int ret = dev_priv->perf.oa.ops.select_<wbr>metric_set(dev_priv);<br>
> +<br>
> +     if (ret)<br>
> +             return ret;<br>
> +<br>
> +     /* We disable slice/unslice clock ratio change reports on SKL since<br>
> +      * they are too noisy. The HW generates a lot of redundant reports<br>
> +      * where the ratio hasn't really changed causing a lot of redundant<br>
> +      * work to processes and increasing the chances we'll hit buffer<br>
> +      * overruns.<br>
> +      *<br>
> +      * Although we don't currently use the 'disable overrun' OABUFFER<br>
> +      * feature it's worth noting that clock ratio reports have to be<br>
> +      * disabled before considering to use that feature since the HW doesn't<br>
> +      * correctly block these reports.<br>
> +      *<br>
> +      * Currently none of the high-level metrics we have depend on knowing<br>
> +      * this ratio to normalize.<br>
> +      *<br>
> +      * Note: This register is not power context saved and restored, but<br>
> +      * that's OK considering that we disable RC6 while the OA unit is<br>
> +      * enabled.<br>
> +      *<br>
> +      * The _INCLUDE_CLK_RATIO bit allows the slice/unslice frequency to<br>
> +      * be read back from automatically triggered reports, as part of the<br>
> +      * RPT_ID field.<br>
> +      */<br>
> +     if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv)) {<br>
> +             I915_WRITE(GEN8_OA_DEBUG,<br>
> +                        _MASKED_BIT_ENABLE(GEN9_OA_<wbr>DEBUG_DISABLE_CLK_RATIO_<wbr>REPORTS |<br>
> +                                           GEN9_OA_DEBUG_INCLUDE_CLK_<wbr>RATIO));<br>
> +     }<br>
> +<br>
> +     I915_WRITE(GDT_CHICKEN_BITS, 0xA0);<br>
> +     config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs,<br>
> +                    dev_priv->perf.oa.mux_regs_<wbr>len);<br>
> +     I915_WRITE(GDT_CHICKEN_BITS, 0x80);<br>
> +<br>
> +     /* It takes a fairly long time for a new MUX configuration to<br>
> +      * be be applied after these register writes. This delay<br>
> +      * duration is take from Haswell (derived empirically based on<br>
> +      * the render_basic config) but hopefully it covers the<br>
> +      * maximum configuration latency for Gen8+ too...<br>
> +      */<br>
> +     usleep_range(15000, 20000);<br>
> +<br>
> +     config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_<wbr>regs,<br>
> +                    dev_priv->perf.oa.b_counter_<wbr>regs_len);<br>
> +<br>
> +     configure_all_contexts(dev_<wbr>priv);<br>
</div></div>Check the return value here, and bubble up.<br></blockquote><div><br></div><div>Oops, right, fixed.<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +<br>
> +     return 0;<br>
> +}<br>
> +<br>
> +static void gen8_disable_metric_set(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     /* NOP */<br>
> +}<br>
> +<br>
>  static void gen7_update_oacontrol_locked(<wbr>struct drm_i915_private *dev_priv)<br>
>  {<br>
>       lockdep_assert_held(&dev_priv-<wbr>>perf.hook_lock);<br>
> @@ -1152,6 +1819,29 @@ static void gen7_oa_enable(struct drm_i915_private *dev_priv)<br>
>       spin_unlock_irqrestore(&dev_<wbr>priv->perf.hook_lock, flags);<br>
>  }<br>
><br>
> +static void gen8_oa_enable(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     u32 report_format = dev_priv->perf.oa.oa_buffer.<wbr>format;<br>
> +<br>
> +     /* Reset buf pointers so we don't forward reports from before now.<br>
> +      *<br>
> +      * Think carefully if considering trying to avoid this, since it<br>
> +      * also ensures status flags and the buffer itself are cleared<br>
> +      * in error paths, and we have checks for invalid reports based<br>
> +      * on the assumption that certain fields are written to zeroed<br>
> +      * memory which this helps maintains.<br>
> +      */<br>
> +     gen8_init_oa_buffer(dev_priv);<br>
> +<br>
> +     /* Note: we don't rely on the hardware to perform single context<br>
> +      * filtering and instead filter on the cpu based on the context-id<br>
> +      * field of reports<br>
> +      */<br>
> +     I915_WRITE(GEN8_OACONTROL, (report_format <<<br>
> +                                 GEN8_OA_REPORT_FORMAT_SHIFT) |<br>
> +                                GEN8_OA_COUNTER_ENABLE);<br>
> +}<br>
> +<br>
>  /**<br>
>   * i915_oa_stream_enable - handle `I915_PERF_IOCTL_ENABLE` for OA stream<br>
>   * @stream: An i915 perf stream opened for OA metrics<br>
> @@ -1178,6 +1868,11 @@ static void gen7_oa_disable(struct drm_i915_private *dev_priv)<br>
>       I915_WRITE(GEN7_OACONTROL, 0);<br>
>  }<br>
><br>
> +static void gen8_oa_disable(struct drm_i915_private *dev_priv)<br>
> +{<br>
> +     I915_WRITE(GEN8_OACONTROL, 0);<br>
> +}<br>
> +<br>
>  /**<br>
>   * i915_oa_stream_disable - handle `I915_PERF_IOCTL_DISABLE` for OA stream<br>
>   * @stream: An i915 perf stream opened for OA metrics<br>
> @@ -1336,6 +2031,88 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,<br>
>       return ret;<br>
>  }<br>
><br>
> +/* NB: It must always remain pointer safe to run this even if the OA unit<br>
> + * has been disabled.<br>
> + *<br>
> + * It's fine to put out-of-date values into these per-context registers<br>
> + * in the case that the OA unit has been disabled.<br>
> + */<br>
> +static void gen8_update_reg_state_<wbr>unlocked(struct intel_engine_cs *engine,<br>
> +                                        struct i915_gem_context *ctx,<br>
> +                                        uint32_t *reg_state)<br>
</div></div>u32 for consistency, elsewhere also.<br></blockquote><div><br></div><div>updated.<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span class=""><br>
> +{<br>
> +     struct drm_i915_private *dev_priv = ctx->i915;<br>
> +     const struct i915_oa_reg *flex_regs = dev_priv->perf.oa.flex_regs;<br>
> +     int n_flex_regs = dev_priv->perf.oa.flex_regs_<wbr>len;<br>
> +     int ctx_oactxctrl = dev_priv->perf.oa.ctx_<wbr>oactxctrl_off;<br>
> +     int ctx_flexeu0 = dev_priv->perf.oa.ctx_flexeu0_<wbr>off;<br>
</span>Can we also make these u32, for consistency.<br></blockquote><div><br></div><div>yup<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +     /* The MMIO offsets for Flex EU registers aren't contiguous */<br>
> +     u32 flex_mmio[] = {<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL0),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL1),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL2),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL3),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL4),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL5),<br>
> +             i915_mmio_reg_offset(EU_PERF_<wbr>CNTL6),<br>
> +     };<br>
> +     int i;<br>
> +<br>
> +     reg_state[ctx_oactxctrl] = i915_mmio_reg_offset(GEN8_<wbr>OACTXCONTROL);<br>
> +     reg_state[ctx_oactxctrl+1] = (dev_priv->perf.oa.period_<wbr>exponent <<<br>
> +                                   GEN8_OA_TIMER_PERIOD_SHIFT) |<br>
> +                                  (dev_priv->perf.oa.periodic ?<br>
> +                                   GEN8_OA_TIMER_ENABLE : 0) |<br>
> +                                  GEN8_OA_COUNTER_RESUME;<br>
> +<br>
> +     for (i = 0; i < ARRAY_SIZE(flex_mmio); i++) {<br>
> +             uint32_t state_offset = ctx_flexeu0 + i * 2;<br>
> +             uint32_t mmio = flex_mmio[i];<br>
> +<br>
> +             /* This arbitrary default will select the 'EU FPU0 Pipeline<br>
> +              * Active' event. In the future it's anticipated that there<br>
> +              * will be an explicit 'No Event' we can select, but not yet...<br>
> +              */<br>
> +             uint32_t value = 0;<br>
> +             int j;<br>
> +<br>
> +             for (j = 0; j < n_flex_regs; j++) {<br>
> +                     if (i915_mmio_reg_offset(flex_<wbr>regs[j].addr) == mmio) {<br>
> +                             value = flex_regs[j].value;<br>
> +                             break;<br>
> +                     }<br>
> +             }<br>
> +<br>
> +             reg_state[state_offset] = mmio;<br>
> +             reg_state[state_offset+1] = value;<br>
> +     }<br>
> +}<br>
> +<br>
> +void i915_oa_update_reg_state(<wbr>struct intel_engine_cs *engine,<br>
> +                           struct i915_gem_context *ctx,<br>
> +                           uint32_t *reg_state)<br>
> +{<br>
> +     struct drm_i915_private *dev_priv = engine->i915;<br>
> +<br>
> +     if (engine->id != RCS)<br>
> +             return;<br>
> +<br>
> +     if (!dev_priv->perf.initialized)<br>
> +             return;<br>
> +<br>
> +     /* XXX: We don't take a lock here and this may run async with<br>
> +      * respect to stream methods. Notably we don't want to block<br>
> +      * context switches by long i915 perf read() operations.<br>
> +      *<br>
> +      * It's expected to always be safe to read the dev_priv->perf<br>
> +      * state needed here, and expected to be benign to redundantly<br>
> +      * update the state if the OA unit has been disabled since<br>
> +      * oa_state_dirty was last set.<br>
> +      */<br>
> +     if (atomic_cmpxchg(&ctx->engine[<wbr>engine->id].oa_state_dirty, 1, 0))<br>
> +             gen8_update_reg_state_<wbr>unlocked(engine, ctx, reg_state);<br>
> +}<br>
> +<br>
>  /**<br>
>   * i915_perf_read_locked - &i915_perf_stream_ops->read with error normalisation<br>
>   * @stream: An i915 perf stream<br>
> @@ -1750,6 +2527,7 @@ i915_perf_open_ioctl_locked(<wbr>struct drm_i915_private *dev_priv,<br>
>       struct i915_gem_context *specific_ctx = NULL;<br>
>       struct i915_perf_stream *stream = NULL;<br>
>       unsigned long f_flags = 0;<br>
> +     bool privileged_op = true;<br>
>       int stream_fd;<br>
>       int ret;<br>
><br>
> @@ -1767,12 +2545,28 @@ i915_perf_open_ioctl_locked(<wbr>struct drm_i915_private *dev_priv,<br>
>               }<br>
>       }<br>
><br>
> +     /* On Haswell the OA unit supports clock gating off for a specific<br>
> +      * context and in this mode there's no visibility of metrics for the<br>
> +      * rest of the system, which we consider acceptable for a<br>
> +      * non-privileged client.<br>
> +      *<br>
> +      * For Gen8+ the OA unit no longer supports clock gating off for a<br>
> +      * specific context and the kernel can't securely stop the counters<br>
> +      * from updating as system-wide / global values. Even though we can<br>
> +      * filter reports based on the included context ID we can't block<br>
> +      * clients from seeing the raw / global counter values via<br>
> +      * MI_REPORT_PERF_COUNT commands and so consider it a privileged op to<br>
> +      * enable the OA unit by default.<br>
> +      */<br>
> +     if (IS_HASWELL(dev_priv) && specific_ctx)<br>
> +             privileged_op = false;<br>
> +<br>
>       /* Similar to perf's kernel.perf_paranoid_cpu sysctl option<br>
>        * we check a dev.i915.perf_stream_paranoid sysctl option<br>
>        * to determine if it's ok to access system wide OA counters<br>
>        * without CAP_SYS_ADMIN privileges.<br>
>        */<br>
> -     if (!specific_ctx &&<br>
> +     if (privileged_op &&<br>
>           i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {<br>
>               DRM_DEBUG("Insufficient privileges to open system-wide i915 perf stream\n");<br>
>               ret = -EACCES;<br>
> @@ -2039,9 +2833,6 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data,<br>
>   */<br>
>  void i915_perf_register(struct drm_i915_private *dev_priv)<br>
>  {<br>
> -     if (!IS_HASWELL(dev_priv))<br>
> -             return;<br>
> -<br>
>       if (!dev_priv->perf.initialized)<br>
>               return;<br>
><br>
> @@ -2057,11 +2848,38 @@ void i915_perf_register(struct drm_i915_private *dev_priv)<br>
>       if (!dev_priv->perf.metrics_kobj)<br>
>               goto exit;<br>
><br>
> -     if (i915_perf_register_sysfs_hsw(<wbr>dev_priv)) {<br>
> -             kobject_put(dev_priv->perf.<wbr>metrics_kobj);<br>
> -             dev_priv->perf.metrics_kobj = NULL;<br>
> +     if (IS_HASWELL(dev_priv)) {<br>
> +             if (i915_perf_register_sysfs_hsw(<wbr>dev_priv))<br>
> +                     goto sysfs_error;<br>
> +     } else if (IS_BROADWELL(dev_priv)) {<br>
> +             if (i915_perf_register_sysfs_bdw(<wbr>dev_priv))<br>
> +                     goto sysfs_error;<br>
> +     } else if (IS_CHERRYVIEW(dev_priv)) {<br>
> +             if (i915_perf_register_sysfs_chv(<wbr>dev_priv))<br>
> +                     goto sysfs_error;<br>
> +     } else if (IS_SKYLAKE(dev_priv)) {<br>
> +             if (IS_SKL_GT2(dev_priv)) {<br>
> +                     if (i915_perf_register_sysfs_<wbr>sklgt2(dev_priv))<br>
> +                             goto sysfs_error;<br>
> +             } else if (IS_SKL_GT3(dev_priv)) {<br>
> +                     if (i915_perf_register_sysfs_<wbr>sklgt3(dev_priv))<br>
> +                             goto sysfs_error;<br>
> +             } else if (IS_SKL_GT4(dev_priv)) {<br>
> +                     if (i915_perf_register_sysfs_<wbr>sklgt4(dev_priv))<br>
> +                             goto sysfs_error;<br>
> +             } else<br>
> +                     goto sysfs_error;<br>
> +     } else if (IS_BROXTON(dev_priv)) {<br>
> +             if (i915_perf_register_sysfs_bxt(<wbr>dev_priv))<br>
> +                     goto sysfs_error;<br>
>       }<br>
><br>
> +     goto exit;<br>
> +<br>
> +sysfs_error:<br>
> +     kobject_put(dev_priv->perf.<wbr>metrics_kobj);<br>
> +     dev_priv->perf.metrics_kobj = NULL;<br>
> +<br>
>  exit:<br>
>       mutex_unlock(&dev_priv->perf.<wbr>lock);<br>
>  }<br>
> @@ -2077,13 +2895,24 @@ void i915_perf_register(struct drm_i915_private *dev_priv)<br>
>   */<br>
>  void i915_perf_unregister(struct drm_i915_private *dev_priv)<br>
>  {<br>
> -     if (!IS_HASWELL(dev_priv))<br>
> -             return;<br>
> -<br>
>       if (!dev_priv->perf.metrics_kobj)<br>
>               return;<br>
><br>
> -     i915_perf_unregister_sysfs_<wbr>hsw(dev_priv);<br>
> +        if (IS_HASWELL(dev_priv))<br>
> +                i915_perf_unregister_sysfs_<wbr>hsw(dev_priv);<br>
> +        else if (IS_BROADWELL(dev_priv))<br>
> +                i915_perf_unregister_sysfs_<wbr>bdw(dev_priv);<br>
> +        else if (IS_CHERRYVIEW(dev_priv))<br>
> +                i915_perf_unregister_sysfs_<wbr>chv(dev_priv);<br>
> +        else if (IS_SKYLAKE(dev_priv)) {<br>
> +             if (IS_SKL_GT2(dev_priv))<br>
> +                     i915_perf_unregister_sysfs_<wbr>sklgt2(dev_priv);<br>
> +             else if (IS_SKL_GT3(dev_priv))<br>
> +                     i915_perf_unregister_sysfs_<wbr>sklgt3(dev_priv);<br>
> +             else if (IS_SKL_GT4(dev_priv))<br>
> +                     i915_perf_unregister_sysfs_<wbr>sklgt4(dev_priv);<br>
> +     } else if (IS_BROXTON(dev_priv))<br>
> +                i915_perf_unregister_sysfs_<wbr>bxt(dev_priv);<br>
><br>
>       kobject_put(dev_priv->perf.<wbr>metrics_kobj);<br>
>       dev_priv->perf.metrics_kobj = NULL;<br>
> @@ -2142,45 +2971,107 @@ static struct ctl_table dev_root[] = {<br>
>   */<br>
>  void i915_perf_init(struct drm_i915_private *dev_priv)<br>
>  {<br>
> -     if (!IS_HASWELL(dev_priv))<br>
> -             return;<br>
> -<br>
> -     /* Using the same limiting factors as printk_ratelimit() */<br>
> -     ratelimit_state_init(&dev_<wbr>priv->perf.oa.spurious_report_<wbr>rs,<br>
> -                     5 * HZ, 10);<br>
> -     /* We use a DRM_NOTE for spurious reports so it would be<br>
> -      * inconsistent to print a warning for throttling.<br>
> -      */<br>
> -     ratelimit_set_flags(&dev_priv-<wbr>>perf.oa.spurious_report_rs,<br>
> -                     RATELIMIT_MSG_ON_RELEASE);<br>
> -<br>
> -     hrtimer_init(&dev_priv->perf.<wbr>oa.poll_check_timer,<br>
> -                  CLOCK_MONOTONIC, HRTIMER_MODE_REL);<br>
> -     dev_priv->perf.oa.poll_check_<wbr>timer.function = oa_poll_check_timer_cb;<br>
> -     init_waitqueue_head(&dev_priv-<wbr>>perf.oa.poll_wq);<br>
> +     dev_priv->perf.oa.n_builtin_<wbr>sets = 0;<br>
> +<br>
> +     if (IS_HASWELL(dev_priv)) {<br>
> +             dev_priv->perf.oa.ops.init_oa_<wbr>buffer = gen7_init_oa_buffer;<br>
> +             dev_priv->perf.oa.ops.enable_<wbr>metric_set = hsw_enable_metric_set;<br>
> +             dev_priv->perf.oa.ops.disable_<wbr>metric_set = hsw_disable_metric_set;<br>
> +             dev_priv->perf.oa.ops.oa_<wbr>enable = gen7_oa_enable;<br>
> +             dev_priv->perf.oa.ops.oa_<wbr>disable = gen7_oa_disable;<br>
> +             dev_priv->perf.oa.ops.read = gen7_oa_read;<br>
> +             dev_priv->perf.oa.ops.oa_<wbr>buffer_check =<br>
> +                     gen7_oa_buffer_check_unlocked;<br>
> +<br>
> +             dev_priv->perf.oa.oa_formats = hsw_oa_formats;<br>
> +<br>
> +             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                     i915_oa_n_builtin_metric_sets_<wbr>hsw;<br>
> +     } else if (i915.enable_execlists) {<br>
</div></div>Maybe a comment for why we don't support legacy mode with gen8+, for the<br>
next reader.<br></blockquote><div><br></div><div>It pretty much just came down to simplicity I think. Earlier iterations of the driver did try and support both, but since BDW enables execlist mode by default now it avoided a bunch of complexity to ignore the case with BDW and legacy ringbuffer mode here. I've added a comment.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +             if (IS_GEN8(dev_priv)) {<br>
> +                     dev_priv->perf.oa.ctx_<wbr>oactxctrl_off = 0x120;<br>
> +                     dev_priv->perf.oa.ctx_flexeu0_<wbr>off = 0x2ce;<br>
> +                     dev_priv->perf.oa.gen8_valid_<wbr>ctx_bit = (1<<25);<br>
> +<br>
> +                     if (IS_BROADWELL(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>bdw;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_bdw;<br>
> +                     } else if (IS_CHERRYVIEW(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>chv;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_chv;<br>
> +                     }<br>
> +             } else if (IS_GEN9(dev_priv)) {<br>
> +                     dev_priv->perf.oa.ctx_<wbr>oactxctrl_off = 0x128;<br>
> +                     dev_priv->perf.oa.ctx_flexeu0_<wbr>off = 0x3de;<br>
> +                     dev_priv->perf.oa.gen8_valid_<wbr>ctx_bit = (1<<16);<br>
> +<br>
> +                     if (IS_SKL_GT2(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>sklgt2;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_<wbr>sklgt2;<br>
> +                     } else if (IS_SKL_GT3(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>sklgt3;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_<wbr>sklgt3;<br>
> +                     } else if (IS_SKL_GT4(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>sklgt4;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_<wbr>sklgt4;<br>
> +                     } else if (IS_BROXTON(dev_priv)) {<br>
> +                             dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> +                                     i915_oa_n_builtin_metric_sets_<wbr>bxt;<br>
> +                             dev_priv->perf.oa.ops.select_<wbr>metric_set =<br>
> +                                     i915_oa_select_metric_set_bxt;<br>
> +                     }<br>
> +             }<br>
><br>
> -     INIT_LIST_HEAD(&dev_priv-><wbr>perf.streams);<br>
> -     mutex_init(&dev_priv->perf.<wbr>lock);<br>
> -     spin_lock_init(&dev_priv-><wbr>perf.hook_lock);<br>
> -     spin_lock_init(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock);<br>
> +             if (dev_priv->perf.oa.n_builtin_<wbr>sets) {<br>
> +                     dev_priv->perf.oa.ops.init_oa_<wbr>buffer = gen8_init_oa_buffer;<br>
> +                     dev_priv->perf.oa.ops.enable_<wbr>metric_set =<br>
> +                             gen8_enable_metric_set;<br>
> +                     dev_priv->perf.oa.ops.disable_<wbr>metric_set =<br>
> +                             gen8_disable_metric_set;<br>
> +                     dev_priv->perf.oa.ops.oa_<wbr>enable = gen8_oa_enable;<br>
> +                     dev_priv->perf.oa.ops.oa_<wbr>disable = gen8_oa_disable;<br>
> +                     dev_priv->perf.oa.ops.read = gen8_oa_read;<br>
> +                     dev_priv->perf.oa.ops.oa_<wbr>buffer_check =<br>
> +                             gen8_oa_buffer_check_unlocked;<br>
> +<br>
> +                     dev_priv->perf.oa.oa_formats = gen8_plus_oa_formats;<br>
> +             }<br>
> +     }<br>
><br>
> -     dev_priv->perf.oa.ops.init_oa_<wbr>buffer = gen7_init_oa_buffer;<br>
> -     dev_priv->perf.oa.ops.enable_<wbr>metric_set = hsw_enable_metric_set;<br>
> -     dev_priv->perf.oa.ops.disable_<wbr>metric_set = hsw_disable_metric_set;<br>
> -     dev_priv->perf.oa.ops.oa_<wbr>enable = gen7_oa_enable;<br>
> -     dev_priv->perf.oa.ops.oa_<wbr>disable = gen7_oa_disable;<br>
> -     dev_priv->perf.oa.ops.read = gen7_oa_read;<br>
> -     dev_priv->perf.oa.ops.oa_<wbr>buffer_check =<br>
> -             gen7_oa_buffer_check_unlocked;<br>
> +     if (dev_priv->perf.oa.n_builtin_<wbr>sets) {<br>
> +             /* Using the same limiting factors as printk_ratelimit() */<br>
> +             ratelimit_state_init(&dev_<wbr>priv->perf.oa.spurious_report_<wbr>rs,<br>
> +                             5 * HZ, 10);<br>
> +             /* We use a DRM_NOTE for spurious reports so it would be<br>
> +              * inconsistent to print a warning for throttling.<br>
> +              */<br>
> +             ratelimit_set_flags(&dev_priv-<wbr>>perf.oa.spurious_report_rs,<br>
> +                             RATELIMIT_MSG_ON_RELEASE);<br>
><br>
> -     dev_priv->perf.oa.oa_formats = hsw_oa_formats;<br>
> +             hrtimer_init(&dev_priv->perf.<wbr>oa.poll_check_timer,<br>
> +                             CLOCK_MONOTONIC, HRTIMER_MODE_REL);<br>
> +             dev_priv->perf.oa.poll_check_<wbr>timer.function = oa_poll_check_timer_cb;<br>
> +             init_waitqueue_head(&dev_priv-<wbr>>perf.oa.poll_wq);<br>
><br>
> -     dev_priv->perf.oa.n_builtin_<wbr>sets =<br>
> -             i915_oa_n_builtin_metric_sets_<wbr>hsw;<br>
> +             INIT_LIST_HEAD(&dev_priv-><wbr>perf.streams);<br>
> +             mutex_init(&dev_priv->perf.<wbr>lock);<br>
> +             spin_lock_init(&dev_priv-><wbr>perf.hook_lock);<br>
</div></div>Not strictly related to this patch, but why do we still need perf.hook_lock?<br></blockquote><div><br></div><div>Right gen7_update_oacontrol_locked can be folded into gen7_oa_enable and the only usage left is actually redundant since gen7 no longer relies on a hook for notifications of context pinning.<br><br></div><div>I think in some earlier review for HSW I had been a little reluctant to push to remove this lock early since I was using it in the gen8+ patches at the time, but that's not the case anymore. The i915_oa_update_reg_state() hook is designed to be safe to run without any i915-perf specific locking.<br><br></div><div>I'll aim to address this in a follow up patch.<br><br></div><div>Thanks for the review, I'll send out an update in a bit.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
> +             spin_lock_init(&dev_priv-><wbr>perf.oa.oa_buffer.ptr_lock);<br>
><br>
> -     dev_priv->perf.sysctl_header = register_sysctl_table(dev_<wbr>root);<br>
> +             dev_priv->perf.sysctl_header = register_sysctl_table(dev_<wbr>root);<br>
><br>
> -     dev_priv->perf.initialized = true;<br>
> +             dev_priv->perf.initialized = true;<br>
> +     }<br>
>  }<br>
><br>
>  /**<br>
> @@ -2200,5 +3091,6 @@ void i915_perf_fini(struct drm_i915_private *dev_priv)<br>
>       unregister_sysctl_table(dev_<wbr>priv->perf.sysctl_header);<br>
><br>
>       memset(&dev_priv->perf.oa.ops, 0, sizeof(dev_priv->perf.oa.ops))<wbr>;<br>
> +<br>
>       dev_priv->perf.initialized = false;<br>
>  }<br>
> diff --git a/drivers/gpu/drm/i915/i915_<wbr>reg.h b/drivers/gpu/drm/i915/i915_<wbr>reg.h<br>
> index 04c8f69fcc62..0052289ed8ad 100644<br>
> --- a/drivers/gpu/drm/i915/i915_<wbr>reg.h<br>
> +++ b/drivers/gpu/drm/i915/i915_<wbr>reg.h<br>
> @@ -645,6 +645,12 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)<br>
><br>
>  #define GEN8_OACTXID _MMIO(0x2364)<br>
><br>
> +#define GEN8_OA_DEBUG _MMIO(0x2B04)<br>
> +#define  GEN9_OA_DEBUG_DISABLE_CLK_<wbr>RATIO_REPORTS    (1<<5)<br>
> +#define  GEN9_OA_DEBUG_INCLUDE_CLK_<wbr>RATIO         (1<<6)<br>
> +#define  GEN9_OA_DEBUG_DISABLE_GO_1_0_<wbr>REPORTS            (1<<2)<br>
> +#define  GEN9_OA_DEBUG_DISABLE_CTX_<wbr>SWITCH_REPORTS   (1<<1)<br>
> +<br>
>  #define GEN8_OACONTROL _MMIO(0x2B00)<br>
>  #define  GEN8_OA_REPORT_FORMAT_A12       (0<<2)<br>
>  #define  GEN8_OA_REPORT_FORMAT_A12_B8_<wbr>C8    (2<<2)<br>
> @@ -666,6 +672,7 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)<br>
>  #define  GEN7_OABUFFER_STOP_RESUME_<wbr>ENABLE   (1<<1)<br>
>  #define  GEN7_OABUFFER_RESUME                    (1<<0)<br>
><br>
> +#define GEN8_OABUFFER_UDW _MMIO(0x23b4)<br>
>  #define GEN8_OABUFFER _MMIO(0x2b14)<br>
><br>
>  #define GEN7_OASTATUS1 _MMIO(0x2364)<br>
> @@ -684,7 +691,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)<br>
>  #define  GEN8_OASTATUS_REPORT_LOST       (1<<0)<br>
><br>
>  #define GEN8_OAHEADPTR _MMIO(0x2B0C)<br>
> +#define GEN8_OAHEADPTR_MASK    0xffffffc0<br>
>  #define GEN8_OATAILPTR _MMIO(0x2B10)<br>
> +#define GEN8_OATAILPTR_MASK    0xffffffc0<br>
><br>
>  #define OABUFFER_SIZE_128K  (0<<3)<br>
>  #define OABUFFER_SIZE_256K  (1<<3)<br>
> @@ -697,7 +706,17 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)<br>
><br>
>  #define OA_MEM_SELECT_GGTT  (1<<0)<br>
><br>
> +/*<br>
> + * Flexible, Aggregate EU Counter Registers.<br>
> + * Note: these aren't contiguous<br>
> + */<br>
>  #define EU_PERF_CNTL0            _MMIO(0xe458)<br>
> +#define EU_PERF_CNTL1            _MMIO(0xe558)<br>
> +#define EU_PERF_CNTL2            _MMIO(0xe658)<br>
> +#define EU_PERF_CNTL3            _MMIO(0xe758)<br>
> +#define EU_PERF_CNTL4            _MMIO(0xe45c)<br>
> +#define EU_PERF_CNTL5            _MMIO(0xe55c)<br>
> +#define EU_PERF_CNTL6            _MMIO(0xe65c)<br>
><br>
>  #define GDT_CHICKEN_BITS    _MMIO(0x9840)<br>
>  #define GT_NOA_ENABLE            0x00000080<br>
> @@ -2317,6 +2336,9 @@ enum skl_disp_power_wells {<br>
>  #define   GEN8_RC_SEMA_IDLE_MSG_DISABLE      (1 << 12)<br>
>  #define   GEN8_FF_DOP_CLOCK_GATE_DISABLE     (1<<10)<br>
><br>
> +#define GEN6_RCS_PWR_FSM _MMIO(0x22ac)<br>
> +#define GEN9_RCS_FE_FSM2 _MMIO(0x22a4)<br>
> +<br>
>  /* Fuse readout registers for GT */<br>
>  #define CHV_FUSE_GT                  _MMIO(VLV_DISPLAY_BASE + 0x2168)<br>
>  #define   CHV_FGT_DISABLE_SS0                (1 << 10)<br>
> diff --git a/drivers/gpu/drm/i915/intel_<wbr>lrc.c b/drivers/gpu/drm/i915/intel_<wbr>lrc.c<br>
> index eec1e714f531..e6d9e4197d3d 100644<br>
> --- a/drivers/gpu/drm/i915/intel_<wbr>lrc.c<br>
> +++ b/drivers/gpu/drm/i915/intel_<wbr>lrc.c<br>
> @@ -337,6 +337,8 @@ static u64 execlists_update_context(<wbr>struct drm_i915_gem_request *rq)<br>
>       if (ppgtt && !i915_vm_is_48bit(&ppgtt-><wbr>base))<br>
>               execlists_update_context_pdps(<wbr>ppgtt, reg_state);<br>
><br>
> +     i915_oa_update_reg_state(rq-><wbr>engine, rq->ctx, reg_state);<br>
> +<br>
>       return ce->lrc_desc;<br>
>  }<br>
><br>
> @@ -1878,6 +1880,9 @@ static void execlists_init_reg_state(u32 *regs,<br>
>               regs[CTX_LRI_HEADER_2] = MI_LOAD_REGISTER_IMM(1);<br>
>               CTX_REG(regs, CTX_R_PWR_CLK_STATE, GEN8_R_PWR_CLK_STATE,<br>
>                       make_rpcs(dev_priv));<br>
> +<br>
> +             atomic_set(&ctx->engine[RCS].<wbr>oa_state_dirty, 1);<br>
> +             i915_oa_update_reg_state(<wbr>engine, ctx, regs);<br>
>       }<br>
>  }<br>
><br>
> diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h<br>
> index e0599e729e68..03b833849919 100644<br>
> --- a/include/uapi/drm/i915_drm.h<br>
> +++ b/include/uapi/drm/i915_drm.h<br>
> @@ -1295,13 +1295,18 @@ struct drm_i915_gem_context_param {<br>
>  };<br>
><br>
>  enum drm_i915_oa_format {<br>
> -     I915_OA_FORMAT_A13 = 1,<br>
> -     I915_OA_FORMAT_A29,<br>
> -     I915_OA_FORMAT_A13_B8_C8,<br>
> -     I915_OA_FORMAT_B4_C8,<br>
> -     I915_OA_FORMAT_A45_B8_C8,<br>
> -     I915_OA_FORMAT_B4_C8_A16,<br>
> -     I915_OA_FORMAT_C4_B8,<br>
> +     I915_OA_FORMAT_A13 = 1,     /* HSW only */<br>
> +     I915_OA_FORMAT_A29,         /* HSW only */<br>
> +     I915_OA_FORMAT_A13_B8_C8,   /* HSW only */<br>
> +     I915_OA_FORMAT_B4_C8,       /* HSW only */<br>
> +     I915_OA_FORMAT_A45_B8_C8,   /* HSW only */<br>
> +     I915_OA_FORMAT_B4_C8_A16,   /* HSW only */<br>
> +     I915_OA_FORMAT_C4_B8,       /* HSW+ */<br>
> +<br>
> +     /* Gen8+ */<br>
> +     I915_OA_FORMAT_A12,<br>
> +     I915_OA_FORMAT_A12_B8_C8,<br>
> +     I915_OA_FORMAT_A32u40_A4u32_<wbr>B8_C8,<br>
><br>
>       I915_OA_FORMAT_MAX          /* non-ABI */<br>
>  };<br>
> --<br>
> 2.12.0<br>
><br>
</div></div>> ______________________________<wbr>_________________<br>
> Intel-gfx mailing list<br>
> <a href="mailto:Intel-gfx@lists.freedesktop.org">Intel-gfx@lists.freedesktop.<wbr>org</a><br>
> <a href="https://lists.freedesktop.org/mailman/listinfo/intel-gfx" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/intel-gfx</a><br>
</blockquote></div><br></div></div>