Message ID | 20161028021430.2177-8-robert@sixbynine.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 28 October 2016 at 03:14, Robert Bragg <robert@sixbynine.org> wrote: > Gen graphics hardware can be set up to periodically write snapshots of > performance counters into a circular buffer via its Observation > Architecture and this patch exposes that capability to userspace via the > i915 perf interface. > > v2: > Make sure to initialize ->specific_ctx_id when opening, without > relying on _pin_notify hook, in case ctx already pinned. > v3: > Revert back to pinning ctx upfront when opening stream, removing > need to hook in to pinning and to update OACONTROL on the fly. > > Cc: Chris Wilson <chris@chris-wilson.co.uk> > Signed-off-by: Robert Bragg <robert@sixbynine.org> > Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com> > --- > drivers/gpu/drm/i915/i915_drv.h | 66 ++- > drivers/gpu/drm/i915/i915_perf.c | 1036 +++++++++++++++++++++++++++++++++++++- > drivers/gpu/drm/i915/i915_reg.h | 338 +++++++++++++ > include/uapi/drm/i915_drm.h | 71 ++- > 4 files changed, 1482 insertions(+), 29 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index f22adc4..dd2b4d3 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -1767,6 +1767,11 @@ struct intel_wm_config { > bool sprites_scaled; > }; > > +struct i915_oa_format { > + u32 format; > + int size; > +}; > + > struct i915_oa_reg { > i915_reg_t addr; > u32 value; > @@ -1787,11 +1792,6 @@ struct i915_perf_stream_ops { > */ > void (*disable)(struct i915_perf_stream *stream); > > - /* Return: true if any i915 perf records are ready to read() > - * for this stream. > - */ > - bool (*can_read)(struct i915_perf_stream *stream); > - > /* Call poll_wait, passing a wait queue that will be woken > * once there is something ready to read() for the stream > */ > @@ -1801,9 +1801,7 @@ struct i915_perf_stream_ops { > > /* For handling a blocking read, wait until there is something > * to ready to read() for the stream. E.g. wait on the same > - * wait queue that would be passed to poll_wait() until > - * ->can_read() returns true (if its safe to call ->can_read() > - * without the i915 perf lock held). > + * wait queue that would be passed to poll_wait(). > */ > int (*wait_unlocked)(struct i915_perf_stream *stream); > > @@ -1843,11 +1841,28 @@ struct i915_perf_stream { > struct list_head link; > > u32 sample_flags; > + int sample_size; > > struct i915_gem_context *ctx; > bool enabled; > > - struct i915_perf_stream_ops *ops; > + const struct i915_perf_stream_ops *ops; > +}; > + > +struct i915_oa_ops { > + void (*init_oa_buffer)(struct drm_i915_private *dev_priv); > + int (*enable_metric_set)(struct drm_i915_private *dev_priv); > + void (*disable_metric_set)(struct drm_i915_private *dev_priv); > + void (*oa_enable)(struct drm_i915_private *dev_priv); > + void (*oa_disable)(struct drm_i915_private *dev_priv); > + void (*update_oacontrol)(struct drm_i915_private *dev_priv); > + void (*update_hw_ctx_id_locked)(struct drm_i915_private *dev_priv, > + u32 ctx_id); > + int (*read)(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset); > + bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv); > }; > > struct drm_i915_private { > @@ -2154,16 +2169,47 @@ struct drm_i915_private { > > struct { > bool initialized; > + > struct mutex lock; > struct list_head streams; > > + spinlock_t hook_lock; We can get rid of this now, or at least rename it ? > + > struct { > - u32 metrics_set; > + struct i915_perf_stream *exclusive_stream; > + > + u32 specific_ctx_id; > + struct i915_vma *pinned_rcs_vma; > + > + struct hrtimer poll_check_timer; > + wait_queue_head_t poll_wq; > + bool pollin; > + > + bool periodic; > + int period_exponent; > + int timestamp_frequency; > + > + int tail_margin; > + > + int metrics_set; > > const struct i915_oa_reg *mux_regs; > int mux_regs_len; > const struct i915_oa_reg *b_counter_regs; > int b_counter_regs_len; > + > + struct { > + struct i915_vma *vma; > + u8 *vaddr; > + int format; > + int format_size; > + } oa_buffer; > + > + u32 gen7_latched_oastatus1; > + > + struct i915_oa_ops ops; > + const struct i915_oa_format *oa_formats; > + int n_builtin_sets; > } oa; > } perf; > > diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c > index c45cf92..8b9cf0d 100644 > --- a/drivers/gpu/drm/i915/i915_perf.c > +++ b/drivers/gpu/drm/i915/i915_perf.c > @@ -25,16 +25,899 @@ > */ > > #include <linux/anon_inodes.h> > +#include <linux/sizes.h> > > #include "i915_drv.h" > +#include "i915_oa_hsw.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 > + * that the overflow cases are unlikely in normal operation. > + */ > +#define OA_BUFFER_SIZE SZ_16M > + > +#define OA_TAKEN(tail, head) ((tail - head) & (OA_BUFFER_SIZE - 1)) > + > +/* There's a HW race condition between OA unit tail pointer register updates and > + * writes to memory whereby the tail pointer can sometimes get ahead of what's > + * been written out to the OA buffer so far. > + * > + * Although this can be observed explicitly by checking for a zeroed report-id > + * field in tail reports, it seems preferable to account for this earlier e.g. > + * as part of the _oa_buffer_is_empty checks to minimize -EAGAIN polling cycles > + * in this situation. > + * > + * To give time for the most recent reports to land before they may be copied to > + * userspace, the driver operates as if the tail pointer effectively lags behind > + * the HW tail pointer by 'tail_margin' bytes. The margin in bytes is calculated > + * based on this constant in nanoseconds, the current OA sampling exponent > + * and current report size. > + * > + * There is also a fallback check while reading to simply skip over reports with > + * a zeroed report-id. > + */ > +#define OA_TAIL_MARGIN_NSEC 100000ULL > + > +/* frequency for checking whether the OA unit has written new reports to the > + * circular OA buffer... > + */ > +#define POLL_FREQUENCY 200 > +#define POLL_PERIOD (NSEC_PER_SEC / POLL_FREQUENCY) > + > +/* The maximum exponent the hardware accepts is 63 (essentially it selects one > + * of the 64bit timestamp bits to trigger reports from) but there's currently > + * no known use case for sampling as infrequently as once per 47 thousand years. > + * > + * Since the timestamps included in OA reports are only 32bits it seems > + * reasonable to limit the OA exponent where it's still possible to account for > + * overflow in OA report timestamps. > + */ > +#define OA_EXPONENT_MAX 31 > + > +#define INVALID_CTX_ID 0xffffffff > + > + > +/* XXX: beware if future OA HW adds new report formats that the current > + * code assumes all reports have a power-of-two size and ~(size - 1) can > + * be used as a mask to align the OA tail pointer. > + */ > +static struct i915_oa_format hsw_oa_formats[I915_OA_FORMAT_MAX] = { > + [I915_OA_FORMAT_A13] = { 0, 64 }, > + [I915_OA_FORMAT_A29] = { 1, 128 }, > + [I915_OA_FORMAT_A13_B8_C8] = { 2, 128 }, > + /* A29_B8_C8 Disallowed as 192 bytes doesn't factor into buffer size */ > + [I915_OA_FORMAT_B4_C8] = { 4, 64 }, > + [I915_OA_FORMAT_A45_B8_C8] = { 5, 256 }, > + [I915_OA_FORMAT_B4_C8_A16] = { 6, 128 }, > + [I915_OA_FORMAT_C4_B8] = { 7, 64 }, > +}; > + > +#define SAMPLE_OA_REPORT (1<<0) > > struct perf_open_properties { > u32 sample_flags; > > u64 single_context:1; > u64 ctx_handle; > + > + /* OA sampling state */ > + int metrics_set; > + int oa_format; > + bool oa_periodic; > + int oa_period_exponent; > +}; > + > +/* NB: This is either called via fops or the poll check hrtimer (atomic ctx) > + * > + * It's safe to read OA config state here unlocked, assuming that this is only > + * called while the stream is enabled, while the global OA configuration can't > + * be modified. > + * > + * Note: we don't lock around the head/tail reads even though there's the slim > + * possibility of read() fop errors forcing a re-init of the OA buffer > + * pointers. A race here could result in a false positive !empty status which > + * is acceptable. > + */ > +static bool gen7_oa_buffer_is_empty_fop_unlocked(struct drm_i915_private *dev_priv) > +{ > + int report_size = dev_priv->perf.oa.oa_buffer.format_size; > + u32 oastatus2 = I915_READ(GEN7_OASTATUS2); > + u32 oastatus1 = I915_READ(GEN7_OASTATUS1); > + u32 head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; > + u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; > + > + return OA_TAKEN(tail, head) < > + dev_priv->perf.oa.tail_margin + report_size; > +} > + > +/** > + * Appends a status record to a userspace read() buffer. > + */ > +static int append_oa_status(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset, > + enum drm_i915_perf_record_type type) > +{ > + struct drm_i915_perf_record_header header = { type, 0, sizeof(header) }; > + > + if ((count - *offset) < header.size) > + return -ENOSPC; > + > + if (copy_to_user(buf + *offset, &header, sizeof(header))) > + return -EFAULT; > + > + (*offset) += header.size; > + > + return 0; > +} > + > +/** > + * Copies single OA report into userspace read() buffer. > + */ > +static int append_oa_sample(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset, > + const u8 *report) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + int report_size = dev_priv->perf.oa.oa_buffer.format_size; > + struct drm_i915_perf_record_header header; > + u32 sample_flags = stream->sample_flags; > + > + header.type = DRM_I915_PERF_RECORD_SAMPLE; > + header.pad = 0; > + header.size = stream->sample_size; > + > + if ((count - *offset) < header.size) > + return -ENOSPC; > + > + buf += *offset; > + if (copy_to_user(buf, &header, sizeof(header))) > + return -EFAULT; > + buf += sizeof(header); > + > + if (sample_flags & SAMPLE_OA_REPORT) { > + if (copy_to_user(buf, report, report_size)) > + return -EFAULT; > + } > + > + (*offset) += header.size; > + > + return 0; > +} > + > +/** > + * Copies all buffered OA reports into userspace read() buffer. @stream: something, something... > + * @buf: destination buffer given by userspace > + * @count: the number of bytes userspace wants to read > + * @offset: (inout): the current position for writing into @buf > + * @head_ptr: (inout): the current oa buffer cpu read position > + * @tail_ptr: the current oa buffer gpu write position @tail, or probably change the param to tail_ptr ? > + * > + * Returns 0 on success, negative error code on failure. > + * > + * Notably any error condition resulting in a short read (-ENOSPC or > + * -EFAULT) will be returned even though one or more records may > + * have been successfully copied. In this case it's up to the caller > + * to decide if the error should be squashed before returning to > + * userspace. > + * > + * Note: reports are consumed from the head, and appended to the > + * tail, so the head chases the tail?... If you think that's mad > + * and back-to-front you're not alone, but this follows the > + * Gen PRM naming convention. > + */ > +static int gen7_append_oa_reports(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset, > + u32 *head_ptr, > + u32 tail) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + int report_size = dev_priv->perf.oa.oa_buffer.format_size; > + u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.vaddr; > + int tail_margin = dev_priv->perf.oa.tail_margin; > + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); > + u32 mask = (OA_BUFFER_SIZE - 1); > + u32 head; > + u32 taken; > + int ret = 0; > + > + Extra newline. > + if (WARN_ON(!stream->enabled)) > + return -EIO; > + > + head = *head_ptr - gtt_offset; > + tail -= gtt_offset; > + > + /* The OA unit is expected to wrap the tail pointer according to the OA > + * buffer size and since we should never write a misaligned head > + * pointer we don't expect to read one back either... > + */ > + if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE || > + head % report_size) { > + DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart", > + head, tail); Missing a '\n' here for the message. > + dev_priv->perf.oa.ops.oa_disable(dev_priv); > + dev_priv->perf.oa.ops.oa_enable(dev_priv); > + *head_ptr = I915_READ(GEN7_OASTATUS2) & > + GEN7_OASTATUS2_HEAD_MASK; > + return -EIO; > + } > + > + > + /* The tail pointer increases in 64 byte increments, not in report_size > + * steps... > + */ > + tail &= ~(report_size - 1); > + > + /* Move the tail pointer back by the current tail_margin to account for > + * the possibility that the latest reports may not have really landed > + * in memory yet... > + */ > + > + if (OA_TAKEN(tail, head) < report_size + tail_margin) > + return -EAGAIN; > + > + tail -= tail_margin; > + tail &= mask; > + > + for (/* none */; > + (taken = OA_TAKEN(tail, head)); > + head = (head + report_size) & mask) { > + u8 *report = oa_buf_base + head; > + u32 *report32 = (void *)report; > + > + /* All the report sizes factor neatly into the buffer > + * size so we never expect to see a report split > + * between the beginning and end of the buffer. > + * > + * Given the initial alignment check a misalignment > + * here would imply a driver bug that would result > + * in an overrun. > + */ > + if (WARN_ON((OA_BUFFER_SIZE - head) < report_size)) { > + DRM_ERROR("Spurious OA head ptr: non-integral report offset\n"); > + break; > + } > + > + /* The report-ID field for periodic samples includes > + * some undocumented flags related to what triggered > + * the report and is never expected to be zero so we > + * can check that the report isn't invalid before > + * copying it to userspace... > + */ > + if (report32[0] == 0) { > + DRM_ERROR("Skipping spurious, invalid OA report\n"); > + continue; > + } > + > + ret = append_oa_sample(stream, buf, count, offset, report); > + if (ret) > + break; > + > + /* The above report-id field sanity check is based on > + * the assumption that the OA buffer is initially > + * zeroed and we reset the field after copying so the > + * check is still meaningful once old reports start > + * being overwritten. > + */ > + report32[0] = 0; > + } > + > + *head_ptr = gtt_offset + head; > + > + return ret; > +} > + > +static int gen7_oa_read(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + int report_size = dev_priv->perf.oa.oa_buffer.format_size; > + u32 oastatus2; > + u32 oastatus1; > + u32 head; > + u32 tail; > + int ret; > + > + if (WARN_ON(!dev_priv->perf.oa.oa_buffer.vaddr)) > + return -EIO; > + > + oastatus2 = I915_READ(GEN7_OASTATUS2); > + oastatus1 = I915_READ(GEN7_OASTATUS1); > + > + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; > + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; > + > + /* XXX: On Haswell we don't have a safe way to clear oastatus1 > + * bits while the OA unit is enabled (while the tail pointer > + * may be updated asynchronously) so we ignore status bits > + * that have already been reported to userspace. > + */ > + oastatus1 &= ~dev_priv->perf.oa.gen7_latched_oastatus1; > + > + /* We treat OABUFFER_OVERFLOW as a significant error: > + * > + * - The status can be interpreted to mean that the buffer is > + * currently full (with a higher precedence than OA_TAKEN() > + * which will start to report a near-empty buffer after an > + * overflow) but it's awkward that we can't clear the status > + * on Haswell, so without a reset we won't be able to catch > + * the state again. > + * > + * - Since it also implies the HW has started overwriting old > + * reports it may also affect our sanity checks for invalid > + * reports when copying to userspace that assume new reports > + * are being written to cleared memory. > + * > + * - In the future we may want to introduce a flight recorder > + * mode where the driver will automatically maintain a safe > + * guard band between head/tail, avoiding this overflow > + * condition, but we avoid the added driver complexity for > + * now. > + */ > + if (unlikely(oastatus1 & GEN7_OASTATUS1_OABUFFER_OVERFLOW)) { > + ret = append_oa_status(stream, buf, count, offset, > + DRM_I915_PERF_RECORD_OA_BUFFER_LOST); > + if (ret) > + return ret; > + > + DRM_ERROR("OA buffer overflow: force restart"); Missing '\n'. > + > + dev_priv->perf.oa.ops.oa_disable(dev_priv); > + dev_priv->perf.oa.ops.oa_enable(dev_priv); > + > + oastatus2 = I915_READ(GEN7_OASTATUS2); > + oastatus1 = I915_READ(GEN7_OASTATUS1); > + > + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; > + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; > + } > + > + if (unlikely(oastatus1 & GEN7_OASTATUS1_REPORT_LOST)) { > + ret = append_oa_status(stream, buf, count, offset, > + DRM_I915_PERF_RECORD_OA_REPORT_LOST); > + if (ret) > + return ret; > + dev_priv->perf.oa.gen7_latched_oastatus1 |= > + GEN7_OASTATUS1_REPORT_LOST; > + } > + > + ret = gen7_append_oa_reports(stream, buf, count, offset, > + &head, tail); > + > + /* All the report sizes are a power of two and the > + * head should always be incremented by some multiple > + * of the report size. > + * > + * A warning here, but notably if we later read back a > + * misaligned pointer we will treat that as a bug since > + * it could lead to a buffer overrun. > + */ > + WARN_ONCE(head & (report_size - 1), > + "i915: Writing misaligned OA head pointer"); > + > + /* Note: we update the head pointer here even if an error > + * was returned since the error may represent a short read > + * where some some reports were successfully copied. > + */ > + I915_WRITE(GEN7_OASTATUS2, > + ((head & GEN7_OASTATUS2_HEAD_MASK) | > + OA_MEM_SELECT_GGTT)); > + > + return ret; > +} > + > +static int i915_oa_wait_unlocked(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + /* We would wait indefinitly if periodic sampling is not enabled */ indefinitely > + if (!dev_priv->perf.oa.periodic) > + return -EIO; > + > + /* Note: the oa_buffer_is_empty() condition is ok to run unlocked as it > + * just performs mmio reads of the OA buffer head + tail pointers and > + * it's assumed we're handling some operation that implies the stream > + * can't be destroyed until completion (such as a read()) that ensures > + * the device + OA buffer can't disappear > + */ > + return wait_event_interruptible(dev_priv->perf.oa.poll_wq, > + !dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)); > +} > + > +static void i915_oa_poll_wait(struct i915_perf_stream *stream, > + struct file *file, > + poll_table *wait) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + poll_wait(file, &dev_priv->perf.oa.poll_wq, wait); > +} > + > +static int i915_oa_read(struct i915_perf_stream *stream, > + char __user *buf, > + size_t count, > + size_t *offset) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + return dev_priv->perf.oa.ops.read(stream, buf, count, offset); > +} > + > +/* Determine the render context hw id, and ensure it remains fixed for the > + * lifetime of the stream. This ensures that we don't have to worry about > + * updating the context ID in OACONTROL on the fly. > + */ > +static int oa_get_render_ctx_id(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + struct i915_vma *vma; > + int ret; > + > + ret = i915_mutex_lock_interruptible(&dev_priv->drm); > + if (ret) > + return ret; > + > + /* As the ID is the gtt offset of the context's vma we pin > + * the vma to ensure the ID remains fixed. > + * > + * NB: implied RCS engine... > + */ > + vma = i915_gem_context_pin_legacy(stream->ctx); In case you missed it https://patchwork.freedesktop.org/patch/119263/ > + if (IS_ERR(vma)) { > + ret = PTR_ERR(vma); > + goto unlock; > + } > + > + dev_priv->perf.oa.pinned_rcs_vma = vma; > + > + /* Explicitly track the ID (instead of calling i915_ggtt_offset() > + * on the fly) considering the difference with gen8+ and > + * execlists > + */ > + dev_priv->perf.oa.specific_ctx_id = i915_ggtt_offset(vma); > + > +unlock: > + mutex_unlock(&dev_priv->drm.struct_mutex); > + > + return ret; > +} > + > +static void oa_put_render_ctx_id(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + mutex_lock(&dev_priv->drm.struct_mutex); > + > + i915_vma_unpin(dev_priv->perf.oa.pinned_rcs_vma); > + dev_priv->perf.oa.pinned_rcs_vma = NULL; > + > + dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID; > + > + mutex_unlock(&dev_priv->drm.struct_mutex); > +} > + > +static void > +free_oa_buffer(struct drm_i915_private *i915) > +{ > + mutex_lock(&i915->drm.struct_mutex); > + > + i915_gem_object_unpin_map(i915->perf.oa.oa_buffer.vma->obj); > + i915_vma_unpin(i915->perf.oa.oa_buffer.vma); > + i915_gem_object_put(i915->perf.oa.oa_buffer.vma->obj); > + > + i915->perf.oa.oa_buffer.vma = NULL; > + i915->perf.oa.oa_buffer.vaddr = NULL; > + > + mutex_unlock(&i915->drm.struct_mutex); > +} > + > +static void i915_oa_stream_destroy(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + BUG_ON(stream != dev_priv->perf.oa.exclusive_stream); > + > + dev_priv->perf.oa.ops.disable_metric_set(dev_priv); > + > + free_oa_buffer(dev_priv); > + > + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); > + intel_runtime_pm_put(dev_priv); > + > + if (stream->ctx) > + oa_put_render_ctx_id(stream); > + > + dev_priv->perf.oa.exclusive_stream = NULL; > +} > + > +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv) > +{ > + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); > + > + /* Pre-DevBDW: OABUFFER must be set with counters off, > + * before OASTATUS1, but after OASTATUS2 > + */ > + I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */ > + I915_WRITE(GEN7_OABUFFER, gtt_offset); > + I915_WRITE(GEN7_OASTATUS1, gtt_offset | OABUFFER_SIZE_16M); /* tail */ > + > + /* On Haswell we have to track which OASTATUS1 flags we've > + * already seen since they can't be cleared while periodic > + * sampling is enabled. > + */ > + dev_priv->perf.oa.gen7_latched_oastatus1 = 0; > + > + /* NB: although the OA buffer will initially be allocated > + * zeroed via shmfs (and so this memset is redundant when > + * first allocating), we may re-init the OA buffer, either > + * when re-enabling a stream or in error/reset paths. > + * > + * The reason we clear the buffer for each re-init is for the > + * sanity check in gen7_append_oa_reports() that looks at the > + * report-id field to make sure it's non-zero which relies on > + * the assumption that new reports are being written to zeroed > + * memory... > + */ > + memset(dev_priv->perf.oa.oa_buffer.vaddr, 0, OA_BUFFER_SIZE); > + > + /* Maybe make ->pollin per-stream state if we support multiple > + * concurrent streams in the future. > + */ > + dev_priv->perf.oa.pollin = false; > +} > + > +static int alloc_oa_buffer(struct drm_i915_private *dev_priv) > +{ > + struct drm_i915_gem_object *bo; > + struct i915_vma *vma; > + int ret; > + > + if (WARN_ON(dev_priv->perf.oa.oa_buffer.vma)) > + return -ENODEV; > + > + ret = i915_mutex_lock_interruptible(&dev_priv->drm); > + if (ret) > + return ret; > + > + BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE); > + BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M); > + > + bo = i915_gem_object_create(&dev_priv->drm, OA_BUFFER_SIZE); > + if (IS_ERR(bo)) { > + DRM_ERROR("Failed to allocate OA buffer\n"); > + ret = PTR_ERR(bo); > + goto unlock; > + } > + > + ret = i915_gem_object_set_cache_level(bo, I915_CACHE_LLC); > + if (ret) > + goto err_unref; > + > + /* PreHSW required 512K alignment, HSW requires 16M */ > + vma = i915_gem_object_ggtt_pin(bo, NULL, 0, SZ_16M, 0); > + if (IS_ERR(vma)) { > + ret = PTR_ERR(vma); > + goto err_unref; > + } > + dev_priv->perf.oa.oa_buffer.vma = vma; > + > + dev_priv->perf.oa.oa_buffer.vaddr = > + i915_gem_object_pin_map(bo, I915_MAP_WB); > + if (IS_ERR(dev_priv->perf.oa.oa_buffer.vaddr)) { > + ret = PTR_ERR(dev_priv->perf.oa.oa_buffer.vaddr); > + goto err_unpin; > + } > + > + dev_priv->perf.oa.ops.init_oa_buffer(dev_priv); > + > + DRM_DEBUG_DRIVER("OA Buffer initialized, gtt offset = 0x%x, vaddr = %p", > + i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma), > + dev_priv->perf.oa.oa_buffer.vaddr); missing '\n'. > + > + goto unlock; > + > +err_unpin: > + __i915_vma_unpin(vma); > + > +err_unref: > + i915_gem_object_put(bo); > + > + dev_priv->perf.oa.oa_buffer.vaddr = NULL; > + dev_priv->perf.oa.oa_buffer.vma = NULL; > + > +unlock: > + mutex_unlock(&dev_priv->drm.struct_mutex); > + return ret; > +} > + > +static void config_oa_regs(struct drm_i915_private *dev_priv, > + const struct i915_oa_reg *regs, > + int n_regs) > +{ > + int i; > + > + for (i = 0; i < n_regs; i++) { > + const struct i915_oa_reg *reg = regs + i; > + > + I915_WRITE(reg->addr, reg->value); > + } > +} > + > +static int hsw_enable_metric_set(struct drm_i915_private *dev_priv) > +{ > + int ret = i915_oa_select_metric_set_hsw(dev_priv); > + > + if (ret) > + return ret; > + > + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) | > + GT_NOA_ENABLE)); > + > + /* PRM: > + * > + * OA unit is using “crclk” for its functionality. When trunk > + * level clock gating takes place, OA clock would be gated, > + * unable to count the events from non-render clock domain. > + * Render clock gating must be disabled when OA is enabled to > + * count the events from non-render domain. Unit level clock > + * gating for RCS should also be disabled. > + */ > + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) & > + ~GEN7_DOP_CLOCK_GATE_ENABLE)); > + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) | > + GEN6_CSUNIT_CLOCK_GATE_DISABLE)); > + > + config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs, > + dev_priv->perf.oa.mux_regs_len); > + > + /* It apparently takes a fairly long time for a new MUX > + * configuration to be be applied after these register writes. > + * This delay duration was derived empirically based on the > + * render_basic config but hopefully it covers the maximum > + * configuration latency. > + * > + * As a fallback, the checks in _append_oa_reports() to skip > + * invalid OA reports do also seem to work to discard reports > + * generated before this config has completed - albeit not > + * silently. > + * > + * Unfortunately this is essentially a magic number, since we > + * don't currently know of a reliable mechanism for predicting > + * how long the MUX config will take to apply and besides > + * seeing invalid reports we don't know of a reliable way to > + * explicitly check that the MUX config has landed. > + * > + * It's even possible we've miss characterized the underlying > + * problem - it just seems like the simplest explanation why > + * a delay at this location would mitigate any invalid reports. > + */ > + usleep_range(15000, 20000); > + > + config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs, > + dev_priv->perf.oa.b_counter_regs_len); > + > + return 0; > +} > + > +static void hsw_disable_metric_set(struct drm_i915_private *dev_priv) > +{ > + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) & > + ~GEN6_CSUNIT_CLOCK_GATE_DISABLE)); > + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) | > + GEN7_DOP_CLOCK_GATE_ENABLE)); > + > + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) & > + ~GT_NOA_ENABLE)); > +} > + > +static void gen7_update_oacontrol_locked(struct drm_i915_private *dev_priv) > +{ > + assert_spin_locked(&dev_priv->perf.hook_lock); > + > + if (dev_priv->perf.oa.exclusive_stream->enabled) { > + struct i915_gem_context *ctx = > + dev_priv->perf.oa.exclusive_stream->ctx; > + u32 ctx_id = dev_priv->perf.oa.specific_ctx_id; > + > + bool periodic = dev_priv->perf.oa.periodic; > + u32 period_exponent = dev_priv->perf.oa.period_exponent; > + u32 report_format = dev_priv->perf.oa.oa_buffer.format; > + > + I915_WRITE(GEN7_OACONTROL, > + (ctx_id & GEN7_OACONTROL_CTX_MASK) | > + (period_exponent << > + GEN7_OACONTROL_TIMER_PERIOD_SHIFT) | > + (periodic ? GEN7_OACONTROL_TIMER_ENABLE : 0) | > + (report_format << GEN7_OACONTROL_FORMAT_SHIFT) | > + (ctx ? GEN7_OACONTROL_PER_CTX_ENABLE : 0) | > + GEN7_OACONTROL_ENABLE); > + } else > + I915_WRITE(GEN7_OACONTROL, 0); > +} > + > +static void gen7_oa_enable(struct drm_i915_private *dev_priv) > +{ > + unsigned long flags; > + > + /* Reset buf pointers so we don't forward reports from before now. > + * > + * Think carefully if considering trying to avoid this, since it > + * also ensures status flags and the buffer itself are cleared > + * in error paths, and we have checks for invalid reports based > + * on the assumption that certain fields are written to zeroed > + * memory which this helps maintains. > + */ > + gen7_init_oa_buffer(dev_priv); > + > + spin_lock_irqsave(&dev_priv->perf.hook_lock, flags); > + gen7_update_oacontrol_locked(dev_priv); > + spin_unlock_irqrestore(&dev_priv->perf.hook_lock, flags); > +} > + > +static void i915_oa_stream_enable(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + dev_priv->perf.oa.ops.oa_enable(dev_priv); > + > + if (dev_priv->perf.oa.periodic) > + hrtimer_start(&dev_priv->perf.oa.poll_check_timer, > + ns_to_ktime(POLL_PERIOD), > + HRTIMER_MODE_REL_PINNED); > +} > + > +static void gen7_oa_disable(struct drm_i915_private *dev_priv) > +{ > + I915_WRITE(GEN7_OACONTROL, 0); > +} > + > +static void i915_oa_stream_disable(struct i915_perf_stream *stream) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + > + dev_priv->perf.oa.ops.oa_disable(dev_priv); > + > + if (dev_priv->perf.oa.periodic) > + hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer); > +} > + > +static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent) > +{ > + return 1000000000ULL * (2ULL << exponent) / > + dev_priv->perf.oa.timestamp_frequency; > +} > + > +static const struct i915_perf_stream_ops i915_oa_stream_ops = { > + .destroy = i915_oa_stream_destroy, > + .enable = i915_oa_stream_enable, > + .disable = i915_oa_stream_disable, > + .wait_unlocked = i915_oa_wait_unlocked, > + .poll_wait = i915_oa_poll_wait, > + .read = i915_oa_read, > }; > > +static int i915_oa_stream_init(struct i915_perf_stream *stream, > + struct drm_i915_perf_open_param *param, > + struct perf_open_properties *props) > +{ > + struct drm_i915_private *dev_priv = stream->dev_priv; > + int format_size; > + int ret; > + > + if (!(props->sample_flags & SAMPLE_OA_REPORT)) { > + DRM_ERROR("Only OA report sampling supported\n"); > + return -EINVAL; > + } > + > + if (!dev_priv->perf.oa.ops.init_oa_buffer) { > + DRM_ERROR("OA unit not supported\n"); > + return -ENODEV; > + } > + > + /* To avoid the complexity of having to accurately filter > + * counter reports and marshal to the appropriate client > + * we currently only allow exclusive access > + */ > + if (dev_priv->perf.oa.exclusive_stream) { > + DRM_ERROR("OA unit already in use\n"); > + return -EBUSY; > + } > + > + if (!props->metrics_set) { > + DRM_ERROR("OA metric set not specified\n"); > + return -EINVAL; > + } > + > + if (!props->oa_format) { > + DRM_ERROR("OA report format not specified\n"); > + return -EINVAL; > + } > + > + stream->sample_size = sizeof(struct drm_i915_perf_record_header); > + > + format_size = dev_priv->perf.oa.oa_formats[props->oa_format].size; > + > + stream->sample_flags |= SAMPLE_OA_REPORT; > + stream->sample_size += format_size; > + > + dev_priv->perf.oa.oa_buffer.format_size = format_size; > + if (WARN_ON(dev_priv->perf.oa.oa_buffer.format_size == 0)) > + return -EINVAL; > + > + dev_priv->perf.oa.oa_buffer.format = > + dev_priv->perf.oa.oa_formats[props->oa_format].format; > + > + dev_priv->perf.oa.metrics_set = props->metrics_set; > + > + dev_priv->perf.oa.periodic = props->oa_periodic; > + if (dev_priv->perf.oa.periodic) { > + u64 period_ns = oa_exponent_to_ns(dev_priv, > + props->oa_period_exponent); > + > + dev_priv->perf.oa.period_exponent = props->oa_period_exponent; > + > + /* See comment for OA_TAIL_MARGIN_NSEC for details > + * about this tail_margin... > + */ > + dev_priv->perf.oa.tail_margin = > + ((OA_TAIL_MARGIN_NSEC / period_ns) + 1) * format_size; > + } > + > + if (stream->ctx) { > + ret = oa_get_render_ctx_id(stream); > + if (ret) > + return ret; > + } > + > + ret = alloc_oa_buffer(dev_priv); > + if (ret) > + goto err_oa_buf_alloc; > + > + /* PRM - observability performance counters: > + * > + * OACONTROL, performance counter enable, note: > + * > + * "When this bit is set, in order to have coherent counts, > + * RC6 power state and trunk clock gating must be disabled. > + * This can be achieved by programming MMIO registers as > + * 0xA094=0 and 0xA090[31]=1" > + * > + * In our case we are expecting that taking pm + FORCEWAKE > + * references will effectively disable RC6. > + */ > + intel_runtime_pm_get(dev_priv); > + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); > + > + ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv); > + if (ret) > + goto err_enable; > + > + stream->ops = &i915_oa_stream_ops; > + > + dev_priv->perf.oa.exclusive_stream = stream; > + > + return 0; > + > +err_enable: > + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); > + intel_runtime_pm_put(dev_priv); > + free_oa_buffer(dev_priv); > + > +err_oa_buf_alloc: > + if (stream->ctx) > + oa_put_render_ctx_id(stream); > + > + return ret; > +} > + > static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream, > struct file *file, > char __user *buf, > @@ -78,8 +961,20 @@ static ssize_t i915_perf_read(struct file *file, > struct drm_i915_private *dev_priv = stream->dev_priv; > ssize_t ret; > > + /* To ensure it's handled consistently we simply treat all reads of a > + * disabled stream as an error. In particular it might otherwise lead > + * to a deadlock for blocking file descriptors... > + */ > + if (!stream->enabled) > + return -EIO; > + > if (!(file->f_flags & O_NONBLOCK)) { > - /* Allow false positives from stream->ops->wait_unlocked. > + /* There's the small chance of false positives from > + * stream->ops->wait_unlocked. > + * > + * E.g. with single context filtering since we only wait until > + * oabuffer has >= 1 report we don't immediately know whether > + * any reports really belong to the current context > */ > do { > ret = stream->ops->wait_unlocked(stream); > @@ -97,21 +992,51 @@ static ssize_t i915_perf_read(struct file *file, > mutex_unlock(&dev_priv->perf.lock); > } > > + if (ret >= 0) { > + /* Maybe make ->pollin per-stream state if we support multiple > + * concurrent streams in the future. > + */ > + dev_priv->perf.oa.pollin = false; > + } > + > return ret; > } > > -static unsigned int i915_perf_poll_locked(struct i915_perf_stream *stream, > +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer) > +{ > + struct drm_i915_private *dev_priv = > + container_of(hrtimer, typeof(*dev_priv), > + perf.oa.poll_check_timer); > + > + if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)) { > + dev_priv->perf.oa.pollin = true; > + wake_up(&dev_priv->perf.oa.poll_wq); > + } > + > + hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD)); > + > + return HRTIMER_RESTART; > +} > + > +static unsigned int i915_perf_poll_locked(struct drm_i915_private *dev_priv, > + struct i915_perf_stream *stream, > struct file *file, > poll_table *wait) > { > - unsigned int streams = 0; > + unsigned int events = 0; > > stream->ops->poll_wait(stream, file, wait); > > - if (stream->ops->can_read(stream)) > - streams |= POLLIN; > + /* Note: we don't explicitly check whether there's something to read > + * here since this path may be very hot depending on what else > + * userspace is polling, or on the timeout in use. We rely solely on > + * the hrtimer/oa_poll_check_timer_cb to notify us when there are > + * samples to read. > + */ > + if (dev_priv->perf.oa.pollin) > + events |= POLLIN; > > - return streams; > + return events; > } > > static unsigned int i915_perf_poll(struct file *file, poll_table *wait) > @@ -121,7 +1046,7 @@ static unsigned int i915_perf_poll(struct file *file, poll_table *wait) > int ret; > > mutex_lock(&dev_priv->perf.lock); > - ret = i915_perf_poll_locked(stream, file, wait); > + ret = i915_perf_poll_locked(dev_priv, stream, file, wait); > mutex_unlock(&dev_priv->perf.lock); > > return ret; > @@ -285,18 +1210,21 @@ i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv, > goto err_ctx; > } > > - stream->sample_flags = props->sample_flags; > stream->dev_priv = dev_priv; > stream->ctx = specific_ctx; > > - /* > - * TODO: support sampling something > - * > - * For now this is as far as we can go. > + ret = i915_oa_stream_init(stream, param, props); > + if (ret) > + goto err_alloc; > + > + /* we avoid simply assigning stream->sample_flags = props->sample_flags > + * to have _stream_init check the combination of sample flags more > + * thoroughly, but still this is the expected result at this point. > */ > - DRM_ERROR("Unsupported i915 perf stream configuration\n"); > - ret = -EINVAL; > - goto err_alloc; > + if (WARN_ON(stream->sample_flags != props->sample_flags)) { > + ret = -ENODEV; > + goto err_alloc; > + } > > list_add(&stream->link, &dev_priv->perf.streams); > > @@ -376,6 +1304,56 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv, > props->single_context = 1; > props->ctx_handle = value; > break; > + case DRM_I915_PERF_PROP_SAMPLE_OA: > + props->sample_flags |= SAMPLE_OA_REPORT; > + break; > + case DRM_I915_PERF_PROP_OA_METRICS_SET: > + if (value == 0 || > + value > dev_priv->perf.oa.n_builtin_sets) { > + DRM_ERROR("Unknown OA metric set ID"); > + return -EINVAL; > + } > + props->metrics_set = value; > + break; > + case DRM_I915_PERF_PROP_OA_FORMAT: > + if (value == 0 || value >= I915_OA_FORMAT_MAX) { > + DRM_ERROR("Invalid OA report format\n"); > + return -EINVAL; > + } > + if (!dev_priv->perf.oa.oa_formats[value].size) { > + DRM_ERROR("Invalid OA report format\n"); > + return -EINVAL; > + } > + props->oa_format = value; > + break; > + case DRM_I915_PERF_PROP_OA_EXPONENT: > + if (value > OA_EXPONENT_MAX) { > + DRM_ERROR("OA timer exponent too high (> %u)\n", > + OA_EXPONENT_MAX); > + return -EINVAL; > + } > + > + /* NB: The exponent represents a period as follows: > + * > + * 80ns * 2^(period_exponent + 1) > + * > + * Theoretically we can program the OA unit to sample > + * every 160ns but don't allow that by default unless > + * root. > + * > + * Referring to perf's > + * kernel.perf_event_max_sample_rate for a precedent > + * (100000 by default); with an OA exponent of 6 we get > + * a period of 10.240 microseconds -just under 100000Hz > + */ > + if (value < 6 && !capable(CAP_SYS_ADMIN)) { > + DRM_ERROR("Minimum OA sampling exponent is 6 without root privileges\n"); > + return -EACCES; > + } > + > + props->oa_periodic = true; > + props->oa_period_exponent = value; > + break; > default: > MISSING_CASE(id); > DRM_ERROR("Unknown i915 perf property ID"); > @@ -426,8 +1404,33 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data, > > void i915_perf_init(struct drm_i915_private *dev_priv) > { > + if (!IS_HASWELL(dev_priv)) > + return; > + > + hrtimer_init(&dev_priv->perf.oa.poll_check_timer, > + CLOCK_MONOTONIC, HRTIMER_MODE_REL); > + dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb; > + init_waitqueue_head(&dev_priv->perf.oa.poll_wq); > + > INIT_LIST_HEAD(&dev_priv->perf.streams); > mutex_init(&dev_priv->perf.lock); > + spin_lock_init(&dev_priv->perf.hook_lock); > + > + dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer; > + dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set; > + dev_priv->perf.oa.ops.disable_metric_set = hsw_disable_metric_set; > + dev_priv->perf.oa.ops.oa_enable = gen7_oa_enable; > + dev_priv->perf.oa.ops.oa_disable = gen7_oa_disable; > + dev_priv->perf.oa.ops.read = gen7_oa_read; > + dev_priv->perf.oa.ops.oa_buffer_is_empty = > + gen7_oa_buffer_is_empty_fop_unlocked; > + > + dev_priv->perf.oa.timestamp_frequency = 12500000; > + > + dev_priv->perf.oa.oa_formats = hsw_oa_formats; > + > + dev_priv->perf.oa.n_builtin_sets = > + i915_oa_n_builtin_metric_sets_hsw; > > dev_priv->perf.initialized = true; > } > @@ -437,7 +1440,6 @@ void i915_perf_fini(struct drm_i915_private *dev_priv) > if (!dev_priv->perf.initialized) > return; > > - /* Currently nothing to clean up */ > - > + memset(&dev_priv->perf.oa.ops, 0, sizeof(dev_priv->perf.oa.ops)); > dev_priv->perf.initialized = false; > } > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index 59628d5..6e3b435 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -616,6 +616,343 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) > #define HSW_CS_GPR_UDW(n) _MMIO(0x2600 + (n) * 8 + 4) > > #define GEN7_OACONTROL _MMIO(0x2360) > +#define GEN7_OACONTROL_CTX_MASK 0xFFFFF000 > +#define GEN7_OACONTROL_TIMER_PERIOD_MASK 0x3F > +#define GEN7_OACONTROL_TIMER_PERIOD_SHIFT 6 > +#define GEN7_OACONTROL_TIMER_ENABLE (1<<5) > +#define GEN7_OACONTROL_FORMAT_A13 (0<<2) > +#define GEN7_OACONTROL_FORMAT_A29 (1<<2) > +#define GEN7_OACONTROL_FORMAT_A13_B8_C8 (2<<2) > +#define GEN7_OACONTROL_FORMAT_A29_B8_C8 (3<<2) > +#define GEN7_OACONTROL_FORMAT_B4_C8 (4<<2) > +#define GEN7_OACONTROL_FORMAT_A45_B8_C8 (5<<2) > +#define GEN7_OACONTROL_FORMAT_B4_C8_A16 (6<<2) > +#define GEN7_OACONTROL_FORMAT_C4_B8 (7<<2) > +#define GEN7_OACONTROL_FORMAT_SHIFT 2 > +#define GEN7_OACONTROL_PER_CTX_ENABLE (1<<1) > +#define GEN7_OACONTROL_ENABLE (1<<0) > + > +#define GEN8_OACTXID _MMIO(0x2364) > + > +#define GEN8_OACONTROL _MMIO(0x2B00) > +#define GEN8_OA_REPORT_FORMAT_A12 (0<<2) > +#define GEN8_OA_REPORT_FORMAT_A12_B8_C8 (2<<2) > +#define GEN8_OA_REPORT_FORMAT_A36_B8_C8 (5<<2) > +#define GEN8_OA_REPORT_FORMAT_C4_B8 (7<<2) > +#define GEN8_OA_REPORT_FORMAT_SHIFT 2 > +#define GEN8_OA_SPECIFIC_CONTEXT_ENABLE (1<<1) > +#define GEN8_OA_COUNTER_ENABLE (1<<0) > + > +#define GEN8_OACTXCONTROL _MMIO(0x2360) > +#define GEN8_OA_TIMER_PERIOD_MASK 0x3F > +#define GEN8_OA_TIMER_PERIOD_SHIFT 2 > +#define GEN8_OA_TIMER_ENABLE (1<<1) > +#define GEN8_OA_COUNTER_RESUME (1<<0) > + > +#define GEN7_OABUFFER _MMIO(0x23B0) /* R/W */ > +#define GEN7_OABUFFER_OVERRUN_DISABLE (1<<3) > +#define GEN7_OABUFFER_EDGE_TRIGGER (1<<2) > +#define GEN7_OABUFFER_STOP_RESUME_ENABLE (1<<1) > +#define GEN7_OABUFFER_RESUME (1<<0) > + > +#define GEN8_OABUFFER _MMIO(0x2b14) > + > +#define GEN7_OASTATUS1 _MMIO(0x2364) > +#define GEN7_OASTATUS1_TAIL_MASK 0xffffffc0 > +#define GEN7_OASTATUS1_COUNTER_OVERFLOW (1<<2) > +#define GEN7_OASTATUS1_OABUFFER_OVERFLOW (1<<1) > +#define GEN7_OASTATUS1_REPORT_LOST (1<<0) > + > +#define GEN7_OASTATUS2 _MMIO(0x2368) > +#define GEN7_OASTATUS2_HEAD_MASK 0xffffffc0 > + > +#define GEN8_OASTATUS _MMIO(0x2b08) > +#define GEN8_OASTATUS_OVERRUN_STATUS (1<<3) > +#define GEN8_OASTATUS_COUNTER_OVERFLOW (1<<2) > +#define GEN8_OASTATUS_OABUFFER_OVERFLOW (1<<1) > +#define GEN8_OASTATUS_REPORT_LOST (1<<0) > + > +#define GEN8_OAHEADPTR _MMIO(0x2B0C) > +#define GEN8_OATAILPTR _MMIO(0x2B10) > + > +#define OABUFFER_SIZE_128K (0<<3) > +#define OABUFFER_SIZE_256K (1<<3) > +#define OABUFFER_SIZE_512K (2<<3) > +#define OABUFFER_SIZE_1M (3<<3) > +#define OABUFFER_SIZE_2M (4<<3) > +#define OABUFFER_SIZE_4M (5<<3) > +#define OABUFFER_SIZE_8M (6<<3) > +#define OABUFFER_SIZE_16M (7<<3) > + > +#define OA_MEM_SELECT_GGTT (1<<0) > + > +#define EU_PERF_CNTL0 _MMIO(0xe458) > + > +#define GDT_CHICKEN_BITS _MMIO(0x9840) > +#define GT_NOA_ENABLE 0x00000080 > + > +/* > + * OA Boolean state > + */ > + > +#define OAREPORTTRIG1 _MMIO(0x2740) > +#define OAREPORTTRIG1_THRESHOLD_MASK 0xffff > +#define OAREPORTTRIG1_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ > + > +#define OAREPORTTRIG2 _MMIO(0x2744) > +#define OAREPORTTRIG2_INVERT_A_0 (1<<0) > +#define OAREPORTTRIG2_INVERT_A_1 (1<<1) > +#define OAREPORTTRIG2_INVERT_A_2 (1<<2) > +#define OAREPORTTRIG2_INVERT_A_3 (1<<3) > +#define OAREPORTTRIG2_INVERT_A_4 (1<<4) > +#define OAREPORTTRIG2_INVERT_A_5 (1<<5) > +#define OAREPORTTRIG2_INVERT_A_6 (1<<6) > +#define OAREPORTTRIG2_INVERT_A_7 (1<<7) > +#define OAREPORTTRIG2_INVERT_A_8 (1<<8) > +#define OAREPORTTRIG2_INVERT_A_9 (1<<9) > +#define OAREPORTTRIG2_INVERT_A_10 (1<<10) > +#define OAREPORTTRIG2_INVERT_A_11 (1<<11) > +#define OAREPORTTRIG2_INVERT_A_12 (1<<12) > +#define OAREPORTTRIG2_INVERT_A_13 (1<<13) > +#define OAREPORTTRIG2_INVERT_A_14 (1<<14) > +#define OAREPORTTRIG2_INVERT_A_15 (1<<15) > +#define OAREPORTTRIG2_INVERT_B_0 (1<<16) > +#define OAREPORTTRIG2_INVERT_B_1 (1<<17) > +#define OAREPORTTRIG2_INVERT_B_2 (1<<18) > +#define OAREPORTTRIG2_INVERT_B_3 (1<<19) > +#define OAREPORTTRIG2_INVERT_C_0 (1<<20) > +#define OAREPORTTRIG2_INVERT_C_1 (1<<21) > +#define OAREPORTTRIG2_INVERT_D_0 (1<<22) > +#define OAREPORTTRIG2_THRESHOLD_ENABLE (1<<23) > +#define OAREPORTTRIG2_REPORT_TRIGGER_ENABLE (1<<31) > + > +#define OAREPORTTRIG3 _MMIO(0x2748) > +#define OAREPORTTRIG3_NOA_SELECT_MASK 0xf > +#define OAREPORTTRIG3_NOA_SELECT_8_SHIFT 0 > +#define OAREPORTTRIG3_NOA_SELECT_9_SHIFT 4 > +#define OAREPORTTRIG3_NOA_SELECT_10_SHIFT 8 > +#define OAREPORTTRIG3_NOA_SELECT_11_SHIFT 12 > +#define OAREPORTTRIG3_NOA_SELECT_12_SHIFT 16 > +#define OAREPORTTRIG3_NOA_SELECT_13_SHIFT 20 > +#define OAREPORTTRIG3_NOA_SELECT_14_SHIFT 24 > +#define OAREPORTTRIG3_NOA_SELECT_15_SHIFT 28 > + > +#define OAREPORTTRIG4 _MMIO(0x274c) > +#define OAREPORTTRIG4_NOA_SELECT_MASK 0xf > +#define OAREPORTTRIG4_NOA_SELECT_0_SHIFT 0 > +#define OAREPORTTRIG4_NOA_SELECT_1_SHIFT 4 > +#define OAREPORTTRIG4_NOA_SELECT_2_SHIFT 8 > +#define OAREPORTTRIG4_NOA_SELECT_3_SHIFT 12 > +#define OAREPORTTRIG4_NOA_SELECT_4_SHIFT 16 > +#define OAREPORTTRIG4_NOA_SELECT_5_SHIFT 20 > +#define OAREPORTTRIG4_NOA_SELECT_6_SHIFT 24 > +#define OAREPORTTRIG4_NOA_SELECT_7_SHIFT 28 > + > +#define OAREPORTTRIG5 _MMIO(0x2750) > +#define OAREPORTTRIG5_THRESHOLD_MASK 0xffff > +#define OAREPORTTRIG5_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ > + > +#define OAREPORTTRIG6 _MMIO(0x2754) > +#define OAREPORTTRIG6_INVERT_A_0 (1<<0) > +#define OAREPORTTRIG6_INVERT_A_1 (1<<1) > +#define OAREPORTTRIG6_INVERT_A_2 (1<<2) > +#define OAREPORTTRIG6_INVERT_A_3 (1<<3) > +#define OAREPORTTRIG6_INVERT_A_4 (1<<4) > +#define OAREPORTTRIG6_INVERT_A_5 (1<<5) > +#define OAREPORTTRIG6_INVERT_A_6 (1<<6) > +#define OAREPORTTRIG6_INVERT_A_7 (1<<7) > +#define OAREPORTTRIG6_INVERT_A_8 (1<<8) > +#define OAREPORTTRIG6_INVERT_A_9 (1<<9) > +#define OAREPORTTRIG6_INVERT_A_10 (1<<10) > +#define OAREPORTTRIG6_INVERT_A_11 (1<<11) > +#define OAREPORTTRIG6_INVERT_A_12 (1<<12) > +#define OAREPORTTRIG6_INVERT_A_13 (1<<13) > +#define OAREPORTTRIG6_INVERT_A_14 (1<<14) > +#define OAREPORTTRIG6_INVERT_A_15 (1<<15) > +#define OAREPORTTRIG6_INVERT_B_0 (1<<16) > +#define OAREPORTTRIG6_INVERT_B_1 (1<<17) > +#define OAREPORTTRIG6_INVERT_B_2 (1<<18) > +#define OAREPORTTRIG6_INVERT_B_3 (1<<19) > +#define OAREPORTTRIG6_INVERT_C_0 (1<<20) > +#define OAREPORTTRIG6_INVERT_C_1 (1<<21) > +#define OAREPORTTRIG6_INVERT_D_0 (1<<22) > +#define OAREPORTTRIG6_THRESHOLD_ENABLE (1<<23) > +#define OAREPORTTRIG6_REPORT_TRIGGER_ENABLE (1<<31) > + > +#define OAREPORTTRIG7 _MMIO(0x2758) > +#define OAREPORTTRIG7_NOA_SELECT_MASK 0xf > +#define OAREPORTTRIG7_NOA_SELECT_8_SHIFT 0 > +#define OAREPORTTRIG7_NOA_SELECT_9_SHIFT 4 > +#define OAREPORTTRIG7_NOA_SELECT_10_SHIFT 8 > +#define OAREPORTTRIG7_NOA_SELECT_11_SHIFT 12 > +#define OAREPORTTRIG7_NOA_SELECT_12_SHIFT 16 > +#define OAREPORTTRIG7_NOA_SELECT_13_SHIFT 20 > +#define OAREPORTTRIG7_NOA_SELECT_14_SHIFT 24 > +#define OAREPORTTRIG7_NOA_SELECT_15_SHIFT 28 > + > +#define OAREPORTTRIG8 _MMIO(0x275c) > +#define OAREPORTTRIG8_NOA_SELECT_MASK 0xf > +#define OAREPORTTRIG8_NOA_SELECT_0_SHIFT 0 > +#define OAREPORTTRIG8_NOA_SELECT_1_SHIFT 4 > +#define OAREPORTTRIG8_NOA_SELECT_2_SHIFT 8 > +#define OAREPORTTRIG8_NOA_SELECT_3_SHIFT 12 > +#define OAREPORTTRIG8_NOA_SELECT_4_SHIFT 16 > +#define OAREPORTTRIG8_NOA_SELECT_5_SHIFT 20 > +#define OAREPORTTRIG8_NOA_SELECT_6_SHIFT 24 > +#define OAREPORTTRIG8_NOA_SELECT_7_SHIFT 28 > + > +#define OASTARTTRIG1 _MMIO(0x2710) > +#define OASTARTTRIG1_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 > +#define OASTARTTRIG1_THRESHOLD_MASK 0xffff > + > +#define OASTARTTRIG2 _MMIO(0x2714) > +#define OASTARTTRIG2_INVERT_A_0 (1<<0) > +#define OASTARTTRIG2_INVERT_A_1 (1<<1) > +#define OASTARTTRIG2_INVERT_A_2 (1<<2) > +#define OASTARTTRIG2_INVERT_A_3 (1<<3) > +#define OASTARTTRIG2_INVERT_A_4 (1<<4) > +#define OASTARTTRIG2_INVERT_A_5 (1<<5) > +#define OASTARTTRIG2_INVERT_A_6 (1<<6) > +#define OASTARTTRIG2_INVERT_A_7 (1<<7) > +#define OASTARTTRIG2_INVERT_A_8 (1<<8) > +#define OASTARTTRIG2_INVERT_A_9 (1<<9) > +#define OASTARTTRIG2_INVERT_A_10 (1<<10) > +#define OASTARTTRIG2_INVERT_A_11 (1<<11) > +#define OASTARTTRIG2_INVERT_A_12 (1<<12) > +#define OASTARTTRIG2_INVERT_A_13 (1<<13) > +#define OASTARTTRIG2_INVERT_A_14 (1<<14) > +#define OASTARTTRIG2_INVERT_A_15 (1<<15) > +#define OASTARTTRIG2_INVERT_B_0 (1<<16) > +#define OASTARTTRIG2_INVERT_B_1 (1<<17) > +#define OASTARTTRIG2_INVERT_B_2 (1<<18) > +#define OASTARTTRIG2_INVERT_B_3 (1<<19) > +#define OASTARTTRIG2_INVERT_C_0 (1<<20) > +#define OASTARTTRIG2_INVERT_C_1 (1<<21) > +#define OASTARTTRIG2_INVERT_D_0 (1<<22) > +#define OASTARTTRIG2_THRESHOLD_ENABLE (1<<23) > +#define OASTARTTRIG2_START_TRIG_FLAG_MBZ (1<<24) > +#define OASTARTTRIG2_EVENT_SELECT_0 (1<<28) > +#define OASTARTTRIG2_EVENT_SELECT_1 (1<<29) > +#define OASTARTTRIG2_EVENT_SELECT_2 (1<<30) > +#define OASTARTTRIG2_EVENT_SELECT_3 (1<<31) > + > +#define OASTARTTRIG3 _MMIO(0x2718) > +#define OASTARTTRIG3_NOA_SELECT_MASK 0xf > +#define OASTARTTRIG3_NOA_SELECT_8_SHIFT 0 > +#define OASTARTTRIG3_NOA_SELECT_9_SHIFT 4 > +#define OASTARTTRIG3_NOA_SELECT_10_SHIFT 8 > +#define OASTARTTRIG3_NOA_SELECT_11_SHIFT 12 > +#define OASTARTTRIG3_NOA_SELECT_12_SHIFT 16 > +#define OASTARTTRIG3_NOA_SELECT_13_SHIFT 20 > +#define OASTARTTRIG3_NOA_SELECT_14_SHIFT 24 > +#define OASTARTTRIG3_NOA_SELECT_15_SHIFT 28 > + > +#define OASTARTTRIG4 _MMIO(0x271c) > +#define OASTARTTRIG4_NOA_SELECT_MASK 0xf > +#define OASTARTTRIG4_NOA_SELECT_0_SHIFT 0 > +#define OASTARTTRIG4_NOA_SELECT_1_SHIFT 4 > +#define OASTARTTRIG4_NOA_SELECT_2_SHIFT 8 > +#define OASTARTTRIG4_NOA_SELECT_3_SHIFT 12 > +#define OASTARTTRIG4_NOA_SELECT_4_SHIFT 16 > +#define OASTARTTRIG4_NOA_SELECT_5_SHIFT 20 > +#define OASTARTTRIG4_NOA_SELECT_6_SHIFT 24 > +#define OASTARTTRIG4_NOA_SELECT_7_SHIFT 28 > + > +#define OASTARTTRIG5 _MMIO(0x2720) > +#define OASTARTTRIG5_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 > +#define OASTARTTRIG5_THRESHOLD_MASK 0xffff > + > +#define OASTARTTRIG6 _MMIO(0x2724) > +#define OASTARTTRIG6_INVERT_A_0 (1<<0) > +#define OASTARTTRIG6_INVERT_A_1 (1<<1) > +#define OASTARTTRIG6_INVERT_A_2 (1<<2) > +#define OASTARTTRIG6_INVERT_A_3 (1<<3) > +#define OASTARTTRIG6_INVERT_A_4 (1<<4) > +#define OASTARTTRIG6_INVERT_A_5 (1<<5) > +#define OASTARTTRIG6_INVERT_A_6 (1<<6) > +#define OASTARTTRIG6_INVERT_A_7 (1<<7) > +#define OASTARTTRIG6_INVERT_A_8 (1<<8) > +#define OASTARTTRIG6_INVERT_A_9 (1<<9) > +#define OASTARTTRIG6_INVERT_A_10 (1<<10) > +#define OASTARTTRIG6_INVERT_A_11 (1<<11) > +#define OASTARTTRIG6_INVERT_A_12 (1<<12) > +#define OASTARTTRIG6_INVERT_A_13 (1<<13) > +#define OASTARTTRIG6_INVERT_A_14 (1<<14) > +#define OASTARTTRIG6_INVERT_A_15 (1<<15) > +#define OASTARTTRIG6_INVERT_B_0 (1<<16) > +#define OASTARTTRIG6_INVERT_B_1 (1<<17) > +#define OASTARTTRIG6_INVERT_B_2 (1<<18) > +#define OASTARTTRIG6_INVERT_B_3 (1<<19) > +#define OASTARTTRIG6_INVERT_C_0 (1<<20) > +#define OASTARTTRIG6_INVERT_C_1 (1<<21) > +#define OASTARTTRIG6_INVERT_D_0 (1<<22) > +#define OASTARTTRIG6_THRESHOLD_ENABLE (1<<23) > +#define OASTARTTRIG6_START_TRIG_FLAG_MBZ (1<<24) > +#define OASTARTTRIG6_EVENT_SELECT_4 (1<<28) > +#define OASTARTTRIG6_EVENT_SELECT_5 (1<<29) > +#define OASTARTTRIG6_EVENT_SELECT_6 (1<<30) > +#define OASTARTTRIG6_EVENT_SELECT_7 (1<<31) > + > +#define OASTARTTRIG7 _MMIO(0x2728) > +#define OASTARTTRIG7_NOA_SELECT_MASK 0xf > +#define OASTARTTRIG7_NOA_SELECT_8_SHIFT 0 > +#define OASTARTTRIG7_NOA_SELECT_9_SHIFT 4 > +#define OASTARTTRIG7_NOA_SELECT_10_SHIFT 8 > +#define OASTARTTRIG7_NOA_SELECT_11_SHIFT 12 > +#define OASTARTTRIG7_NOA_SELECT_12_SHIFT 16 > +#define OASTARTTRIG7_NOA_SELECT_13_SHIFT 20 > +#define OASTARTTRIG7_NOA_SELECT_14_SHIFT 24 > +#define OASTARTTRIG7_NOA_SELECT_15_SHIFT 28 > + > +#define OASTARTTRIG8 _MMIO(0x272c) > +#define OASTARTTRIG8_NOA_SELECT_MASK 0xf > +#define OASTARTTRIG8_NOA_SELECT_0_SHIFT 0 > +#define OASTARTTRIG8_NOA_SELECT_1_SHIFT 4 > +#define OASTARTTRIG8_NOA_SELECT_2_SHIFT 8 > +#define OASTARTTRIG8_NOA_SELECT_3_SHIFT 12 > +#define OASTARTTRIG8_NOA_SELECT_4_SHIFT 16 > +#define OASTARTTRIG8_NOA_SELECT_5_SHIFT 20 > +#define OASTARTTRIG8_NOA_SELECT_6_SHIFT 24 > +#define OASTARTTRIG8_NOA_SELECT_7_SHIFT 28 > + > +/* CECX_0 */ > +#define OACEC_COMPARE_LESS_OR_EQUAL 6 > +#define OACEC_COMPARE_NOT_EQUAL 5 > +#define OACEC_COMPARE_LESS_THAN 4 > +#define OACEC_COMPARE_GREATER_OR_EQUAL 3 > +#define OACEC_COMPARE_EQUAL 2 > +#define OACEC_COMPARE_GREATER_THAN 1 > +#define OACEC_COMPARE_ANY_EQUAL 0 > + > +#define OACEC_COMPARE_VALUE_MASK 0xffff > +#define OACEC_COMPARE_VALUE_SHIFT 3 > + > +#define OACEC_SELECT_NOA (0<<19) > +#define OACEC_SELECT_PREV (1<<19) > +#define OACEC_SELECT_BOOLEAN (2<<19) > + > +/* CECX_1 */ > +#define OACEC_MASK_MASK 0xffff > +#define OACEC_CONSIDERATIONS_MASK 0xffff > +#define OACEC_CONSIDERATIONS_SHIFT 16 > + > +#define OACEC0_0 _MMIO(0x2770) > +#define OACEC0_1 _MMIO(0x2774) > +#define OACEC1_0 _MMIO(0x2778) > +#define OACEC1_1 _MMIO(0x277c) > +#define OACEC2_0 _MMIO(0x2780) > +#define OACEC2_1 _MMIO(0x2784) > +#define OACEC3_0 _MMIO(0x2788) > +#define OACEC3_1 _MMIO(0x278c) > +#define OACEC4_0 _MMIO(0x2790) > +#define OACEC4_1 _MMIO(0x2794) > +#define OACEC5_0 _MMIO(0x2798) > +#define OACEC5_1 _MMIO(0x279c) > +#define OACEC6_0 _MMIO(0x27a0) > +#define OACEC6_1 _MMIO(0x27a4) > +#define OACEC7_0 _MMIO(0x27a8) > +#define OACEC7_1 _MMIO(0x27ac) > + > > #define _GEN7_PIPEA_DE_LOAD_SL 0x70068 > #define _GEN7_PIPEB_DE_LOAD_SL 0x71068 > @@ -6993,6 +7330,7 @@ enum { > # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11) > > #define GEN6_UCGCTL3 _MMIO(0x9408) > +# define GEN6_OACSUNIT_CLOCK_GATE_DISABLE (1 << 20) > > #define GEN7_UCGCTL4 _MMIO(0x940c) > #define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25) > diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h > index 98cd493..82d6239 100644 > --- a/include/uapi/drm/i915_drm.h > +++ b/include/uapi/drm/i915_drm.h > @@ -1224,6 +1224,18 @@ struct drm_i915_gem_context_param { > __u64 value; > }; > > +enum drm_i915_oa_format { > + I915_OA_FORMAT_A13 = 1, > + I915_OA_FORMAT_A29, > + I915_OA_FORMAT_A13_B8_C8, > + I915_OA_FORMAT_B4_C8, > + I915_OA_FORMAT_A45_B8_C8, > + I915_OA_FORMAT_B4_C8_A16, > + I915_OA_FORMAT_C4_B8, > + > + I915_OA_FORMAT_MAX /* non-ABI */ > +}; > + > enum drm_i915_perf_property_id { > /** > * Open the stream for a specific context handle (as used with > @@ -1232,6 +1244,32 @@ enum drm_i915_perf_property_id { > */ > DRM_I915_PERF_PROP_CTX_HANDLE = 1, > > + /** > + * A value of 1 requests the inclusion of raw OA unit reports as > + * part of stream samples. > + */ > + DRM_I915_PERF_PROP_SAMPLE_OA, > + > + /** > + * The value specifies which set of OA unit metrics should be > + * be configured, defining the contents of any OA unit reports. > + */ > + DRM_I915_PERF_PROP_OA_METRICS_SET, > + > + /** > + * The value specifies the size and layout of OA unit reports. > + */ > + DRM_I915_PERF_PROP_OA_FORMAT, > + > + /** > + * Specifying this property implicitly requests periodic OA unit > + * sampling and (at least on Haswell) the sampling frequency is derived > + * from this exponent as follows: > + * > + * 80ns * 2^(period_exponent + 1) > + */ > + DRM_I915_PERF_PROP_OA_EXPONENT, > + > DRM_I915_PERF_PROP_MAX /* non-ABI */ > }; > > @@ -1251,7 +1289,23 @@ struct drm_i915_perf_open_param { > __u64 __user properties_ptr; > }; > > +/** > + * Enable data capture for a stream that was either opened in a disabled state > + * via I915_PERF_FLAG_DISABLED or was later disabled via > + * I915_PERF_IOCTL_DISABLE. > + * > + * It is intended to be cheaper to disable and enable a stream than it may be > + * to close and re-open a stream with the same configuration. > + * > + * It's undefined whether any pending data for the stream will be lost. > + */ > #define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) > + > +/** > + * Disable data capture for a stream. > + * > + * It is an error to try and read a stream that is disabled. > + */ > #define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) > > /** > @@ -1275,17 +1329,30 @@ enum drm_i915_perf_record_type { > * every sample. > * > * The order of these sample properties given by userspace has no > - * affect on the ordering of data within a sample. The order will be > + * affect on the ordering of data within a sample. The order is > * documented here. > * > * struct { > * struct drm_i915_perf_record_header header; > * > - * TODO: itemize extensible sample data here > + * { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA > * }; > */ > DRM_I915_PERF_RECORD_SAMPLE = 1, > > + /* > + * Indicates that one or more OA reports were not written by the > + * hardware. This can happen for example if an MI_REPORT_PERF_COUNT > + * command collides with periodic sampling - which would be more likely > + * at higher sampling frequencies. > + */ > + DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2, > + > + /** > + * An error occurred that resulted in all pending OA reports being lost. > + */ > + DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3, > + > DRM_I915_PERF_RECORD_MAX /* non-ABI */ > }; > > -- > 2.10.1 > Otherwise I think we should be good, so assuming you also folded in all of Chris' comments: Reviewed-by: Matthew Auld <matthew.auld@intel.com>
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f22adc4..dd2b4d3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1767,6 +1767,11 @@ struct intel_wm_config { bool sprites_scaled; }; +struct i915_oa_format { + u32 format; + int size; +}; + struct i915_oa_reg { i915_reg_t addr; u32 value; @@ -1787,11 +1792,6 @@ struct i915_perf_stream_ops { */ void (*disable)(struct i915_perf_stream *stream); - /* Return: true if any i915 perf records are ready to read() - * for this stream. - */ - bool (*can_read)(struct i915_perf_stream *stream); - /* Call poll_wait, passing a wait queue that will be woken * once there is something ready to read() for the stream */ @@ -1801,9 +1801,7 @@ struct i915_perf_stream_ops { /* For handling a blocking read, wait until there is something * to ready to read() for the stream. E.g. wait on the same - * wait queue that would be passed to poll_wait() until - * ->can_read() returns true (if its safe to call ->can_read() - * without the i915 perf lock held). + * wait queue that would be passed to poll_wait(). */ int (*wait_unlocked)(struct i915_perf_stream *stream); @@ -1843,11 +1841,28 @@ struct i915_perf_stream { struct list_head link; u32 sample_flags; + int sample_size; struct i915_gem_context *ctx; bool enabled; - struct i915_perf_stream_ops *ops; + const struct i915_perf_stream_ops *ops; +}; + +struct i915_oa_ops { + void (*init_oa_buffer)(struct drm_i915_private *dev_priv); + int (*enable_metric_set)(struct drm_i915_private *dev_priv); + void (*disable_metric_set)(struct drm_i915_private *dev_priv); + void (*oa_enable)(struct drm_i915_private *dev_priv); + void (*oa_disable)(struct drm_i915_private *dev_priv); + void (*update_oacontrol)(struct drm_i915_private *dev_priv); + void (*update_hw_ctx_id_locked)(struct drm_i915_private *dev_priv, + u32 ctx_id); + int (*read)(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset); + bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv); }; struct drm_i915_private { @@ -2154,16 +2169,47 @@ struct drm_i915_private { struct { bool initialized; + struct mutex lock; struct list_head streams; + spinlock_t hook_lock; + struct { - u32 metrics_set; + struct i915_perf_stream *exclusive_stream; + + u32 specific_ctx_id; + struct i915_vma *pinned_rcs_vma; + + struct hrtimer poll_check_timer; + wait_queue_head_t poll_wq; + bool pollin; + + bool periodic; + int period_exponent; + int timestamp_frequency; + + int tail_margin; + + int metrics_set; const struct i915_oa_reg *mux_regs; int mux_regs_len; const struct i915_oa_reg *b_counter_regs; int b_counter_regs_len; + + struct { + struct i915_vma *vma; + u8 *vaddr; + int format; + int format_size; + } oa_buffer; + + u32 gen7_latched_oastatus1; + + struct i915_oa_ops ops; + const struct i915_oa_format *oa_formats; + int n_builtin_sets; } oa; } perf; diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index c45cf92..8b9cf0d 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -25,16 +25,899 @@ */ #include <linux/anon_inodes.h> +#include <linux/sizes.h> #include "i915_drv.h" +#include "i915_oa_hsw.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 + * that the overflow cases are unlikely in normal operation. + */ +#define OA_BUFFER_SIZE SZ_16M + +#define OA_TAKEN(tail, head) ((tail - head) & (OA_BUFFER_SIZE - 1)) + +/* There's a HW race condition between OA unit tail pointer register updates and + * writes to memory whereby the tail pointer can sometimes get ahead of what's + * been written out to the OA buffer so far. + * + * Although this can be observed explicitly by checking for a zeroed report-id + * field in tail reports, it seems preferable to account for this earlier e.g. + * as part of the _oa_buffer_is_empty checks to minimize -EAGAIN polling cycles + * in this situation. + * + * To give time for the most recent reports to land before they may be copied to + * userspace, the driver operates as if the tail pointer effectively lags behind + * the HW tail pointer by 'tail_margin' bytes. The margin in bytes is calculated + * based on this constant in nanoseconds, the current OA sampling exponent + * and current report size. + * + * There is also a fallback check while reading to simply skip over reports with + * a zeroed report-id. + */ +#define OA_TAIL_MARGIN_NSEC 100000ULL + +/* frequency for checking whether the OA unit has written new reports to the + * circular OA buffer... + */ +#define POLL_FREQUENCY 200 +#define POLL_PERIOD (NSEC_PER_SEC / POLL_FREQUENCY) + +/* The maximum exponent the hardware accepts is 63 (essentially it selects one + * of the 64bit timestamp bits to trigger reports from) but there's currently + * no known use case for sampling as infrequently as once per 47 thousand years. + * + * Since the timestamps included in OA reports are only 32bits it seems + * reasonable to limit the OA exponent where it's still possible to account for + * overflow in OA report timestamps. + */ +#define OA_EXPONENT_MAX 31 + +#define INVALID_CTX_ID 0xffffffff + + +/* XXX: beware if future OA HW adds new report formats that the current + * code assumes all reports have a power-of-two size and ~(size - 1) can + * be used as a mask to align the OA tail pointer. + */ +static struct i915_oa_format hsw_oa_formats[I915_OA_FORMAT_MAX] = { + [I915_OA_FORMAT_A13] = { 0, 64 }, + [I915_OA_FORMAT_A29] = { 1, 128 }, + [I915_OA_FORMAT_A13_B8_C8] = { 2, 128 }, + /* A29_B8_C8 Disallowed as 192 bytes doesn't factor into buffer size */ + [I915_OA_FORMAT_B4_C8] = { 4, 64 }, + [I915_OA_FORMAT_A45_B8_C8] = { 5, 256 }, + [I915_OA_FORMAT_B4_C8_A16] = { 6, 128 }, + [I915_OA_FORMAT_C4_B8] = { 7, 64 }, +}; + +#define SAMPLE_OA_REPORT (1<<0) struct perf_open_properties { u32 sample_flags; u64 single_context:1; u64 ctx_handle; + + /* OA sampling state */ + int metrics_set; + int oa_format; + bool oa_periodic; + int oa_period_exponent; +}; + +/* NB: This is either called via fops or the poll check hrtimer (atomic ctx) + * + * It's safe to read OA config state here unlocked, assuming that this is only + * called while the stream is enabled, while the global OA configuration can't + * be modified. + * + * Note: we don't lock around the head/tail reads even though there's the slim + * possibility of read() fop errors forcing a re-init of the OA buffer + * pointers. A race here could result in a false positive !empty status which + * is acceptable. + */ +static bool gen7_oa_buffer_is_empty_fop_unlocked(struct drm_i915_private *dev_priv) +{ + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u32 oastatus2 = I915_READ(GEN7_OASTATUS2); + u32 oastatus1 = I915_READ(GEN7_OASTATUS1); + u32 head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + + return OA_TAKEN(tail, head) < + dev_priv->perf.oa.tail_margin + report_size; +} + +/** + * Appends a status record to a userspace read() buffer. + */ +static int append_oa_status(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + enum drm_i915_perf_record_type type) +{ + struct drm_i915_perf_record_header header = { type, 0, sizeof(header) }; + + if ((count - *offset) < header.size) + return -ENOSPC; + + if (copy_to_user(buf + *offset, &header, sizeof(header))) + return -EFAULT; + + (*offset) += header.size; + + return 0; +} + +/** + * Copies single OA report into userspace read() buffer. + */ +static int append_oa_sample(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + const u8 *report) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + struct drm_i915_perf_record_header header; + u32 sample_flags = stream->sample_flags; + + header.type = DRM_I915_PERF_RECORD_SAMPLE; + header.pad = 0; + header.size = stream->sample_size; + + if ((count - *offset) < header.size) + return -ENOSPC; + + buf += *offset; + if (copy_to_user(buf, &header, sizeof(header))) + return -EFAULT; + buf += sizeof(header); + + if (sample_flags & SAMPLE_OA_REPORT) { + if (copy_to_user(buf, report, report_size)) + return -EFAULT; + } + + (*offset) += header.size; + + return 0; +} + +/** + * Copies all buffered OA reports into userspace read() buffer. + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * @head_ptr: (inout): the current oa buffer cpu read position + * @tail_ptr: the current oa buffer gpu write position + * + * Returns 0 on success, negative error code on failure. + * + * Notably any error condition resulting in a short read (-ENOSPC or + * -EFAULT) will be returned even though one or more records may + * have been successfully copied. In this case it's up to the caller + * to decide if the error should be squashed before returning to + * userspace. + * + * Note: reports are consumed from the head, and appended to the + * tail, so the head chases the tail?... If you think that's mad + * and back-to-front you're not alone, but this follows the + * Gen PRM naming convention. + */ +static int gen7_append_oa_reports(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + u32 *head_ptr, + u32 tail) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.vaddr; + int tail_margin = dev_priv->perf.oa.tail_margin; + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); + u32 mask = (OA_BUFFER_SIZE - 1); + u32 head; + u32 taken; + int ret = 0; + + + if (WARN_ON(!stream->enabled)) + return -EIO; + + head = *head_ptr - gtt_offset; + tail -= gtt_offset; + + /* The OA unit is expected to wrap the tail pointer according to the OA + * buffer size and since we should never write a misaligned head + * pointer we don't expect to read one back either... + */ + if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE || + head % report_size) { + DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart", + head, tail); + dev_priv->perf.oa.ops.oa_disable(dev_priv); + dev_priv->perf.oa.ops.oa_enable(dev_priv); + *head_ptr = I915_READ(GEN7_OASTATUS2) & + GEN7_OASTATUS2_HEAD_MASK; + return -EIO; + } + + + /* The tail pointer increases in 64 byte increments, not in report_size + * steps... + */ + tail &= ~(report_size - 1); + + /* Move the tail pointer back by the current tail_margin to account for + * the possibility that the latest reports may not have really landed + * in memory yet... + */ + + if (OA_TAKEN(tail, head) < report_size + tail_margin) + return -EAGAIN; + + tail -= tail_margin; + tail &= mask; + + for (/* none */; + (taken = OA_TAKEN(tail, head)); + head = (head + report_size) & mask) { + u8 *report = oa_buf_base + head; + u32 *report32 = (void *)report; + + /* All the report sizes factor neatly into the buffer + * size so we never expect to see a report split + * between the beginning and end of the buffer. + * + * Given the initial alignment check a misalignment + * here would imply a driver bug that would result + * in an overrun. + */ + if (WARN_ON((OA_BUFFER_SIZE - head) < report_size)) { + DRM_ERROR("Spurious OA head ptr: non-integral report offset\n"); + break; + } + + /* The report-ID field for periodic samples includes + * some undocumented flags related to what triggered + * the report and is never expected to be zero so we + * can check that the report isn't invalid before + * copying it to userspace... + */ + if (report32[0] == 0) { + DRM_ERROR("Skipping spurious, invalid OA report\n"); + continue; + } + + ret = append_oa_sample(stream, buf, count, offset, report); + if (ret) + break; + + /* The above report-id field sanity check is based on + * the assumption that the OA buffer is initially + * zeroed and we reset the field after copying so the + * check is still meaningful once old reports start + * being overwritten. + */ + report32[0] = 0; + } + + *head_ptr = gtt_offset + head; + + return ret; +} + +static int gen7_oa_read(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u32 oastatus2; + u32 oastatus1; + u32 head; + u32 tail; + int ret; + + if (WARN_ON(!dev_priv->perf.oa.oa_buffer.vaddr)) + return -EIO; + + oastatus2 = I915_READ(GEN7_OASTATUS2); + oastatus1 = I915_READ(GEN7_OASTATUS1); + + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + + /* XXX: On Haswell we don't have a safe way to clear oastatus1 + * bits while the OA unit is enabled (while the tail pointer + * may be updated asynchronously) so we ignore status bits + * that have already been reported to userspace. + */ + oastatus1 &= ~dev_priv->perf.oa.gen7_latched_oastatus1; + + /* We treat OABUFFER_OVERFLOW as a significant error: + * + * - The status can be interpreted to mean that the buffer is + * currently full (with a higher precedence than OA_TAKEN() + * which will start to report a near-empty buffer after an + * overflow) but it's awkward that we can't clear the status + * on Haswell, so without a reset we won't be able to catch + * the state again. + * + * - Since it also implies the HW has started overwriting old + * reports it may also affect our sanity checks for invalid + * reports when copying to userspace that assume new reports + * are being written to cleared memory. + * + * - In the future we may want to introduce a flight recorder + * mode where the driver will automatically maintain a safe + * guard band between head/tail, avoiding this overflow + * condition, but we avoid the added driver complexity for + * now. + */ + if (unlikely(oastatus1 & GEN7_OASTATUS1_OABUFFER_OVERFLOW)) { + ret = append_oa_status(stream, buf, count, offset, + DRM_I915_PERF_RECORD_OA_BUFFER_LOST); + if (ret) + return ret; + + DRM_ERROR("OA buffer overflow: force restart"); + + dev_priv->perf.oa.ops.oa_disable(dev_priv); + dev_priv->perf.oa.ops.oa_enable(dev_priv); + + oastatus2 = I915_READ(GEN7_OASTATUS2); + oastatus1 = I915_READ(GEN7_OASTATUS1); + + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + } + + if (unlikely(oastatus1 & GEN7_OASTATUS1_REPORT_LOST)) { + ret = append_oa_status(stream, buf, count, offset, + DRM_I915_PERF_RECORD_OA_REPORT_LOST); + if (ret) + return ret; + dev_priv->perf.oa.gen7_latched_oastatus1 |= + GEN7_OASTATUS1_REPORT_LOST; + } + + ret = gen7_append_oa_reports(stream, buf, count, offset, + &head, tail); + + /* All the report sizes are a power of two and the + * head should always be incremented by some multiple + * of the report size. + * + * A warning here, but notably if we later read back a + * misaligned pointer we will treat that as a bug since + * it could lead to a buffer overrun. + */ + WARN_ONCE(head & (report_size - 1), + "i915: Writing misaligned OA head pointer"); + + /* Note: we update the head pointer here even if an error + * was returned since the error may represent a short read + * where some some reports were successfully copied. + */ + I915_WRITE(GEN7_OASTATUS2, + ((head & GEN7_OASTATUS2_HEAD_MASK) | + OA_MEM_SELECT_GGTT)); + + return ret; +} + +static int i915_oa_wait_unlocked(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + /* We would wait indefinitly if periodic sampling is not enabled */ + if (!dev_priv->perf.oa.periodic) + return -EIO; + + /* Note: the oa_buffer_is_empty() condition is ok to run unlocked as it + * just performs mmio reads of the OA buffer head + tail pointers and + * it's assumed we're handling some operation that implies the stream + * can't be destroyed until completion (such as a read()) that ensures + * the device + OA buffer can't disappear + */ + return wait_event_interruptible(dev_priv->perf.oa.poll_wq, + !dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)); +} + +static void i915_oa_poll_wait(struct i915_perf_stream *stream, + struct file *file, + poll_table *wait) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + poll_wait(file, &dev_priv->perf.oa.poll_wq, wait); +} + +static int i915_oa_read(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + return dev_priv->perf.oa.ops.read(stream, buf, count, offset); +} + +/* Determine the render context hw id, and ensure it remains fixed for the + * lifetime of the stream. This ensures that we don't have to worry about + * updating the context ID in OACONTROL on the fly. + */ +static int oa_get_render_ctx_id(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + struct i915_vma *vma; + int ret; + + ret = i915_mutex_lock_interruptible(&dev_priv->drm); + if (ret) + return ret; + + /* As the ID is the gtt offset of the context's vma we pin + * the vma to ensure the ID remains fixed. + * + * NB: implied RCS engine... + */ + vma = i915_gem_context_pin_legacy(stream->ctx); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto unlock; + } + + dev_priv->perf.oa.pinned_rcs_vma = vma; + + /* Explicitly track the ID (instead of calling i915_ggtt_offset() + * on the fly) considering the difference with gen8+ and + * execlists + */ + dev_priv->perf.oa.specific_ctx_id = i915_ggtt_offset(vma); + +unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); + + return ret; +} + +static void oa_put_render_ctx_id(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + mutex_lock(&dev_priv->drm.struct_mutex); + + i915_vma_unpin(dev_priv->perf.oa.pinned_rcs_vma); + dev_priv->perf.oa.pinned_rcs_vma = NULL; + + dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID; + + mutex_unlock(&dev_priv->drm.struct_mutex); +} + +static void +free_oa_buffer(struct drm_i915_private *i915) +{ + mutex_lock(&i915->drm.struct_mutex); + + i915_gem_object_unpin_map(i915->perf.oa.oa_buffer.vma->obj); + i915_vma_unpin(i915->perf.oa.oa_buffer.vma); + i915_gem_object_put(i915->perf.oa.oa_buffer.vma->obj); + + i915->perf.oa.oa_buffer.vma = NULL; + i915->perf.oa.oa_buffer.vaddr = NULL; + + mutex_unlock(&i915->drm.struct_mutex); +} + +static void i915_oa_stream_destroy(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + BUG_ON(stream != dev_priv->perf.oa.exclusive_stream); + + dev_priv->perf.oa.ops.disable_metric_set(dev_priv); + + free_oa_buffer(dev_priv); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + + if (stream->ctx) + oa_put_render_ctx_id(stream); + + dev_priv->perf.oa.exclusive_stream = NULL; +} + +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv) +{ + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); + + /* Pre-DevBDW: OABUFFER must be set with counters off, + * before OASTATUS1, but after OASTATUS2 + */ + I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */ + I915_WRITE(GEN7_OABUFFER, gtt_offset); + I915_WRITE(GEN7_OASTATUS1, gtt_offset | OABUFFER_SIZE_16M); /* tail */ + + /* On Haswell we have to track which OASTATUS1 flags we've + * already seen since they can't be cleared while periodic + * sampling is enabled. + */ + dev_priv->perf.oa.gen7_latched_oastatus1 = 0; + + /* NB: although the OA buffer will initially be allocated + * zeroed via shmfs (and so this memset is redundant when + * first allocating), we may re-init the OA buffer, either + * when re-enabling a stream or in error/reset paths. + * + * The reason we clear the buffer for each re-init is for the + * sanity check in gen7_append_oa_reports() that looks at the + * report-id field to make sure it's non-zero which relies on + * the assumption that new reports are being written to zeroed + * memory... + */ + memset(dev_priv->perf.oa.oa_buffer.vaddr, 0, OA_BUFFER_SIZE); + + /* Maybe make ->pollin per-stream state if we support multiple + * concurrent streams in the future. + */ + dev_priv->perf.oa.pollin = false; +} + +static int alloc_oa_buffer(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *bo; + struct i915_vma *vma; + int ret; + + if (WARN_ON(dev_priv->perf.oa.oa_buffer.vma)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(&dev_priv->drm); + if (ret) + return ret; + + BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE); + BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M); + + bo = i915_gem_object_create(&dev_priv->drm, OA_BUFFER_SIZE); + if (IS_ERR(bo)) { + DRM_ERROR("Failed to allocate OA buffer\n"); + ret = PTR_ERR(bo); + goto unlock; + } + + ret = i915_gem_object_set_cache_level(bo, I915_CACHE_LLC); + if (ret) + goto err_unref; + + /* PreHSW required 512K alignment, HSW requires 16M */ + vma = i915_gem_object_ggtt_pin(bo, NULL, 0, SZ_16M, 0); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto err_unref; + } + dev_priv->perf.oa.oa_buffer.vma = vma; + + dev_priv->perf.oa.oa_buffer.vaddr = + i915_gem_object_pin_map(bo, I915_MAP_WB); + if (IS_ERR(dev_priv->perf.oa.oa_buffer.vaddr)) { + ret = PTR_ERR(dev_priv->perf.oa.oa_buffer.vaddr); + goto err_unpin; + } + + dev_priv->perf.oa.ops.init_oa_buffer(dev_priv); + + DRM_DEBUG_DRIVER("OA Buffer initialized, gtt offset = 0x%x, vaddr = %p", + i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma), + dev_priv->perf.oa.oa_buffer.vaddr); + + goto unlock; + +err_unpin: + __i915_vma_unpin(vma); + +err_unref: + i915_gem_object_put(bo); + + dev_priv->perf.oa.oa_buffer.vaddr = NULL; + dev_priv->perf.oa.oa_buffer.vma = NULL; + +unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); + return ret; +} + +static void config_oa_regs(struct drm_i915_private *dev_priv, + const struct i915_oa_reg *regs, + int n_regs) +{ + int i; + + for (i = 0; i < n_regs; i++) { + const struct i915_oa_reg *reg = regs + i; + + I915_WRITE(reg->addr, reg->value); + } +} + +static int hsw_enable_metric_set(struct drm_i915_private *dev_priv) +{ + int ret = i915_oa_select_metric_set_hsw(dev_priv); + + if (ret) + return ret; + + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) | + GT_NOA_ENABLE)); + + /* PRM: + * + * OA unit is using “crclk” for its functionality. When trunk + * level clock gating takes place, OA clock would be gated, + * unable to count the events from non-render clock domain. + * Render clock gating must be disabled when OA is enabled to + * count the events from non-render domain. Unit level clock + * gating for RCS should also be disabled. + */ + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) & + ~GEN7_DOP_CLOCK_GATE_ENABLE)); + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) | + GEN6_CSUNIT_CLOCK_GATE_DISABLE)); + + config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs, + dev_priv->perf.oa.mux_regs_len); + + /* It apparently takes a fairly long time for a new MUX + * configuration to be be applied after these register writes. + * This delay duration was derived empirically based on the + * render_basic config but hopefully it covers the maximum + * configuration latency. + * + * As a fallback, the checks in _append_oa_reports() to skip + * invalid OA reports do also seem to work to discard reports + * generated before this config has completed - albeit not + * silently. + * + * Unfortunately this is essentially a magic number, since we + * don't currently know of a reliable mechanism for predicting + * how long the MUX config will take to apply and besides + * seeing invalid reports we don't know of a reliable way to + * explicitly check that the MUX config has landed. + * + * It's even possible we've miss characterized the underlying + * problem - it just seems like the simplest explanation why + * a delay at this location would mitigate any invalid reports. + */ + usleep_range(15000, 20000); + + config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs, + dev_priv->perf.oa.b_counter_regs_len); + + return 0; +} + +static void hsw_disable_metric_set(struct drm_i915_private *dev_priv) +{ + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) & + ~GEN6_CSUNIT_CLOCK_GATE_DISABLE)); + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) | + GEN7_DOP_CLOCK_GATE_ENABLE)); + + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) & + ~GT_NOA_ENABLE)); +} + +static void gen7_update_oacontrol_locked(struct drm_i915_private *dev_priv) +{ + assert_spin_locked(&dev_priv->perf.hook_lock); + + if (dev_priv->perf.oa.exclusive_stream->enabled) { + struct i915_gem_context *ctx = + dev_priv->perf.oa.exclusive_stream->ctx; + u32 ctx_id = dev_priv->perf.oa.specific_ctx_id; + + bool periodic = dev_priv->perf.oa.periodic; + u32 period_exponent = dev_priv->perf.oa.period_exponent; + u32 report_format = dev_priv->perf.oa.oa_buffer.format; + + I915_WRITE(GEN7_OACONTROL, + (ctx_id & GEN7_OACONTROL_CTX_MASK) | + (period_exponent << + GEN7_OACONTROL_TIMER_PERIOD_SHIFT) | + (periodic ? GEN7_OACONTROL_TIMER_ENABLE : 0) | + (report_format << GEN7_OACONTROL_FORMAT_SHIFT) | + (ctx ? GEN7_OACONTROL_PER_CTX_ENABLE : 0) | + GEN7_OACONTROL_ENABLE); + } else + I915_WRITE(GEN7_OACONTROL, 0); +} + +static void gen7_oa_enable(struct drm_i915_private *dev_priv) +{ + unsigned long flags; + + /* Reset buf pointers so we don't forward reports from before now. + * + * Think carefully if considering trying to avoid this, since it + * also ensures status flags and the buffer itself are cleared + * in error paths, and we have checks for invalid reports based + * on the assumption that certain fields are written to zeroed + * memory which this helps maintains. + */ + gen7_init_oa_buffer(dev_priv); + + spin_lock_irqsave(&dev_priv->perf.hook_lock, flags); + gen7_update_oacontrol_locked(dev_priv); + spin_unlock_irqrestore(&dev_priv->perf.hook_lock, flags); +} + +static void i915_oa_stream_enable(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + dev_priv->perf.oa.ops.oa_enable(dev_priv); + + if (dev_priv->perf.oa.periodic) + hrtimer_start(&dev_priv->perf.oa.poll_check_timer, + ns_to_ktime(POLL_PERIOD), + HRTIMER_MODE_REL_PINNED); +} + +static void gen7_oa_disable(struct drm_i915_private *dev_priv) +{ + I915_WRITE(GEN7_OACONTROL, 0); +} + +static void i915_oa_stream_disable(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + dev_priv->perf.oa.ops.oa_disable(dev_priv); + + if (dev_priv->perf.oa.periodic) + hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer); +} + +static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent) +{ + return 1000000000ULL * (2ULL << exponent) / + dev_priv->perf.oa.timestamp_frequency; +} + +static const struct i915_perf_stream_ops i915_oa_stream_ops = { + .destroy = i915_oa_stream_destroy, + .enable = i915_oa_stream_enable, + .disable = i915_oa_stream_disable, + .wait_unlocked = i915_oa_wait_unlocked, + .poll_wait = i915_oa_poll_wait, + .read = i915_oa_read, }; +static int i915_oa_stream_init(struct i915_perf_stream *stream, + struct drm_i915_perf_open_param *param, + struct perf_open_properties *props) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int format_size; + int ret; + + if (!(props->sample_flags & SAMPLE_OA_REPORT)) { + DRM_ERROR("Only OA report sampling supported\n"); + return -EINVAL; + } + + if (!dev_priv->perf.oa.ops.init_oa_buffer) { + DRM_ERROR("OA unit not supported\n"); + return -ENODEV; + } + + /* To avoid the complexity of having to accurately filter + * counter reports and marshal to the appropriate client + * we currently only allow exclusive access + */ + if (dev_priv->perf.oa.exclusive_stream) { + DRM_ERROR("OA unit already in use\n"); + return -EBUSY; + } + + if (!props->metrics_set) { + DRM_ERROR("OA metric set not specified\n"); + return -EINVAL; + } + + if (!props->oa_format) { + DRM_ERROR("OA report format not specified\n"); + return -EINVAL; + } + + stream->sample_size = sizeof(struct drm_i915_perf_record_header); + + format_size = dev_priv->perf.oa.oa_formats[props->oa_format].size; + + stream->sample_flags |= SAMPLE_OA_REPORT; + stream->sample_size += format_size; + + dev_priv->perf.oa.oa_buffer.format_size = format_size; + if (WARN_ON(dev_priv->perf.oa.oa_buffer.format_size == 0)) + return -EINVAL; + + dev_priv->perf.oa.oa_buffer.format = + dev_priv->perf.oa.oa_formats[props->oa_format].format; + + dev_priv->perf.oa.metrics_set = props->metrics_set; + + dev_priv->perf.oa.periodic = props->oa_periodic; + if (dev_priv->perf.oa.periodic) { + u64 period_ns = oa_exponent_to_ns(dev_priv, + props->oa_period_exponent); + + dev_priv->perf.oa.period_exponent = props->oa_period_exponent; + + /* See comment for OA_TAIL_MARGIN_NSEC for details + * about this tail_margin... + */ + dev_priv->perf.oa.tail_margin = + ((OA_TAIL_MARGIN_NSEC / period_ns) + 1) * format_size; + } + + if (stream->ctx) { + ret = oa_get_render_ctx_id(stream); + if (ret) + return ret; + } + + ret = alloc_oa_buffer(dev_priv); + if (ret) + goto err_oa_buf_alloc; + + /* PRM - observability performance counters: + * + * OACONTROL, performance counter enable, note: + * + * "When this bit is set, in order to have coherent counts, + * RC6 power state and trunk clock gating must be disabled. + * This can be achieved by programming MMIO registers as + * 0xA094=0 and 0xA090[31]=1" + * + * In our case we are expecting that taking pm + FORCEWAKE + * references will effectively disable RC6. + */ + intel_runtime_pm_get(dev_priv); + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv); + if (ret) + goto err_enable; + + stream->ops = &i915_oa_stream_ops; + + dev_priv->perf.oa.exclusive_stream = stream; + + return 0; + +err_enable: + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + free_oa_buffer(dev_priv); + +err_oa_buf_alloc: + if (stream->ctx) + oa_put_render_ctx_id(stream); + + return ret; +} + static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream, struct file *file, char __user *buf, @@ -78,8 +961,20 @@ static ssize_t i915_perf_read(struct file *file, struct drm_i915_private *dev_priv = stream->dev_priv; ssize_t ret; + /* To ensure it's handled consistently we simply treat all reads of a + * disabled stream as an error. In particular it might otherwise lead + * to a deadlock for blocking file descriptors... + */ + if (!stream->enabled) + return -EIO; + if (!(file->f_flags & O_NONBLOCK)) { - /* Allow false positives from stream->ops->wait_unlocked. + /* There's the small chance of false positives from + * stream->ops->wait_unlocked. + * + * E.g. with single context filtering since we only wait until + * oabuffer has >= 1 report we don't immediately know whether + * any reports really belong to the current context */ do { ret = stream->ops->wait_unlocked(stream); @@ -97,21 +992,51 @@ static ssize_t i915_perf_read(struct file *file, mutex_unlock(&dev_priv->perf.lock); } + if (ret >= 0) { + /* Maybe make ->pollin per-stream state if we support multiple + * concurrent streams in the future. + */ + dev_priv->perf.oa.pollin = false; + } + return ret; } -static unsigned int i915_perf_poll_locked(struct i915_perf_stream *stream, +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer) +{ + struct drm_i915_private *dev_priv = + container_of(hrtimer, typeof(*dev_priv), + perf.oa.poll_check_timer); + + if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)) { + dev_priv->perf.oa.pollin = true; + wake_up(&dev_priv->perf.oa.poll_wq); + } + + hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD)); + + return HRTIMER_RESTART; +} + +static unsigned int i915_perf_poll_locked(struct drm_i915_private *dev_priv, + struct i915_perf_stream *stream, struct file *file, poll_table *wait) { - unsigned int streams = 0; + unsigned int events = 0; stream->ops->poll_wait(stream, file, wait); - if (stream->ops->can_read(stream)) - streams |= POLLIN; + /* Note: we don't explicitly check whether there's something to read + * here since this path may be very hot depending on what else + * userspace is polling, or on the timeout in use. We rely solely on + * the hrtimer/oa_poll_check_timer_cb to notify us when there are + * samples to read. + */ + if (dev_priv->perf.oa.pollin) + events |= POLLIN; - return streams; + return events; } static unsigned int i915_perf_poll(struct file *file, poll_table *wait) @@ -121,7 +1046,7 @@ static unsigned int i915_perf_poll(struct file *file, poll_table *wait) int ret; mutex_lock(&dev_priv->perf.lock); - ret = i915_perf_poll_locked(stream, file, wait); + ret = i915_perf_poll_locked(dev_priv, stream, file, wait); mutex_unlock(&dev_priv->perf.lock); return ret; @@ -285,18 +1210,21 @@ i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv, goto err_ctx; } - stream->sample_flags = props->sample_flags; stream->dev_priv = dev_priv; stream->ctx = specific_ctx; - /* - * TODO: support sampling something - * - * For now this is as far as we can go. + ret = i915_oa_stream_init(stream, param, props); + if (ret) + goto err_alloc; + + /* we avoid simply assigning stream->sample_flags = props->sample_flags + * to have _stream_init check the combination of sample flags more + * thoroughly, but still this is the expected result at this point. */ - DRM_ERROR("Unsupported i915 perf stream configuration\n"); - ret = -EINVAL; - goto err_alloc; + if (WARN_ON(stream->sample_flags != props->sample_flags)) { + ret = -ENODEV; + goto err_alloc; + } list_add(&stream->link, &dev_priv->perf.streams); @@ -376,6 +1304,56 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv, props->single_context = 1; props->ctx_handle = value; break; + case DRM_I915_PERF_PROP_SAMPLE_OA: + props->sample_flags |= SAMPLE_OA_REPORT; + break; + case DRM_I915_PERF_PROP_OA_METRICS_SET: + if (value == 0 || + value > dev_priv->perf.oa.n_builtin_sets) { + DRM_ERROR("Unknown OA metric set ID"); + return -EINVAL; + } + props->metrics_set = value; + break; + case DRM_I915_PERF_PROP_OA_FORMAT: + if (value == 0 || value >= I915_OA_FORMAT_MAX) { + DRM_ERROR("Invalid OA report format\n"); + return -EINVAL; + } + if (!dev_priv->perf.oa.oa_formats[value].size) { + DRM_ERROR("Invalid OA report format\n"); + return -EINVAL; + } + props->oa_format = value; + break; + case DRM_I915_PERF_PROP_OA_EXPONENT: + if (value > OA_EXPONENT_MAX) { + DRM_ERROR("OA timer exponent too high (> %u)\n", + OA_EXPONENT_MAX); + return -EINVAL; + } + + /* NB: The exponent represents a period as follows: + * + * 80ns * 2^(period_exponent + 1) + * + * Theoretically we can program the OA unit to sample + * every 160ns but don't allow that by default unless + * root. + * + * Referring to perf's + * kernel.perf_event_max_sample_rate for a precedent + * (100000 by default); with an OA exponent of 6 we get + * a period of 10.240 microseconds -just under 100000Hz + */ + if (value < 6 && !capable(CAP_SYS_ADMIN)) { + DRM_ERROR("Minimum OA sampling exponent is 6 without root privileges\n"); + return -EACCES; + } + + props->oa_periodic = true; + props->oa_period_exponent = value; + break; default: MISSING_CASE(id); DRM_ERROR("Unknown i915 perf property ID"); @@ -426,8 +1404,33 @@ int i915_perf_open_ioctl(struct drm_device *dev, void *data, void i915_perf_init(struct drm_i915_private *dev_priv) { + if (!IS_HASWELL(dev_priv)) + return; + + hrtimer_init(&dev_priv->perf.oa.poll_check_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb; + init_waitqueue_head(&dev_priv->perf.oa.poll_wq); + INIT_LIST_HEAD(&dev_priv->perf.streams); mutex_init(&dev_priv->perf.lock); + spin_lock_init(&dev_priv->perf.hook_lock); + + dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer; + dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set; + dev_priv->perf.oa.ops.disable_metric_set = hsw_disable_metric_set; + dev_priv->perf.oa.ops.oa_enable = gen7_oa_enable; + dev_priv->perf.oa.ops.oa_disable = gen7_oa_disable; + dev_priv->perf.oa.ops.read = gen7_oa_read; + dev_priv->perf.oa.ops.oa_buffer_is_empty = + gen7_oa_buffer_is_empty_fop_unlocked; + + dev_priv->perf.oa.timestamp_frequency = 12500000; + + dev_priv->perf.oa.oa_formats = hsw_oa_formats; + + dev_priv->perf.oa.n_builtin_sets = + i915_oa_n_builtin_metric_sets_hsw; dev_priv->perf.initialized = true; } @@ -437,7 +1440,6 @@ void i915_perf_fini(struct drm_i915_private *dev_priv) if (!dev_priv->perf.initialized) return; - /* Currently nothing to clean up */ - + memset(&dev_priv->perf.oa.ops, 0, sizeof(dev_priv->perf.oa.ops)); dev_priv->perf.initialized = false; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 59628d5..6e3b435 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -616,6 +616,343 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define HSW_CS_GPR_UDW(n) _MMIO(0x2600 + (n) * 8 + 4) #define GEN7_OACONTROL _MMIO(0x2360) +#define GEN7_OACONTROL_CTX_MASK 0xFFFFF000 +#define GEN7_OACONTROL_TIMER_PERIOD_MASK 0x3F +#define GEN7_OACONTROL_TIMER_PERIOD_SHIFT 6 +#define GEN7_OACONTROL_TIMER_ENABLE (1<<5) +#define GEN7_OACONTROL_FORMAT_A13 (0<<2) +#define GEN7_OACONTROL_FORMAT_A29 (1<<2) +#define GEN7_OACONTROL_FORMAT_A13_B8_C8 (2<<2) +#define GEN7_OACONTROL_FORMAT_A29_B8_C8 (3<<2) +#define GEN7_OACONTROL_FORMAT_B4_C8 (4<<2) +#define GEN7_OACONTROL_FORMAT_A45_B8_C8 (5<<2) +#define GEN7_OACONTROL_FORMAT_B4_C8_A16 (6<<2) +#define GEN7_OACONTROL_FORMAT_C4_B8 (7<<2) +#define GEN7_OACONTROL_FORMAT_SHIFT 2 +#define GEN7_OACONTROL_PER_CTX_ENABLE (1<<1) +#define GEN7_OACONTROL_ENABLE (1<<0) + +#define GEN8_OACTXID _MMIO(0x2364) + +#define GEN8_OACONTROL _MMIO(0x2B00) +#define GEN8_OA_REPORT_FORMAT_A12 (0<<2) +#define GEN8_OA_REPORT_FORMAT_A12_B8_C8 (2<<2) +#define GEN8_OA_REPORT_FORMAT_A36_B8_C8 (5<<2) +#define GEN8_OA_REPORT_FORMAT_C4_B8 (7<<2) +#define GEN8_OA_REPORT_FORMAT_SHIFT 2 +#define GEN8_OA_SPECIFIC_CONTEXT_ENABLE (1<<1) +#define GEN8_OA_COUNTER_ENABLE (1<<0) + +#define GEN8_OACTXCONTROL _MMIO(0x2360) +#define GEN8_OA_TIMER_PERIOD_MASK 0x3F +#define GEN8_OA_TIMER_PERIOD_SHIFT 2 +#define GEN8_OA_TIMER_ENABLE (1<<1) +#define GEN8_OA_COUNTER_RESUME (1<<0) + +#define GEN7_OABUFFER _MMIO(0x23B0) /* R/W */ +#define GEN7_OABUFFER_OVERRUN_DISABLE (1<<3) +#define GEN7_OABUFFER_EDGE_TRIGGER (1<<2) +#define GEN7_OABUFFER_STOP_RESUME_ENABLE (1<<1) +#define GEN7_OABUFFER_RESUME (1<<0) + +#define GEN8_OABUFFER _MMIO(0x2b14) + +#define GEN7_OASTATUS1 _MMIO(0x2364) +#define GEN7_OASTATUS1_TAIL_MASK 0xffffffc0 +#define GEN7_OASTATUS1_COUNTER_OVERFLOW (1<<2) +#define GEN7_OASTATUS1_OABUFFER_OVERFLOW (1<<1) +#define GEN7_OASTATUS1_REPORT_LOST (1<<0) + +#define GEN7_OASTATUS2 _MMIO(0x2368) +#define GEN7_OASTATUS2_HEAD_MASK 0xffffffc0 + +#define GEN8_OASTATUS _MMIO(0x2b08) +#define GEN8_OASTATUS_OVERRUN_STATUS (1<<3) +#define GEN8_OASTATUS_COUNTER_OVERFLOW (1<<2) +#define GEN8_OASTATUS_OABUFFER_OVERFLOW (1<<1) +#define GEN8_OASTATUS_REPORT_LOST (1<<0) + +#define GEN8_OAHEADPTR _MMIO(0x2B0C) +#define GEN8_OATAILPTR _MMIO(0x2B10) + +#define OABUFFER_SIZE_128K (0<<3) +#define OABUFFER_SIZE_256K (1<<3) +#define OABUFFER_SIZE_512K (2<<3) +#define OABUFFER_SIZE_1M (3<<3) +#define OABUFFER_SIZE_2M (4<<3) +#define OABUFFER_SIZE_4M (5<<3) +#define OABUFFER_SIZE_8M (6<<3) +#define OABUFFER_SIZE_16M (7<<3) + +#define OA_MEM_SELECT_GGTT (1<<0) + +#define EU_PERF_CNTL0 _MMIO(0xe458) + +#define GDT_CHICKEN_BITS _MMIO(0x9840) +#define GT_NOA_ENABLE 0x00000080 + +/* + * OA Boolean state + */ + +#define OAREPORTTRIG1 _MMIO(0x2740) +#define OAREPORTTRIG1_THRESHOLD_MASK 0xffff +#define OAREPORTTRIG1_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ + +#define OAREPORTTRIG2 _MMIO(0x2744) +#define OAREPORTTRIG2_INVERT_A_0 (1<<0) +#define OAREPORTTRIG2_INVERT_A_1 (1<<1) +#define OAREPORTTRIG2_INVERT_A_2 (1<<2) +#define OAREPORTTRIG2_INVERT_A_3 (1<<3) +#define OAREPORTTRIG2_INVERT_A_4 (1<<4) +#define OAREPORTTRIG2_INVERT_A_5 (1<<5) +#define OAREPORTTRIG2_INVERT_A_6 (1<<6) +#define OAREPORTTRIG2_INVERT_A_7 (1<<7) +#define OAREPORTTRIG2_INVERT_A_8 (1<<8) +#define OAREPORTTRIG2_INVERT_A_9 (1<<9) +#define OAREPORTTRIG2_INVERT_A_10 (1<<10) +#define OAREPORTTRIG2_INVERT_A_11 (1<<11) +#define OAREPORTTRIG2_INVERT_A_12 (1<<12) +#define OAREPORTTRIG2_INVERT_A_13 (1<<13) +#define OAREPORTTRIG2_INVERT_A_14 (1<<14) +#define OAREPORTTRIG2_INVERT_A_15 (1<<15) +#define OAREPORTTRIG2_INVERT_B_0 (1<<16) +#define OAREPORTTRIG2_INVERT_B_1 (1<<17) +#define OAREPORTTRIG2_INVERT_B_2 (1<<18) +#define OAREPORTTRIG2_INVERT_B_3 (1<<19) +#define OAREPORTTRIG2_INVERT_C_0 (1<<20) +#define OAREPORTTRIG2_INVERT_C_1 (1<<21) +#define OAREPORTTRIG2_INVERT_D_0 (1<<22) +#define OAREPORTTRIG2_THRESHOLD_ENABLE (1<<23) +#define OAREPORTTRIG2_REPORT_TRIGGER_ENABLE (1<<31) + +#define OAREPORTTRIG3 _MMIO(0x2748) +#define OAREPORTTRIG3_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG3_NOA_SELECT_8_SHIFT 0 +#define OAREPORTTRIG3_NOA_SELECT_9_SHIFT 4 +#define OAREPORTTRIG3_NOA_SELECT_10_SHIFT 8 +#define OAREPORTTRIG3_NOA_SELECT_11_SHIFT 12 +#define OAREPORTTRIG3_NOA_SELECT_12_SHIFT 16 +#define OAREPORTTRIG3_NOA_SELECT_13_SHIFT 20 +#define OAREPORTTRIG3_NOA_SELECT_14_SHIFT 24 +#define OAREPORTTRIG3_NOA_SELECT_15_SHIFT 28 + +#define OAREPORTTRIG4 _MMIO(0x274c) +#define OAREPORTTRIG4_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG4_NOA_SELECT_0_SHIFT 0 +#define OAREPORTTRIG4_NOA_SELECT_1_SHIFT 4 +#define OAREPORTTRIG4_NOA_SELECT_2_SHIFT 8 +#define OAREPORTTRIG4_NOA_SELECT_3_SHIFT 12 +#define OAREPORTTRIG4_NOA_SELECT_4_SHIFT 16 +#define OAREPORTTRIG4_NOA_SELECT_5_SHIFT 20 +#define OAREPORTTRIG4_NOA_SELECT_6_SHIFT 24 +#define OAREPORTTRIG4_NOA_SELECT_7_SHIFT 28 + +#define OAREPORTTRIG5 _MMIO(0x2750) +#define OAREPORTTRIG5_THRESHOLD_MASK 0xffff +#define OAREPORTTRIG5_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ + +#define OAREPORTTRIG6 _MMIO(0x2754) +#define OAREPORTTRIG6_INVERT_A_0 (1<<0) +#define OAREPORTTRIG6_INVERT_A_1 (1<<1) +#define OAREPORTTRIG6_INVERT_A_2 (1<<2) +#define OAREPORTTRIG6_INVERT_A_3 (1<<3) +#define OAREPORTTRIG6_INVERT_A_4 (1<<4) +#define OAREPORTTRIG6_INVERT_A_5 (1<<5) +#define OAREPORTTRIG6_INVERT_A_6 (1<<6) +#define OAREPORTTRIG6_INVERT_A_7 (1<<7) +#define OAREPORTTRIG6_INVERT_A_8 (1<<8) +#define OAREPORTTRIG6_INVERT_A_9 (1<<9) +#define OAREPORTTRIG6_INVERT_A_10 (1<<10) +#define OAREPORTTRIG6_INVERT_A_11 (1<<11) +#define OAREPORTTRIG6_INVERT_A_12 (1<<12) +#define OAREPORTTRIG6_INVERT_A_13 (1<<13) +#define OAREPORTTRIG6_INVERT_A_14 (1<<14) +#define OAREPORTTRIG6_INVERT_A_15 (1<<15) +#define OAREPORTTRIG6_INVERT_B_0 (1<<16) +#define OAREPORTTRIG6_INVERT_B_1 (1<<17) +#define OAREPORTTRIG6_INVERT_B_2 (1<<18) +#define OAREPORTTRIG6_INVERT_B_3 (1<<19) +#define OAREPORTTRIG6_INVERT_C_0 (1<<20) +#define OAREPORTTRIG6_INVERT_C_1 (1<<21) +#define OAREPORTTRIG6_INVERT_D_0 (1<<22) +#define OAREPORTTRIG6_THRESHOLD_ENABLE (1<<23) +#define OAREPORTTRIG6_REPORT_TRIGGER_ENABLE (1<<31) + +#define OAREPORTTRIG7 _MMIO(0x2758) +#define OAREPORTTRIG7_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG7_NOA_SELECT_8_SHIFT 0 +#define OAREPORTTRIG7_NOA_SELECT_9_SHIFT 4 +#define OAREPORTTRIG7_NOA_SELECT_10_SHIFT 8 +#define OAREPORTTRIG7_NOA_SELECT_11_SHIFT 12 +#define OAREPORTTRIG7_NOA_SELECT_12_SHIFT 16 +#define OAREPORTTRIG7_NOA_SELECT_13_SHIFT 20 +#define OAREPORTTRIG7_NOA_SELECT_14_SHIFT 24 +#define OAREPORTTRIG7_NOA_SELECT_15_SHIFT 28 + +#define OAREPORTTRIG8 _MMIO(0x275c) +#define OAREPORTTRIG8_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG8_NOA_SELECT_0_SHIFT 0 +#define OAREPORTTRIG8_NOA_SELECT_1_SHIFT 4 +#define OAREPORTTRIG8_NOA_SELECT_2_SHIFT 8 +#define OAREPORTTRIG8_NOA_SELECT_3_SHIFT 12 +#define OAREPORTTRIG8_NOA_SELECT_4_SHIFT 16 +#define OAREPORTTRIG8_NOA_SELECT_5_SHIFT 20 +#define OAREPORTTRIG8_NOA_SELECT_6_SHIFT 24 +#define OAREPORTTRIG8_NOA_SELECT_7_SHIFT 28 + +#define OASTARTTRIG1 _MMIO(0x2710) +#define OASTARTTRIG1_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 +#define OASTARTTRIG1_THRESHOLD_MASK 0xffff + +#define OASTARTTRIG2 _MMIO(0x2714) +#define OASTARTTRIG2_INVERT_A_0 (1<<0) +#define OASTARTTRIG2_INVERT_A_1 (1<<1) +#define OASTARTTRIG2_INVERT_A_2 (1<<2) +#define OASTARTTRIG2_INVERT_A_3 (1<<3) +#define OASTARTTRIG2_INVERT_A_4 (1<<4) +#define OASTARTTRIG2_INVERT_A_5 (1<<5) +#define OASTARTTRIG2_INVERT_A_6 (1<<6) +#define OASTARTTRIG2_INVERT_A_7 (1<<7) +#define OASTARTTRIG2_INVERT_A_8 (1<<8) +#define OASTARTTRIG2_INVERT_A_9 (1<<9) +#define OASTARTTRIG2_INVERT_A_10 (1<<10) +#define OASTARTTRIG2_INVERT_A_11 (1<<11) +#define OASTARTTRIG2_INVERT_A_12 (1<<12) +#define OASTARTTRIG2_INVERT_A_13 (1<<13) +#define OASTARTTRIG2_INVERT_A_14 (1<<14) +#define OASTARTTRIG2_INVERT_A_15 (1<<15) +#define OASTARTTRIG2_INVERT_B_0 (1<<16) +#define OASTARTTRIG2_INVERT_B_1 (1<<17) +#define OASTARTTRIG2_INVERT_B_2 (1<<18) +#define OASTARTTRIG2_INVERT_B_3 (1<<19) +#define OASTARTTRIG2_INVERT_C_0 (1<<20) +#define OASTARTTRIG2_INVERT_C_1 (1<<21) +#define OASTARTTRIG2_INVERT_D_0 (1<<22) +#define OASTARTTRIG2_THRESHOLD_ENABLE (1<<23) +#define OASTARTTRIG2_START_TRIG_FLAG_MBZ (1<<24) +#define OASTARTTRIG2_EVENT_SELECT_0 (1<<28) +#define OASTARTTRIG2_EVENT_SELECT_1 (1<<29) +#define OASTARTTRIG2_EVENT_SELECT_2 (1<<30) +#define OASTARTTRIG2_EVENT_SELECT_3 (1<<31) + +#define OASTARTTRIG3 _MMIO(0x2718) +#define OASTARTTRIG3_NOA_SELECT_MASK 0xf +#define OASTARTTRIG3_NOA_SELECT_8_SHIFT 0 +#define OASTARTTRIG3_NOA_SELECT_9_SHIFT 4 +#define OASTARTTRIG3_NOA_SELECT_10_SHIFT 8 +#define OASTARTTRIG3_NOA_SELECT_11_SHIFT 12 +#define OASTARTTRIG3_NOA_SELECT_12_SHIFT 16 +#define OASTARTTRIG3_NOA_SELECT_13_SHIFT 20 +#define OASTARTTRIG3_NOA_SELECT_14_SHIFT 24 +#define OASTARTTRIG3_NOA_SELECT_15_SHIFT 28 + +#define OASTARTTRIG4 _MMIO(0x271c) +#define OASTARTTRIG4_NOA_SELECT_MASK 0xf +#define OASTARTTRIG4_NOA_SELECT_0_SHIFT 0 +#define OASTARTTRIG4_NOA_SELECT_1_SHIFT 4 +#define OASTARTTRIG4_NOA_SELECT_2_SHIFT 8 +#define OASTARTTRIG4_NOA_SELECT_3_SHIFT 12 +#define OASTARTTRIG4_NOA_SELECT_4_SHIFT 16 +#define OASTARTTRIG4_NOA_SELECT_5_SHIFT 20 +#define OASTARTTRIG4_NOA_SELECT_6_SHIFT 24 +#define OASTARTTRIG4_NOA_SELECT_7_SHIFT 28 + +#define OASTARTTRIG5 _MMIO(0x2720) +#define OASTARTTRIG5_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 +#define OASTARTTRIG5_THRESHOLD_MASK 0xffff + +#define OASTARTTRIG6 _MMIO(0x2724) +#define OASTARTTRIG6_INVERT_A_0 (1<<0) +#define OASTARTTRIG6_INVERT_A_1 (1<<1) +#define OASTARTTRIG6_INVERT_A_2 (1<<2) +#define OASTARTTRIG6_INVERT_A_3 (1<<3) +#define OASTARTTRIG6_INVERT_A_4 (1<<4) +#define OASTARTTRIG6_INVERT_A_5 (1<<5) +#define OASTARTTRIG6_INVERT_A_6 (1<<6) +#define OASTARTTRIG6_INVERT_A_7 (1<<7) +#define OASTARTTRIG6_INVERT_A_8 (1<<8) +#define OASTARTTRIG6_INVERT_A_9 (1<<9) +#define OASTARTTRIG6_INVERT_A_10 (1<<10) +#define OASTARTTRIG6_INVERT_A_11 (1<<11) +#define OASTARTTRIG6_INVERT_A_12 (1<<12) +#define OASTARTTRIG6_INVERT_A_13 (1<<13) +#define OASTARTTRIG6_INVERT_A_14 (1<<14) +#define OASTARTTRIG6_INVERT_A_15 (1<<15) +#define OASTARTTRIG6_INVERT_B_0 (1<<16) +#define OASTARTTRIG6_INVERT_B_1 (1<<17) +#define OASTARTTRIG6_INVERT_B_2 (1<<18) +#define OASTARTTRIG6_INVERT_B_3 (1<<19) +#define OASTARTTRIG6_INVERT_C_0 (1<<20) +#define OASTARTTRIG6_INVERT_C_1 (1<<21) +#define OASTARTTRIG6_INVERT_D_0 (1<<22) +#define OASTARTTRIG6_THRESHOLD_ENABLE (1<<23) +#define OASTARTTRIG6_START_TRIG_FLAG_MBZ (1<<24) +#define OASTARTTRIG6_EVENT_SELECT_4 (1<<28) +#define OASTARTTRIG6_EVENT_SELECT_5 (1<<29) +#define OASTARTTRIG6_EVENT_SELECT_6 (1<<30) +#define OASTARTTRIG6_EVENT_SELECT_7 (1<<31) + +#define OASTARTTRIG7 _MMIO(0x2728) +#define OASTARTTRIG7_NOA_SELECT_MASK 0xf +#define OASTARTTRIG7_NOA_SELECT_8_SHIFT 0 +#define OASTARTTRIG7_NOA_SELECT_9_SHIFT 4 +#define OASTARTTRIG7_NOA_SELECT_10_SHIFT 8 +#define OASTARTTRIG7_NOA_SELECT_11_SHIFT 12 +#define OASTARTTRIG7_NOA_SELECT_12_SHIFT 16 +#define OASTARTTRIG7_NOA_SELECT_13_SHIFT 20 +#define OASTARTTRIG7_NOA_SELECT_14_SHIFT 24 +#define OASTARTTRIG7_NOA_SELECT_15_SHIFT 28 + +#define OASTARTTRIG8 _MMIO(0x272c) +#define OASTARTTRIG8_NOA_SELECT_MASK 0xf +#define OASTARTTRIG8_NOA_SELECT_0_SHIFT 0 +#define OASTARTTRIG8_NOA_SELECT_1_SHIFT 4 +#define OASTARTTRIG8_NOA_SELECT_2_SHIFT 8 +#define OASTARTTRIG8_NOA_SELECT_3_SHIFT 12 +#define OASTARTTRIG8_NOA_SELECT_4_SHIFT 16 +#define OASTARTTRIG8_NOA_SELECT_5_SHIFT 20 +#define OASTARTTRIG8_NOA_SELECT_6_SHIFT 24 +#define OASTARTTRIG8_NOA_SELECT_7_SHIFT 28 + +/* CECX_0 */ +#define OACEC_COMPARE_LESS_OR_EQUAL 6 +#define OACEC_COMPARE_NOT_EQUAL 5 +#define OACEC_COMPARE_LESS_THAN 4 +#define OACEC_COMPARE_GREATER_OR_EQUAL 3 +#define OACEC_COMPARE_EQUAL 2 +#define OACEC_COMPARE_GREATER_THAN 1 +#define OACEC_COMPARE_ANY_EQUAL 0 + +#define OACEC_COMPARE_VALUE_MASK 0xffff +#define OACEC_COMPARE_VALUE_SHIFT 3 + +#define OACEC_SELECT_NOA (0<<19) +#define OACEC_SELECT_PREV (1<<19) +#define OACEC_SELECT_BOOLEAN (2<<19) + +/* CECX_1 */ +#define OACEC_MASK_MASK 0xffff +#define OACEC_CONSIDERATIONS_MASK 0xffff +#define OACEC_CONSIDERATIONS_SHIFT 16 + +#define OACEC0_0 _MMIO(0x2770) +#define OACEC0_1 _MMIO(0x2774) +#define OACEC1_0 _MMIO(0x2778) +#define OACEC1_1 _MMIO(0x277c) +#define OACEC2_0 _MMIO(0x2780) +#define OACEC2_1 _MMIO(0x2784) +#define OACEC3_0 _MMIO(0x2788) +#define OACEC3_1 _MMIO(0x278c) +#define OACEC4_0 _MMIO(0x2790) +#define OACEC4_1 _MMIO(0x2794) +#define OACEC5_0 _MMIO(0x2798) +#define OACEC5_1 _MMIO(0x279c) +#define OACEC6_0 _MMIO(0x27a0) +#define OACEC6_1 _MMIO(0x27a4) +#define OACEC7_0 _MMIO(0x27a8) +#define OACEC7_1 _MMIO(0x27ac) + #define _GEN7_PIPEA_DE_LOAD_SL 0x70068 #define _GEN7_PIPEB_DE_LOAD_SL 0x71068 @@ -6993,6 +7330,7 @@ enum { # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11) #define GEN6_UCGCTL3 _MMIO(0x9408) +# define GEN6_OACSUNIT_CLOCK_GATE_DISABLE (1 << 20) #define GEN7_UCGCTL4 _MMIO(0x940c) #define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25) diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 98cd493..82d6239 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -1224,6 +1224,18 @@ struct drm_i915_gem_context_param { __u64 value; }; +enum drm_i915_oa_format { + I915_OA_FORMAT_A13 = 1, + I915_OA_FORMAT_A29, + I915_OA_FORMAT_A13_B8_C8, + I915_OA_FORMAT_B4_C8, + I915_OA_FORMAT_A45_B8_C8, + I915_OA_FORMAT_B4_C8_A16, + I915_OA_FORMAT_C4_B8, + + I915_OA_FORMAT_MAX /* non-ABI */ +}; + enum drm_i915_perf_property_id { /** * Open the stream for a specific context handle (as used with @@ -1232,6 +1244,32 @@ enum drm_i915_perf_property_id { */ DRM_I915_PERF_PROP_CTX_HANDLE = 1, + /** + * A value of 1 requests the inclusion of raw OA unit reports as + * part of stream samples. + */ + DRM_I915_PERF_PROP_SAMPLE_OA, + + /** + * The value specifies which set of OA unit metrics should be + * be configured, defining the contents of any OA unit reports. + */ + DRM_I915_PERF_PROP_OA_METRICS_SET, + + /** + * The value specifies the size and layout of OA unit reports. + */ + DRM_I915_PERF_PROP_OA_FORMAT, + + /** + * Specifying this property implicitly requests periodic OA unit + * sampling and (at least on Haswell) the sampling frequency is derived + * from this exponent as follows: + * + * 80ns * 2^(period_exponent + 1) + */ + DRM_I915_PERF_PROP_OA_EXPONENT, + DRM_I915_PERF_PROP_MAX /* non-ABI */ }; @@ -1251,7 +1289,23 @@ struct drm_i915_perf_open_param { __u64 __user properties_ptr; }; +/** + * Enable data capture for a stream that was either opened in a disabled state + * via I915_PERF_FLAG_DISABLED or was later disabled via + * I915_PERF_IOCTL_DISABLE. + * + * It is intended to be cheaper to disable and enable a stream than it may be + * to close and re-open a stream with the same configuration. + * + * It's undefined whether any pending data for the stream will be lost. + */ #define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) + +/** + * Disable data capture for a stream. + * + * It is an error to try and read a stream that is disabled. + */ #define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) /** @@ -1275,17 +1329,30 @@ enum drm_i915_perf_record_type { * every sample. * * The order of these sample properties given by userspace has no - * affect on the ordering of data within a sample. The order will be + * affect on the ordering of data within a sample. The order is * documented here. * * struct { * struct drm_i915_perf_record_header header; * - * TODO: itemize extensible sample data here + * { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA * }; */ DRM_I915_PERF_RECORD_SAMPLE = 1, + /* + * Indicates that one or more OA reports were not written by the + * hardware. This can happen for example if an MI_REPORT_PERF_COUNT + * command collides with periodic sampling - which would be more likely + * at higher sampling frequencies. + */ + DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2, + + /** + * An error occurred that resulted in all pending OA reports being lost. + */ + DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3, + DRM_I915_PERF_RECORD_MAX /* non-ABI */ };