Commit 0dd860cf authored by Robert Bragg's avatar Robert Bragg Committed by Chris Wilson

drm/i915/perf: improve tail race workaround

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 (in terms of
what's visible to the CPU).

Although this can be observed explicitly while copying reports to
userspace by checking for a zeroed report-id field in tail reports, we
want to account for this earlier, as part of the _oa_buffer_check to
avoid lots of redundant read() attempts.

Previously the driver used to define an effective tail pointer that
lagged the real pointer by a 'tail margin' measured in bytes derived
from OA_TAIL_MARGIN_NSEC and the configured sampling frequency.
Unfortunately this was flawed considering that the OA unit may also
automatically generate non-periodic reports (such as on context switch)
or the OA unit may be enabled without any periodic sampling.

This improves how we define a tail pointer for reading that lags the
real tail pointer by at least %OA_TAIL_MARGIN_NSEC nanoseconds, which
gives enough time for the corresponding reports to become visible to the
CPU.

The driver now maintains two tail pointers:
 1) An 'aging' tail with an associated timestamp that is tracked until we
    can trust the corresponding data is visible to the CPU; at which point
    it is considered 'aged'.
 2) An 'aged' tail that can be used for read()ing.

The two separate pointers let us decouple read()s from tail pointer aging.

The tail pointers are checked and updated at a limited rate within a
hrtimer callback (the same callback that is used for delivering POLLIN
events) and since we're now measuring the wall clock time elapsed since
a given tail pointer was read the mechanism no longer cares about
the OA unit's periodic sampling frequency.

The natural place to handle the tail pointer updates was in
gen7_oa_buffer_is_empty() which is called as part of blocking reads and
the hrtimer callback used for polling, and so this was renamed to
oa_buffer_check() considering the added side effect while checking
whether the buffer contains data.
Signed-off-by: default avatarRobert Bragg <robert@sixbynine.org>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170511154345.962-6-lionel.g.landwerlin@intel.comSigned-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent 3bb335c1
...@@ -2000,7 +2000,7 @@ struct i915_oa_ops { ...@@ -2000,7 +2000,7 @@ struct i915_oa_ops {
size_t *offset); size_t *offset);
/** /**
* @oa_buffer_is_empty: Check if OA buffer empty (false positives OK) * @oa_buffer_check: Check for OA buffer data + update tail
* *
* This is either called via fops or the poll check hrtimer (atomic * This is either called via fops or the poll check hrtimer (atomic
* ctx) without any locks taken. * ctx) without any locks taken.
...@@ -2013,7 +2013,7 @@ struct i915_oa_ops { ...@@ -2013,7 +2013,7 @@ struct i915_oa_ops {
* here, which will be handled gracefully - likely resulting in an * here, which will be handled gracefully - likely resulting in an
* %EAGAIN error for userspace. * %EAGAIN error for userspace.
*/ */
bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv); bool (*oa_buffer_check)(struct drm_i915_private *dev_priv);
}; };
struct intel_cdclk_state { struct intel_cdclk_state {
...@@ -2356,9 +2356,6 @@ struct drm_i915_private { ...@@ -2356,9 +2356,6 @@ struct drm_i915_private {
bool periodic; bool periodic;
int period_exponent; int period_exponent;
int timestamp_frequency;
int tail_margin;
int metrics_set; int metrics_set;
...@@ -2373,6 +2370,59 @@ struct drm_i915_private { ...@@ -2373,6 +2370,59 @@ struct drm_i915_private {
int format; int format;
int format_size; int format_size;
/**
* Locks reads and writes to all head/tail state
*
* Consider: the head and tail pointer state
* needs to be read consistently from a hrtimer
* callback (atomic context) and read() fop
* (user context) with tail pointer updates
* happening in atomic context and head updates
* in user context and the (unlikely)
* possibility of read() errors needing to
* reset all head/tail state.
*
* Note: Contention or performance aren't
* currently a significant concern here
* considering the relatively low frequency of
* hrtimer callbacks (5ms period) and that
* reads typically only happen in response to a
* hrtimer event and likely complete before the
* next callback.
*
* Note: This lock is not held *while* reading
* and copying data to userspace so the value
* of head observed in htrimer callbacks won't
* represent any partial consumption of data.
*/
spinlock_t ptr_lock;
/**
* One 'aging' tail pointer and one 'aged'
* tail pointer ready to used for reading.
*
* Initial values of 0xffffffff are invalid
* and imply that an update is required
* (and should be ignored by an attempted
* read)
*/
struct {
u32 offset;
} tails[2];
/**
* Index for the aged tail ready to read()
* data up to.
*/
unsigned int aged_tail_idx;
/**
* A monotonic timestamp for when the current
* aging tail pointer was read; used to
* determine when it is old enough to trust.
*/
u64 aging_timestamp;
/** /**
* Although we can always read back the head * Although we can always read back the head
* pointer register, we prefer to avoid * pointer register, we prefer to avoid
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment