[Intel-gfx] [PATCH 3/5] drm/i915/perf: avoid read back of head register
Matthew Auld
matthew.william.auld at gmail.com
Wed Jan 25 15:54:22 UTC 2017
On 24 January 2017 at 01:25, Robert Bragg <robert at sixbynine.org> wrote:
> There's no need for the driver to keep reading back the head pointer
> from hardware since the hardware doesn't update it automatically. This
> way we can treat any invalid head pointer value as a software/driver
> bug instead of spurious hardware behaviour.
>
> This change is also a small stepping stone towards re-working how
> the head and tail state is managed as part of an improved workaround
> for the tail register race condition.
>
> Signed-off-by: Robert Bragg <robert at sixbynine.org>
> ---
> drivers/gpu/drm/i915/i915_drv.h | 11 ++++++++++
> drivers/gpu/drm/i915/i915_perf.c | 46 ++++++++++++++++++----------------------
> 2 files changed, 32 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 38509505424d..e732d0b3bf65 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -2397,6 +2397,17 @@ struct drm_i915_private {
> u8 *vaddr;
> int format;
> int format_size;
> +
> + /**
> + * Although we can always read back the head
> + * pointer register, we prefer to avoid
> + * trusting the HW state, just to avoid any
> + * risk that some hardware condition could
> + * somehow bump the head pointer unpredictably
> + * and cause us to forward the wrong OA buffer
> + * data to uesrspace.
"userspace"
> + */
> + u32 head;
> } oa_buffer;
>
> u32 gen7_latched_oastatus1;
> diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
> index 4bb7333dac45..e85583d0bcff 100644
> --- a/drivers/gpu/drm/i915/i915_perf.c
> +++ b/drivers/gpu/drm/i915/i915_perf.c
> @@ -322,9 +322,8 @@ struct perf_open_properties {
> 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 head = dev_priv->perf.oa.oa_buffer.head;
> u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
>
> return OA_TAKEN(tail, head) <
> @@ -458,16 +457,24 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
> return -EIO;
>
> head = *head_ptr - gtt_offset;
> +
> + /* An out of bounds or misaligned head pointer implies a driver bug
> + * since we are in full control of head pointer which should only
> + * be incremented by multiples of the report size (notably also
> + * all a power of two).
> + */
> + if (WARN_ONCE(head > OA_BUFFER_SIZE || head % report_size,
> + "Inconsistent OA buffer head pointer = %u\n", head))
> + return -EIO;
> +
> 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...
> + * buffer size
> */
> - if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE ||
> - head % report_size) {
> - DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart\n",
> - head, tail);
> + if (tail > OA_BUFFER_SIZE) {
> + DRM_ERROR("Inconsistent OA buffer tail pointer = %u: force restart\n",
> + 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) &
> @@ -562,8 +569,6 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
> 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;
> @@ -572,10 +577,9 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
> 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;
> + head = dev_priv->perf.oa.oa_buffer.head;
> tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
>
> /* XXX: On Haswell we don't have a safe way to clear oastatus1
> @@ -616,10 +620,9 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
> 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;
> + head = dev_priv->perf.oa.oa_buffer.head;
> tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK;
> }
>
> @@ -635,17 +638,6 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
> 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.
> @@ -653,6 +645,7 @@ static int gen7_oa_read(struct i915_perf_stream *stream,
> I915_WRITE(GEN7_OASTATUS2,
> ((head & GEN7_OASTATUS2_HEAD_MASK) |
> OA_MEM_SELECT_GGTT));
> + dev_priv->perf.oa.oa_buffer.head = head;
>
> return ret;
> }
> @@ -834,7 +827,10 @@ static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
> * before OASTATUS1, but after OASTATUS2
> */
> I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */
> + dev_priv->perf.oa.oa_buffer.head = gtt_offset;
> +
> I915_WRITE(GEN7_OABUFFER, gtt_offset);
> +
Spurious newline ?
Reviewed-by: Matthew Auld <matthew.auld at intel.com>
More information about the Intel-gfx
mailing list