]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - drivers/gpu/drm/i915/i915_perf.c
Merge tag 'drm-next-2020-06-02' of git://anongit.freedesktop.org/drm/drm
[thirdparty/linux.git] / drivers / gpu / drm / i915 / i915_perf.c
index cf2c01f17da837235dd8f79e1614cf896e814299..75c60c2afb7ea280d4fcbea3dcdaed96d73bb2c1 100644 (file)
 
 #include "i915_drv.h"
 #include "i915_perf.h"
-#include "oa/i915_oa_hsw.h"
-#include "oa/i915_oa_bdw.h"
-#include "oa/i915_oa_chv.h"
-#include "oa/i915_oa_sklgt2.h"
-#include "oa/i915_oa_sklgt3.h"
-#include "oa/i915_oa_sklgt4.h"
-#include "oa/i915_oa_bxt.h"
-#include "oa/i915_oa_kblgt2.h"
-#include "oa/i915_oa_kblgt3.h"
-#include "oa/i915_oa_glk.h"
-#include "oa/i915_oa_cflgt2.h"
-#include "oa/i915_oa_cflgt3.h"
-#include "oa/i915_oa_cnl.h"
-#include "oa/i915_oa_icl.h"
-#include "oa/i915_oa_tgl.h"
 
 /* HW requires this to be a power of two, between 128k and 16M, though driver
  * is currently generally designed assuming the largest 16M size is used such
  *
  * Although this can be observed explicitly while copying reports to userspace
  * by checking for a zeroed report-id field in tail reports, we want to account
- * for this earlier, as part of the oa_buffer_check to avoid lots of redundant
- * read() attempts.
- *
- * In effect we define a tail pointer for reading that lags the real tail
- * pointer by at least %OA_TAIL_MARGIN_NSEC nanoseconds, which gives enough
- * time for the corresponding reports to become visible to the CPU.
- *
- * To manage this we actually track two tail pointers:
- *  1) An 'aging' tail with an associated timestamp that is tracked until we
- *     can trust the corresponding data is visible to the CPU; at which point
- *     it is considered 'aged'.
- *  2) An 'aged' tail that can be used for read()ing.
- *
- * The two separate pointers let us decouple read()s from tail pointer aging.
- *
- * The tail pointers are checked and updated at a limited rate within a hrtimer
- * callback (the same callback that is used for delivering EPOLLIN events)
- *
- * Initially the tails are marked invalid with %INVALID_TAIL_PTR which
- * indicates that an updated tail pointer is needed.
+ * for this earlier, as part of the oa_buffer_check_unlocked to avoid lots of
+ * redundant read() attempts.
+ *
+ * We workaround this issue in oa_buffer_check_unlocked() by reading the reports
+ * in the OA buffer, starting from the tail reported by the HW until we find a
+ * report with its first 2 dwords not 0 meaning its previous report is
+ * completely in memory and ready to be read. Those dwords are also set to 0
+ * once read and the whole buffer is cleared upon OA buffer initialization. The
+ * first dword is the reason for this report while the second is the timestamp,
+ * making the chances of having those 2 fields at 0 fairly unlikely. A more
+ * detailed explanation is available in oa_buffer_check_unlocked().
  *
  * Most of the implementation details for this workaround are in
  * oa_buffer_check_unlocked() and _append_oa_reports()
 #define OA_TAIL_MARGIN_NSEC    100000ULL
 #define INVALID_TAIL_PTR       0xffffffff
 
-/* frequency for checking whether the OA unit has written new reports to the
- * circular OA buffer...
+/* The default 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)
+#define DEFAULT_POLL_FREQUENCY_HZ 200
+#define DEFAULT_POLL_PERIOD_NS (NSEC_PER_SEC / DEFAULT_POLL_FREQUENCY_HZ)
 
 /* for sysctl proc_dointvec_minmax of dev.i915.perf_stream_paranoid */
 static u32 i915_perf_stream_paranoid = true;
@@ -359,6 +335,12 @@ static const struct i915_oa_format gen12_oa_formats[I915_OA_FORMAT_MAX] = {
  * @oa_periodic: Whether to enable periodic OA unit sampling
  * @oa_period_exponent: The OA unit sampling period is derived from this
  * @engine: The engine (typically rcs0) being monitored by the OA unit
+ * @has_sseu: Whether @sseu was specified by userspace
+ * @sseu: internal SSEU configuration computed either from the userspace
+ *        specified configuration in the opening parameters or a default value
+ *        (see get_default_sseu_config())
+ * @poll_oa_period: The period in nanoseconds at which the CPU will check for OA
+ * data availability
  *
  * As read_properties_unlocked() enumerates and validates the properties given
  * to open a stream of metrics the configuration is built up in the structure
@@ -378,6 +360,11 @@ struct perf_open_properties {
        int oa_period_exponent;
 
        struct intel_engine_cs *engine;
+
+       bool has_sseu;
+       struct intel_sseu sseu;
+
+       u64 poll_oa_period;
 };
 
 struct i915_oa_config_bo {
@@ -409,10 +396,7 @@ i915_perf_get_oa_config(struct i915_perf *perf, int metrics_set)
        struct i915_oa_config *oa_config;
 
        rcu_read_lock();
-       if (metrics_set == 1)
-               oa_config = &perf->test_config;
-       else
-               oa_config = idr_find(&perf->metrics_idr, metrics_set);
+       oa_config = idr_find(&perf->metrics_idr, metrics_set);
        if (oa_config)
                oa_config = i915_oa_config_get(oa_config);
        rcu_read_unlock();
@@ -465,8 +449,8 @@ static u32 gen7_oa_hw_tail_read(struct i915_perf_stream *stream)
  * (See description of OA_TAIL_MARGIN_NSEC above for further details.)
  *
  * Besides returning true when there is data available to read() this function
- * also has the side effect of updating the oa_buffer.tails[], .aging_timestamp
- * and .aged_tail_idx state used for reading.
+ * also updates the tail, aging_tail and aging_timestamp in the oa_buffer
+ * object.
  *
  * Note: 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
@@ -476,28 +460,19 @@ static u32 gen7_oa_hw_tail_read(struct i915_perf_stream *stream)
  */
 static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream)
 {
+       u32 gtt_offset = i915_ggtt_offset(stream->oa_buffer.vma);
        int report_size = stream->oa_buffer.format_size;
        unsigned long flags;
-       unsigned int aged_idx;
-       u32 head, hw_tail, aged_tail, aging_tail;
+       bool pollin;
+       u32 hw_tail;
        u64 now;
 
        /* We have to consider the (unlikely) possibility that read() errors
-        * could result in an OA buffer reset which might reset the head,
-        * tails[] and aged_tail state.
+        * could result in an OA buffer reset which might reset the head and
+        * tail state.
         */
        spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
 
-       /* NB: The head we observe here might effectively be a little out of
-        * date (between head and tails[aged_idx].offset if there is currently
-        * a read() in progress.
-        */
-       head = stream->oa_buffer.head;
-
-       aged_idx = stream->oa_buffer.aged_tail_idx;
-       aged_tail = stream->oa_buffer.tails[aged_idx].offset;
-       aging_tail = stream->oa_buffer.tails[!aged_idx].offset;
-
        hw_tail = stream->perf->ops.oa_hw_tail_read(stream);
 
        /* The tail pointer increases in 64 byte increments,
@@ -507,64 +482,63 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream)
 
        now = ktime_get_mono_fast_ns();
 
-       /* Update the aged tail
-        *
-        * Flip the tail pointer available for read()s once the aging tail is
-        * old enough to trust that the corresponding data will be visible to
-        * the CPU...
-        *
-        * Do this before updating the aging pointer in case we may be able to
-        * immediately start aging a new pointer too (if new data has become
-        * available) without needing to wait for a later hrtimer callback.
-        */
-       if (aging_tail != INVALID_TAIL_PTR &&
-           ((now - stream->oa_buffer.aging_timestamp) >
-            OA_TAIL_MARGIN_NSEC)) {
-
-               aged_idx ^= 1;
-               stream->oa_buffer.aged_tail_idx = aged_idx;
+       if (hw_tail == stream->oa_buffer.aging_tail &&
+           (now - stream->oa_buffer.aging_timestamp) > OA_TAIL_MARGIN_NSEC) {
+               /* If the HW tail hasn't move since the last check and the HW
+                * tail has been aging for long enough, declare it the new
+                * tail.
+                */
+               stream->oa_buffer.tail = stream->oa_buffer.aging_tail;
+       } else {
+               u32 head, tail, aged_tail;
 
-               aged_tail = aging_tail;
+               /* NB: The head we observe here might effectively be a little
+                * out of date. If a read() is in progress, the head could be
+                * anywhere between this head and stream->oa_buffer.tail.
+                */
+               head = stream->oa_buffer.head - gtt_offset;
+               aged_tail = stream->oa_buffer.tail - gtt_offset;
+
+               hw_tail -= gtt_offset;
+               tail = hw_tail;
+
+               /* Walk the stream backward until we find a report with dword 0
+                * & 1 not at 0. Since the circular buffer pointers progress by
+                * increments of 64 bytes and that reports can be up to 256
+                * bytes long, we can't tell whether a report has fully landed
+                * in memory before the first 2 dwords of the following report
+                * have effectively landed.
+                *
+                * This is assuming that the writes of the OA unit land in
+                * memory in the order they were written to.
+                * If not : (╯°□°)╯︵ ┻━┻
+                */
+               while (OA_TAKEN(tail, aged_tail) >= report_size) {
+                       u32 *report32 = (void *)(stream->oa_buffer.vaddr + tail);
 
-               /* Mark that we need a new pointer to start aging... */
-               stream->oa_buffer.tails[!aged_idx].offset = INVALID_TAIL_PTR;
-               aging_tail = INVALID_TAIL_PTR;
-       }
+                       if (report32[0] != 0 || report32[1] != 0)
+                               break;
 
-       /* Update the aging tail
-        *
-        * We throttle aging tail updates until we have a new tail that
-        * represents >= one report more data than is already available for
-        * reading. This ensures there will be enough data for a successful
-        * read once this new pointer has aged and ensures we will give the new
-        * pointer time to age.
-        */
-       if (aging_tail == INVALID_TAIL_PTR &&
-           (aged_tail == INVALID_TAIL_PTR ||
-            OA_TAKEN(hw_tail, aged_tail) >= report_size)) {
-               struct i915_vma *vma = stream->oa_buffer.vma;
-               u32 gtt_offset = i915_ggtt_offset(vma);
-
-               /* Be paranoid and do a bounds check on the pointer read back
-                * from hardware, just in case some spurious hardware condition
-                * could put the tail out of bounds...
-                */
-               if (hw_tail >= gtt_offset &&
-                   hw_tail < (gtt_offset + OA_BUFFER_SIZE)) {
-                       stream->oa_buffer.tails[!aged_idx].offset =
-                               aging_tail = hw_tail;
-                       stream->oa_buffer.aging_timestamp = now;
-               } else {
-                       drm_err(&stream->perf->i915->drm,
-                               "Ignoring spurious out of range OA buffer tail pointer = %x\n",
-                               hw_tail);
+                       tail = (tail - report_size) & (OA_BUFFER_SIZE - 1);
                }
+
+               if (OA_TAKEN(hw_tail, tail) > report_size &&
+                   __ratelimit(&stream->perf->tail_pointer_race))
+                       DRM_NOTE("unlanded report(s) head=0x%x "
+                                "tail=0x%x hw_tail=0x%x\n",
+                                head, tail, hw_tail);
+
+               stream->oa_buffer.tail = gtt_offset + tail;
+               stream->oa_buffer.aging_tail = gtt_offset + hw_tail;
+               stream->oa_buffer.aging_timestamp = now;
        }
 
+       pollin = OA_TAKEN(stream->oa_buffer.tail - gtt_offset,
+                         stream->oa_buffer.head - gtt_offset) >= report_size;
+
        spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
 
-       return aged_tail == INVALID_TAIL_PTR ?
-               false : OA_TAKEN(aged_tail, head) >= report_size;
+       return pollin;
 }
 
 /**
@@ -682,7 +656,6 @@ static int gen8_append_oa_reports(struct i915_perf_stream *stream,
        u32 mask = (OA_BUFFER_SIZE - 1);
        size_t start_offset = *offset;
        unsigned long flags;
-       unsigned int aged_tail_idx;
        u32 head, tail;
        u32 taken;
        int ret = 0;
@@ -693,18 +666,10 @@ static int gen8_append_oa_reports(struct i915_perf_stream *stream,
        spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
 
        head = stream->oa_buffer.head;
-       aged_tail_idx = stream->oa_buffer.aged_tail_idx;
-       tail = stream->oa_buffer.tails[aged_tail_idx].offset;
+       tail = stream->oa_buffer.tail;
 
        spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
 
-       /*
-        * An invalid tail pointer here means we're still waiting for the poll
-        * hrtimer callback to give us a pointer
-        */
-       if (tail == INVALID_TAIL_PTR)
-               return -EAGAIN;
-
        /*
         * NB: oa_buffer.head/tail include the gtt_offset which we don't want
         * while indexing relative to oa_buf_base.
@@ -838,13 +803,11 @@ static int gen8_append_oa_reports(struct i915_perf_stream *stream,
                }
 
                /*
-                * The above reason 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.
+                * Clear out the first 2 dword as a mean to detect unlanded
+                * reports.
                 */
                report32[0] = 0;
+               report32[1] = 0;
        }
 
        if (start_offset != *offset) {
@@ -985,7 +948,6 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
        u32 mask = (OA_BUFFER_SIZE - 1);
        size_t start_offset = *offset;
        unsigned long flags;
-       unsigned int aged_tail_idx;
        u32 head, tail;
        u32 taken;
        int ret = 0;
@@ -996,17 +958,10 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
        spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
 
        head = stream->oa_buffer.head;
-       aged_tail_idx = stream->oa_buffer.aged_tail_idx;
-       tail = stream->oa_buffer.tails[aged_tail_idx].offset;
+       tail = stream->oa_buffer.tail;
 
        spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
 
-       /* An invalid tail pointer here means we're still waiting for the poll
-        * hrtimer callback to give us a pointer
-        */
-       if (tail == INVALID_TAIL_PTR)
-               return -EAGAIN;
-
        /* NB: oa_buffer.head/tail include the gtt_offset which we don't want
         * while indexing relative to oa_buf_base.
         */
@@ -1064,13 +1019,11 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
                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.
+               /* Clear out the first 2 dwords as a mean to detect unlanded
+                * reports.
                 */
                report32[0] = 0;
+               report32[1] = 0;
        }
 
        if (start_offset != *offset) {
@@ -1447,8 +1400,8 @@ static void gen7_init_oa_buffer(struct i915_perf_stream *stream)
                           gtt_offset | OABUFFER_SIZE_16M);
 
        /* Mark that we need updated tail pointers to read from... */
-       stream->oa_buffer.tails[0].offset = INVALID_TAIL_PTR;
-       stream->oa_buffer.tails[1].offset = INVALID_TAIL_PTR;
+       stream->oa_buffer.aging_tail = INVALID_TAIL_PTR;
+       stream->oa_buffer.tail = gtt_offset;
 
        spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
 
@@ -1470,8 +1423,6 @@ static void gen7_init_oa_buffer(struct i915_perf_stream *stream)
         * memory...
         */
        memset(stream->oa_buffer.vaddr, 0, OA_BUFFER_SIZE);
-
-       stream->pollin = false;
 }
 
 static void gen8_init_oa_buffer(struct i915_perf_stream *stream)
@@ -1501,8 +1452,8 @@ static void gen8_init_oa_buffer(struct i915_perf_stream *stream)
        intel_uncore_write(uncore, GEN8_OATAILPTR, gtt_offset & GEN8_OATAILPTR_MASK);
 
        /* Mark that we need updated tail pointers to read from... */
-       stream->oa_buffer.tails[0].offset = INVALID_TAIL_PTR;
-       stream->oa_buffer.tails[1].offset = INVALID_TAIL_PTR;
+       stream->oa_buffer.aging_tail = INVALID_TAIL_PTR;
+       stream->oa_buffer.tail = gtt_offset;
 
        /*
         * Reset state used to recognise context switches, affecting which
@@ -1526,8 +1477,6 @@ static void gen8_init_oa_buffer(struct i915_perf_stream *stream)
         * memory...
         */
        memset(stream->oa_buffer.vaddr, 0, OA_BUFFER_SIZE);
-
-       stream->pollin = false;
 }
 
 static void gen12_init_oa_buffer(struct i915_perf_stream *stream)
@@ -1557,8 +1506,8 @@ static void gen12_init_oa_buffer(struct i915_perf_stream *stream)
                           gtt_offset & GEN12_OAG_OATAILPTR_MASK);
 
        /* Mark that we need updated tail pointers to read from... */
-       stream->oa_buffer.tails[0].offset = INVALID_TAIL_PTR;
-       stream->oa_buffer.tails[1].offset = INVALID_TAIL_PTR;
+       stream->oa_buffer.aging_tail = INVALID_TAIL_PTR;
+       stream->oa_buffer.tail = gtt_offset;
 
        /*
         * Reset state used to recognise context switches, affecting which
@@ -1583,8 +1532,6 @@ static void gen12_init_oa_buffer(struct i915_perf_stream *stream)
         */
        memset(stream->oa_buffer.vaddr, 0,
               stream->oa_buffer.vma->size);
-
-       stream->pollin = false;
 }
 
 static int alloc_oa_buffer(struct i915_perf_stream *stream)
@@ -1665,10 +1612,7 @@ static int alloc_noa_wait(struct i915_perf_stream *stream)
        struct drm_i915_gem_object *bo;
        struct i915_vma *vma;
        const u64 delay_ticks = 0xffffffffffffffff -
-               DIV64_U64_ROUND_UP(
-                       atomic64_read(&stream->perf->noa_programming_delay) *
-                       RUNTIME_INFO(i915)->cs_timestamp_frequency_khz,
-                       1000000ull);
+               i915_cs_timestamp_ns_to_ticks(i915, atomic64_read(&stream->perf->noa_programming_delay));
        const u32 base = stream->engine->mmio_base;
 #define CS_GPR(x) GEN8_RING_CS_GPR(base, x)
        u32 *batch, *ts0, *cs, *jump;
@@ -1970,10 +1914,11 @@ out:
        return i915_vma_get(oa_bo->vma);
 }
 
-static struct i915_request *
+static int
 emit_oa_config(struct i915_perf_stream *stream,
               struct i915_oa_config *oa_config,
-              struct intel_context *ce)
+              struct intel_context *ce,
+              struct i915_active *active)
 {
        struct i915_request *rq;
        struct i915_vma *vma;
@@ -1981,7 +1926,7 @@ emit_oa_config(struct i915_perf_stream *stream,
 
        vma = get_oa_vma(stream, oa_config);
        if (IS_ERR(vma))
-               return ERR_CAST(vma);
+               return PTR_ERR(vma);
 
        err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
        if (err)
@@ -1995,6 +1940,18 @@ emit_oa_config(struct i915_perf_stream *stream,
                goto err_vma_unpin;
        }
 
+       if (!IS_ERR_OR_NULL(active)) {
+               /* After all individual context modifications */
+               err = i915_request_await_active(rq, active,
+                                               I915_ACTIVE_AWAIT_ACTIVE);
+               if (err)
+                       goto err_add_request;
+
+               err = i915_active_add_request(active, rq);
+               if (err)
+                       goto err_add_request;
+       }
+
        i915_vma_lock(vma);
        err = i915_request_await_object(rq, vma->obj, 0);
        if (!err)
@@ -2009,14 +1966,13 @@ emit_oa_config(struct i915_perf_stream *stream,
        if (err)
                goto err_add_request;
 
-       i915_request_get(rq);
 err_add_request:
        i915_request_add(rq);
 err_vma_unpin:
        i915_vma_unpin(vma);
 err_vma_put:
        i915_vma_put(vma);
-       return err ? ERR_PTR(err) : rq;
+       return err;
 }
 
 static struct intel_context *oa_context(struct i915_perf_stream *stream)
@@ -2024,8 +1980,9 @@ static struct intel_context *oa_context(struct i915_perf_stream *stream)
        return stream->pinned_ctx ?: stream->engine->kernel_context;
 }
 
-static struct i915_request *
-hsw_enable_metric_set(struct i915_perf_stream *stream)
+static int
+hsw_enable_metric_set(struct i915_perf_stream *stream,
+                     struct i915_active *active)
 {
        struct intel_uncore *uncore = stream->uncore;
 
@@ -2044,7 +2001,9 @@ hsw_enable_metric_set(struct i915_perf_stream *stream)
        intel_uncore_rmw(uncore, GEN6_UCGCTL1,
                         0, GEN6_CSUNIT_CLOCK_GATE_DISABLE);
 
-       return emit_oa_config(stream, stream->oa_config, oa_context(stream));
+       return emit_oa_config(stream,
+                             stream->oa_config, oa_context(stream),
+                             active);
 }
 
 static void hsw_disable_metric_set(struct i915_perf_stream *stream)
@@ -2114,9 +2073,6 @@ gen8_update_reg_state_unlocked(const struct intel_context *ce,
        for (i = 0; i < ARRAY_SIZE(flex_regs); i++)
                reg_state[ctx_flexeu0 + i * 2 + 1] =
                        oa_config_flex_reg(stream->oa_config, flex_regs[i]);
-
-       reg_state[CTX_R_PWR_CLK_STATE] =
-               intel_sseu_make_rpcs(ce->engine->i915, &ce->sseu);
 }
 
 struct flex {
@@ -2137,7 +2093,7 @@ gen8_store_flex(struct i915_request *rq,
        if (IS_ERR(cs))
                return PTR_ERR(cs);
 
-       offset = i915_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE;
+       offset = i915_ggtt_offset(ce->state) + LRC_STATE_OFFSET;
        do {
                *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
                *cs++ = offset + flex->offset * sizeof(u32);
@@ -2194,8 +2150,10 @@ static int gen8_modify_context(struct intel_context *ce,
        return err;
 }
 
-static int gen8_modify_self(struct intel_context *ce,
-                           const struct flex *flex, unsigned int count)
+static int
+gen8_modify_self(struct intel_context *ce,
+                const struct flex *flex, unsigned int count,
+                struct i915_active *active)
 {
        struct i915_request *rq;
        int err;
@@ -2206,8 +2164,17 @@ static int gen8_modify_self(struct intel_context *ce,
        if (IS_ERR(rq))
                return PTR_ERR(rq);
 
+       if (!IS_ERR_OR_NULL(active)) {
+               err = i915_active_add_request(active, rq);
+               if (err)
+                       goto err_add_request;
+       }
+
        err = gen8_load_flex(rq, ce, flex, count);
+       if (err)
+               goto err_add_request;
 
+err_add_request:
        i915_request_add(rq);
        return err;
 }
@@ -2241,7 +2208,8 @@ static int gen8_configure_context(struct i915_gem_context *ctx,
        return err;
 }
 
-static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool enable)
+static int gen12_configure_oar_context(struct i915_perf_stream *stream,
+                                      struct i915_active *active)
 {
        int err;
        struct intel_context *ce = stream->pinned_ctx;
@@ -2250,7 +2218,7 @@ static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool ena
                {
                        GEN8_OACTXCONTROL,
                        stream->perf->ctx_oactxctrl_offset + 1,
-                       enable ? GEN8_OA_COUNTER_RESUME : 0,
+                       active ? GEN8_OA_COUNTER_RESUME : 0,
                },
        };
        /* Offsets in regs_lri are not used since this configuration is only
@@ -2262,13 +2230,13 @@ static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool ena
                        GEN12_OAR_OACONTROL,
                        GEN12_OAR_OACONTROL_OFFSET + 1,
                        (format << GEN12_OAR_OACONTROL_COUNTER_FORMAT_SHIFT) |
-                       (enable ? GEN12_OAR_OACONTROL_COUNTER_ENABLE : 0)
+                       (active ? GEN12_OAR_OACONTROL_COUNTER_ENABLE : 0)
                },
                {
                        RING_CONTEXT_CONTROL(ce->engine->mmio_base),
                        CTX_CONTEXT_CONTROL,
                        _MASKED_FIELD(GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE,
-                                     enable ?
+                                     active ?
                                      GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE :
                                      0)
                },
@@ -2285,7 +2253,7 @@ static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool ena
                return err;
 
        /* Apply regs_lri using LRI with pinned context */
-       return gen8_modify_self(ce, regs_lri, ARRAY_SIZE(regs_lri));
+       return gen8_modify_self(ce, regs_lri, ARRAY_SIZE(regs_lri), active);
 }
 
 /*
@@ -2313,9 +2281,11 @@ static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool ena
  * Note: it's only the RCS/Render context that has any OA state.
  * Note: the first flex register passed must always be R_PWR_CLK_STATE
  */
-static int oa_configure_all_contexts(struct i915_perf_stream *stream,
-                                    struct flex *regs,
-                                    size_t num_regs)
+static int
+oa_configure_all_contexts(struct i915_perf_stream *stream,
+                         struct flex *regs,
+                         size_t num_regs,
+                         struct i915_active *active)
 {
        struct drm_i915_private *i915 = stream->perf->i915;
        struct intel_engine_cs *engine;
@@ -2372,7 +2342,7 @@ static int oa_configure_all_contexts(struct i915_perf_stream *stream,
 
                regs[0].value = intel_sseu_make_rpcs(i915, &ce->sseu);
 
-               err = gen8_modify_self(ce, regs, num_regs);
+               err = gen8_modify_self(ce, regs, num_regs, active);
                if (err)
                        return err;
        }
@@ -2380,8 +2350,10 @@ static int oa_configure_all_contexts(struct i915_perf_stream *stream,
        return 0;
 }
 
-static int gen12_configure_all_contexts(struct i915_perf_stream *stream,
-                                       const struct i915_oa_config *oa_config)
+static int
+gen12_configure_all_contexts(struct i915_perf_stream *stream,
+                            const struct i915_oa_config *oa_config,
+                            struct i915_active *active)
 {
        struct flex regs[] = {
                {
@@ -2390,11 +2362,15 @@ static int gen12_configure_all_contexts(struct i915_perf_stream *stream,
                },
        };
 
-       return oa_configure_all_contexts(stream, regs, ARRAY_SIZE(regs));
+       return oa_configure_all_contexts(stream,
+                                        regs, ARRAY_SIZE(regs),
+                                        active);
 }
 
-static int lrc_configure_all_contexts(struct i915_perf_stream *stream,
-                                     const struct i915_oa_config *oa_config)
+static int
+lrc_configure_all_contexts(struct i915_perf_stream *stream,
+                          const struct i915_oa_config *oa_config,
+                          struct i915_active *active)
 {
        /* The MMIO offsets for Flex EU registers aren't contiguous */
        const u32 ctx_flexeu0 = stream->perf->ctx_flexeu0_offset;
@@ -2427,11 +2403,14 @@ static int lrc_configure_all_contexts(struct i915_perf_stream *stream,
        for (i = 2; i < ARRAY_SIZE(regs); i++)
                regs[i].value = oa_config_flex_reg(oa_config, regs[i].reg);
 
-       return oa_configure_all_contexts(stream, regs, ARRAY_SIZE(regs));
+       return oa_configure_all_contexts(stream,
+                                        regs, ARRAY_SIZE(regs),
+                                        active);
 }
 
-static struct i915_request *
-gen8_enable_metric_set(struct i915_perf_stream *stream)
+static int
+gen8_enable_metric_set(struct i915_perf_stream *stream,
+                      struct i915_active *active)
 {
        struct intel_uncore *uncore = stream->uncore;
        struct i915_oa_config *oa_config = stream->oa_config;
@@ -2471,11 +2450,13 @@ gen8_enable_metric_set(struct i915_perf_stream *stream)
         * to make sure all slices/subslices are ON before writing to NOA
         * registers.
         */
-       ret = lrc_configure_all_contexts(stream, oa_config);
+       ret = lrc_configure_all_contexts(stream, oa_config, active);
        if (ret)
-               return ERR_PTR(ret);
+               return ret;
 
-       return emit_oa_config(stream, oa_config, oa_context(stream));
+       return emit_oa_config(stream,
+                             stream->oa_config, oa_context(stream),
+                             active);
 }
 
 static u32 oag_report_ctx_switches(const struct i915_perf_stream *stream)
@@ -2485,8 +2466,9 @@ static u32 oag_report_ctx_switches(const struct i915_perf_stream *stream)
                             0 : GEN12_OAG_OA_DEBUG_DISABLE_CTX_SWITCH_REPORTS);
 }
 
-static struct i915_request *
-gen12_enable_metric_set(struct i915_perf_stream *stream)
+static int
+gen12_enable_metric_set(struct i915_perf_stream *stream,
+                       struct i915_active *active)
 {
        struct intel_uncore *uncore = stream->uncore;
        struct i915_oa_config *oa_config = stream->oa_config;
@@ -2515,9 +2497,9 @@ gen12_enable_metric_set(struct i915_perf_stream *stream)
         * to make sure all slices/subslices are ON before writing to NOA
         * registers.
         */
-       ret = gen12_configure_all_contexts(stream, oa_config);
+       ret = gen12_configure_all_contexts(stream, oa_config, active);
        if (ret)
-               return ERR_PTR(ret);
+               return ret;
 
        /*
         * For Gen12, performance counters are context
@@ -2525,12 +2507,14 @@ gen12_enable_metric_set(struct i915_perf_stream *stream)
         * requested this.
         */
        if (stream->ctx) {
-               ret = gen12_configure_oar_context(stream, true);
+               ret = gen12_configure_oar_context(stream, active);
                if (ret)
-                       return ERR_PTR(ret);
+                       return ret;
        }
 
-       return emit_oa_config(stream, oa_config, oa_context(stream));
+       return emit_oa_config(stream,
+                             stream->oa_config, oa_context(stream),
+                             active);
 }
 
 static void gen8_disable_metric_set(struct i915_perf_stream *stream)
@@ -2538,7 +2522,7 @@ static void gen8_disable_metric_set(struct i915_perf_stream *stream)
        struct intel_uncore *uncore = stream->uncore;
 
        /* Reset all contexts' slices/subslices configurations. */
-       lrc_configure_all_contexts(stream, NULL);
+       lrc_configure_all_contexts(stream, NULL, NULL);
 
        intel_uncore_rmw(uncore, GDT_CHICKEN_BITS, GT_NOA_ENABLE, 0);
 }
@@ -2548,7 +2532,7 @@ static void gen10_disable_metric_set(struct i915_perf_stream *stream)
        struct intel_uncore *uncore = stream->uncore;
 
        /* Reset all contexts' slices/subslices configurations. */
-       lrc_configure_all_contexts(stream, NULL);
+       lrc_configure_all_contexts(stream, NULL, NULL);
 
        /* Make sure we disable noa to save power. */
        intel_uncore_rmw(uncore, RPM_CONFIG1, GEN10_GT_NOA_ENABLE, 0);
@@ -2559,11 +2543,11 @@ static void gen12_disable_metric_set(struct i915_perf_stream *stream)
        struct intel_uncore *uncore = stream->uncore;
 
        /* Reset all contexts' slices/subslices configurations. */
-       gen12_configure_all_contexts(stream, NULL);
+       gen12_configure_all_contexts(stream, NULL, NULL);
 
        /* disable the context save/restore or OAR counters */
        if (stream->ctx)
-               gen12_configure_oar_context(stream, false);
+               gen12_configure_oar_context(stream, NULL);
 
        /* Make sure we disable noa to save power. */
        intel_uncore_rmw(uncore, RPM_CONFIG1, GEN10_GT_NOA_ENABLE, 0);
@@ -2655,11 +2639,13 @@ static void gen12_oa_enable(struct i915_perf_stream *stream)
  */
 static void i915_oa_stream_enable(struct i915_perf_stream *stream)
 {
+       stream->pollin = false;
+
        stream->perf->ops.oa_enable(stream);
 
        if (stream->periodic)
                hrtimer_start(&stream->poll_check_timer,
-                             ns_to_ktime(POLL_PERIOD),
+                             ns_to_ktime(stream->poll_oa_period),
                              HRTIMER_MODE_REL_PINNED);
 }
 
@@ -2735,16 +2721,52 @@ static const struct i915_perf_stream_ops i915_oa_stream_ops = {
 
 static int i915_perf_stream_enable_sync(struct i915_perf_stream *stream)
 {
-       struct i915_request *rq;
+       struct i915_active *active;
+       int err;
 
-       rq = stream->perf->ops.enable_metric_set(stream);
-       if (IS_ERR(rq))
-               return PTR_ERR(rq);
+       active = i915_active_create();
+       if (!active)
+               return -ENOMEM;
 
-       i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT);
-       i915_request_put(rq);
+       err = stream->perf->ops.enable_metric_set(stream, active);
+       if (err == 0)
+               __i915_active_wait(active, TASK_UNINTERRUPTIBLE);
 
-       return 0;
+       i915_active_put(active);
+       return err;
+}
+
+static void
+get_default_sseu_config(struct intel_sseu *out_sseu,
+                       struct intel_engine_cs *engine)
+{
+       const struct sseu_dev_info *devinfo_sseu =
+               &RUNTIME_INFO(engine->i915)->sseu;
+
+       *out_sseu = intel_sseu_from_device_info(devinfo_sseu);
+
+       if (IS_GEN(engine->i915, 11)) {
+               /*
+                * We only need subslice count so it doesn't matter which ones
+                * we select - just turn off low bits in the amount of half of
+                * all available subslices per slice.
+                */
+               out_sseu->subslice_mask =
+                       ~(~0 << (hweight8(out_sseu->subslice_mask) / 2));
+               out_sseu->slice_mask = 0x1;
+       }
+}
+
+static int
+get_sseu_config(struct intel_sseu *out_sseu,
+               struct intel_engine_cs *engine,
+               const struct drm_i915_gem_context_param_sseu *drm_sseu)
+{
+       if (drm_sseu->engine.engine_class != engine->uabi_class ||
+           drm_sseu->engine.engine_instance != engine->uabi_instance)
+               return -EINVAL;
+
+       return i915_gem_user_to_context_sseu(engine->i915, drm_sseu, out_sseu);
 }
 
 /**
@@ -2879,6 +2901,8 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
                goto err_oa_buf_alloc;
 
        stream->ops = &i915_oa_stream_ops;
+
+       perf->sseu = props->sseu;
        WRITE_ONCE(perf->exclusive_stream, stream);
 
        ret = i915_perf_stream_enable_sync(stream);
@@ -2930,10 +2954,6 @@ void i915_oa_init_reg_state(const struct intel_context *ce,
 
        /* perf.exclusive_stream serialised by lrc_configure_all_contexts() */
        stream = READ_ONCE(engine->i915->perf.exclusive_stream);
-       /*
-        * For gen12, only CTX_R_PWR_CLK_STATE needs update, but the caller
-        * is already doing that, so nothing to be done for gen12 here.
-        */
        if (stream && INTEL_GEN(stream->perf->i915) < 12)
                gen8_update_reg_state_unlocked(ce, stream);
 }
@@ -3024,7 +3044,8 @@ static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer)
                wake_up(&stream->poll_wq);
        }
 
-       hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD));
+       hrtimer_forward_now(hrtimer,
+                           ns_to_ktime(stream->poll_oa_period));
 
        return HRTIMER_RESTART;
 }
@@ -3155,7 +3176,7 @@ static long i915_perf_config_locked(struct i915_perf_stream *stream,
                return -EINVAL;
 
        if (config != stream->oa_config) {
-               struct i915_request *rq;
+               int err;
 
                /*
                 * If OA is bound to a specific context, emit the
@@ -3166,13 +3187,11 @@ static long i915_perf_config_locked(struct i915_perf_stream *stream,
                 * When set globally, we use a low priority kernel context,
                 * so it will effectively take effect when idle.
                 */
-               rq = emit_oa_config(stream, config, oa_context(stream));
-               if (!IS_ERR(rq)) {
+               err = emit_oa_config(stream, config, oa_context(stream), NULL);
+               if (!err)
                        config = xchg(&stream->oa_config, config);
-                       i915_request_put(rq);
-               } else {
-                       ret = PTR_ERR(rq);
-               }
+               else
+                       ret = err;
        }
 
        i915_oa_config_put(config);
@@ -3385,13 +3404,21 @@ i915_perf_open_ioctl_locked(struct i915_perf *perf,
                privileged_op = true;
        }
 
+       /*
+        * Asking for SSEU configuration is a priviliged operation.
+        */
+       if (props->has_sseu)
+               privileged_op = true;
+       else
+               get_default_sseu_config(&props->sseu, props->engine);
+
        /* Similar to perf's kernel.perf_paranoid_cpu sysctl option
         * we check a dev.i915.perf_stream_paranoid sysctl option
         * to determine if it's ok to access system wide OA counters
-        * without CAP_SYS_ADMIN privileges.
+        * without CAP_PERFMON or CAP_SYS_ADMIN privileges.
         */
        if (privileged_op &&
-           i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+           i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to open i915 perf stream\n");
                ret = -EACCES;
                goto err_ctx;
@@ -3405,6 +3432,7 @@ i915_perf_open_ioctl_locked(struct i915_perf *perf,
 
        stream->perf = perf;
        stream->ctx = specific_ctx;
+       stream->poll_oa_period = props->poll_oa_period;
 
        ret = i915_oa_stream_init(stream, param, props);
        if (ret)
@@ -3454,8 +3482,7 @@ err:
 
 static u64 oa_exponent_to_ns(struct i915_perf *perf, int exponent)
 {
-       return div64_u64(1000000000ULL * (2ULL << exponent),
-                        1000ULL * RUNTIME_INFO(perf->i915)->cs_timestamp_frequency_khz);
+       return i915_cs_timestamp_ticks_to_ns(perf->i915, 2ULL << exponent);
 }
 
 /**
@@ -3480,8 +3507,10 @@ static int read_properties_unlocked(struct i915_perf *perf,
 {
        u64 __user *uprop = uprops;
        u32 i;
+       int ret;
 
        memset(props, 0, sizeof(struct perf_open_properties));
+       props->poll_oa_period = DEFAULT_POLL_PERIOD_NS;
 
        if (!n_props) {
                DRM_DEBUG("No i915 perf properties given\n");
@@ -3511,7 +3540,6 @@ static int read_properties_unlocked(struct i915_perf *perf,
        for (i = 0; i < n_props; i++) {
                u64 oa_period, oa_freq_hz;
                u64 id, value;
-               int ret;
 
                ret = get_user(id, uprop);
                if (ret)
@@ -3584,9 +3612,8 @@ static int read_properties_unlocked(struct i915_perf *perf,
                        } else
                                oa_freq_hz = 0;
 
-                       if (oa_freq_hz > i915_oa_max_sample_rate &&
-                           !capable(CAP_SYS_ADMIN)) {
-                               DRM_DEBUG("OA exponent would exceed the max sampling frequency (sysctl dev.i915.oa_max_sample_rate) %uHz without root privileges\n",
+                       if (oa_freq_hz > i915_oa_max_sample_rate && !perfmon_capable()) {
+                               DRM_DEBUG("OA exponent would exceed the max sampling frequency (sysctl dev.i915.oa_max_sample_rate) %uHz without CAP_PERFMON or CAP_SYS_ADMIN privileges\n",
                                          i915_oa_max_sample_rate);
                                return -EACCES;
                        }
@@ -3597,6 +3624,32 @@ static int read_properties_unlocked(struct i915_perf *perf,
                case DRM_I915_PERF_PROP_HOLD_PREEMPTION:
                        props->hold_preemption = !!value;
                        break;
+               case DRM_I915_PERF_PROP_GLOBAL_SSEU: {
+                       struct drm_i915_gem_context_param_sseu user_sseu;
+
+                       if (copy_from_user(&user_sseu,
+                                          u64_to_user_ptr(value),
+                                          sizeof(user_sseu))) {
+                               DRM_DEBUG("Unable to copy global sseu parameter\n");
+                               return -EFAULT;
+                       }
+
+                       ret = get_sseu_config(&props->sseu, props->engine, &user_sseu);
+                       if (ret) {
+                               DRM_DEBUG("Invalid SSEU configuration\n");
+                               return ret;
+                       }
+                       props->has_sseu = true;
+                       break;
+               }
+               case DRM_I915_PERF_PROP_POLL_OA_PERIOD:
+                       if (value < 100000 /* 100us */) {
+                               DRM_DEBUG("OA availability timer too small (%lluns < 100us)\n",
+                                         value);
+                               return -EINVAL;
+                       }
+                       props->poll_oa_period = value;
+                       break;
                case DRM_I915_PERF_PROP_MAX:
                        MISSING_CASE(id);
                        return -EINVAL;
@@ -3679,7 +3732,6 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data,
 void i915_perf_register(struct drm_i915_private *i915)
 {
        struct i915_perf *perf = &i915->perf;
-       int ret;
 
        if (!perf->i915)
                return;
@@ -3693,64 +3745,7 @@ void i915_perf_register(struct drm_i915_private *i915)
        perf->metrics_kobj =
                kobject_create_and_add("metrics",
                                       &i915->drm.primary->kdev->kobj);
-       if (!perf->metrics_kobj)
-               goto exit;
-
-       sysfs_attr_init(&perf->test_config.sysfs_metric_id.attr);
-
-       if (IS_TIGERLAKE(i915)) {
-               i915_perf_load_test_config_tgl(i915);
-       } else if (INTEL_GEN(i915) >= 11) {
-               i915_perf_load_test_config_icl(i915);
-       } else if (IS_CANNONLAKE(i915)) {
-               i915_perf_load_test_config_cnl(i915);
-       } else if (IS_COFFEELAKE(i915)) {
-               if (IS_CFL_GT2(i915))
-                       i915_perf_load_test_config_cflgt2(i915);
-               if (IS_CFL_GT3(i915))
-                       i915_perf_load_test_config_cflgt3(i915);
-       } else if (IS_GEMINILAKE(i915)) {
-               i915_perf_load_test_config_glk(i915);
-       } else if (IS_KABYLAKE(i915)) {
-               if (IS_KBL_GT2(i915))
-                       i915_perf_load_test_config_kblgt2(i915);
-               else if (IS_KBL_GT3(i915))
-                       i915_perf_load_test_config_kblgt3(i915);
-       } else if (IS_BROXTON(i915)) {
-               i915_perf_load_test_config_bxt(i915);
-       } else if (IS_SKYLAKE(i915)) {
-               if (IS_SKL_GT2(i915))
-                       i915_perf_load_test_config_sklgt2(i915);
-               else if (IS_SKL_GT3(i915))
-                       i915_perf_load_test_config_sklgt3(i915);
-               else if (IS_SKL_GT4(i915))
-                       i915_perf_load_test_config_sklgt4(i915);
-       } else if (IS_CHERRYVIEW(i915)) {
-               i915_perf_load_test_config_chv(i915);
-       } else if (IS_BROADWELL(i915)) {
-               i915_perf_load_test_config_bdw(i915);
-       } else if (IS_HASWELL(i915)) {
-               i915_perf_load_test_config_hsw(i915);
-       }
-
-       if (perf->test_config.id == 0)
-               goto sysfs_error;
-
-       ret = sysfs_create_group(perf->metrics_kobj,
-                                &perf->test_config.sysfs_metric);
-       if (ret)
-               goto sysfs_error;
-
-       perf->test_config.perf = perf;
-       kref_init(&perf->test_config.ref);
-
-       goto exit;
 
-sysfs_error:
-       kobject_put(perf->metrics_kobj);
-       perf->metrics_kobj = NULL;
-
-exit:
        mutex_unlock(&perf->lock);
 }
 
@@ -3770,9 +3765,6 @@ void i915_perf_unregister(struct drm_i915_private *i915)
        if (!perf->metrics_kobj)
                return;
 
-       sysfs_remove_group(perf->metrics_kobj,
-                          &perf->test_config.sysfs_metric);
-
        kobject_put(perf->metrics_kobj);
        perf->metrics_kobj = NULL;
 }
@@ -4007,7 +3999,7 @@ int i915_perf_add_config_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       if (i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+       if (i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to add i915 OA config\n");
                return -EACCES;
        }
@@ -4154,7 +4146,7 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data,
                return -ENOTSUPP;
        }
 
-       if (i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+       if (i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to remove i915 OA config\n");
                return -EACCES;
        }
@@ -4347,8 +4339,8 @@ void i915_perf_init(struct drm_i915_private *i915)
        if (perf->ops.enable_metric_set) {
                mutex_init(&perf->lock);
 
-               oa_sample_rate_hard_limit = 1000 *
-                       (RUNTIME_INFO(i915)->cs_timestamp_frequency_khz / 2);
+               oa_sample_rate_hard_limit =
+                       RUNTIME_INFO(i915)->cs_timestamp_frequency_hz / 2;
 
                mutex_init(&perf->metrics_lock);
                idr_init(&perf->metrics_idr);
@@ -4371,6 +4363,11 @@ void i915_perf_init(struct drm_i915_private *i915)
                ratelimit_set_flags(&perf->spurious_report_rs,
                                    RATELIMIT_MSG_ON_RELEASE);
 
+               ratelimit_state_init(&perf->tail_pointer_race,
+                                    5 * HZ, 10);
+               ratelimit_set_flags(&perf->tail_pointer_race,
+                                   RATELIMIT_MSG_ON_RELEASE);
+
                atomic64_set(&perf->noa_programming_delay,
                             500 * 1000 /* 500us */);
 
@@ -4431,8 +4428,15 @@ int i915_perf_ioctl_version(void)
         *    preemption on a particular context so that performance data is
         *    accessible from a delta of MI_RPC reports without looking at the
         *    OA buffer.
+        *
+        * 4: Add DRM_I915_PERF_PROP_ALLOWED_SSEU to limit what contexts can
+        *    be run for the duration of the performance recording based on
+        *    their SSEU configuration.
+        *
+        * 5: Add DRM_I915_PERF_PROP_POLL_OA_PERIOD parameter that controls the
+        *    interval for the hrtimer used to check for OA data.
         */
-       return 3;
+       return 5;
 }
 
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)