Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915/pmu: Do not assume fixed hrtimer period

As Chris has discovered on his Ivybridge, and later automated test runs
have confirmed, on most of our platforms hrtimer faced with heavy GPU load
can occasionally become sufficiently imprecise to affect PMU sampling
calculations.

This means we cannot assume sampling frequency is what we asked for, but
we need to measure the interval ourselves.

This patch is similar to Chris' original proposal for per-engine counters,
but instead of introducing a new set to work around the problem with
frequency sampling, it swaps around the way internal frequency accounting
is done. Instead of accumulating current frequency and dividing by
sampling frequency on readout, it accumulates frequency scaled by each
period.

v2:
* Typo in commit message, comment on period calculation and USEC_PER_SEC.
(Chris Wilson)

Testcase: igt/perf_pmu/*busy* # snb, ivb, hsw
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20180605140253.3541-1-tvrtko.ursulin@linux.intel.com

+55 -20
+47 -20
drivers/gpu/drm/i915/i915_pmu.c
··· 127 127 { 128 128 if (!i915->pmu.timer_enabled && pmu_needs_timer(i915, true)) { 129 129 i915->pmu.timer_enabled = true; 130 + i915->pmu.timer_last = ktime_get(); 130 131 hrtimer_start_range_ns(&i915->pmu.timer, 131 132 ns_to_ktime(PERIOD), 0, 132 133 HRTIMER_MODE_REL_PINNED); ··· 156 155 } 157 156 158 157 static void 159 - update_sample(struct i915_pmu_sample *sample, u32 unit, u32 val) 158 + add_sample(struct i915_pmu_sample *sample, u32 val) 160 159 { 161 - sample->cur += mul_u32_u32(val, unit); 160 + sample->cur += val; 162 161 } 163 162 164 - static void engines_sample(struct drm_i915_private *dev_priv) 163 + static void 164 + engines_sample(struct drm_i915_private *dev_priv, unsigned int period_ns) 165 165 { 166 166 struct intel_engine_cs *engine; 167 167 enum intel_engine_id id; ··· 184 182 185 183 val = !i915_seqno_passed(current_seqno, last_seqno); 186 184 187 - update_sample(&engine->pmu.sample[I915_SAMPLE_BUSY], 188 - PERIOD, val); 185 + if (val) 186 + add_sample(&engine->pmu.sample[I915_SAMPLE_BUSY], 187 + period_ns); 189 188 190 189 if (val && (engine->pmu.enable & 191 190 (BIT(I915_SAMPLE_WAIT) | BIT(I915_SAMPLE_SEMA)))) { ··· 197 194 val = 0; 198 195 } 199 196 200 - update_sample(&engine->pmu.sample[I915_SAMPLE_WAIT], 201 - PERIOD, !!(val & RING_WAIT)); 197 + if (val & RING_WAIT) 198 + add_sample(&engine->pmu.sample[I915_SAMPLE_WAIT], 199 + period_ns); 202 200 203 - update_sample(&engine->pmu.sample[I915_SAMPLE_SEMA], 204 - PERIOD, !!(val & RING_WAIT_SEMAPHORE)); 201 + if (val & RING_WAIT_SEMAPHORE) 202 + add_sample(&engine->pmu.sample[I915_SAMPLE_SEMA], 203 + period_ns); 205 204 } 206 205 207 206 if (fw) ··· 212 207 intel_runtime_pm_put(dev_priv); 213 208 } 214 209 215 - static void frequency_sample(struct drm_i915_private *dev_priv) 210 + static void 211 + add_sample_mult(struct i915_pmu_sample *sample, u32 val, u32 mul) 212 + { 213 + sample->cur += mul_u32_u32(val, mul); 214 + } 215 + 216 + static void 217 + frequency_sample(struct drm_i915_private *dev_priv, unsigned int period_ns) 216 218 { 217 219 if (dev_priv->pmu.enable & 218 220 config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY)) { ··· 233 221 intel_runtime_pm_put(dev_priv); 234 222 } 235 223 236 - update_sample(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_ACT], 237 - 1, intel_gpu_freq(dev_priv, val)); 224 + add_sample_mult(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_ACT], 225 + intel_gpu_freq(dev_priv, val), 226 + period_ns / 1000); 238 227 } 239 228 240 229 if (dev_priv->pmu.enable & 241 230 config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY)) { 242 - update_sample(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_REQ], 1, 243 - intel_gpu_freq(dev_priv, 244 - dev_priv->gt_pm.rps.cur_freq)); 231 + add_sample_mult(&dev_priv->pmu.sample[__I915_SAMPLE_FREQ_REQ], 232 + intel_gpu_freq(dev_priv, 233 + dev_priv->gt_pm.rps.cur_freq), 234 + period_ns / 1000); 245 235 } 246 236 } 247 237 ··· 251 237 { 252 238 struct drm_i915_private *i915 = 253 239 container_of(hrtimer, struct drm_i915_private, pmu.timer); 240 + unsigned int period_ns; 241 + ktime_t now; 254 242 255 243 if (!READ_ONCE(i915->pmu.timer_enabled)) 256 244 return HRTIMER_NORESTART; 257 245 258 - engines_sample(i915); 259 - frequency_sample(i915); 246 + now = ktime_get(); 247 + period_ns = ktime_to_ns(ktime_sub(now, i915->pmu.timer_last)); 248 + i915->pmu.timer_last = now; 260 249 261 - hrtimer_forward_now(hrtimer, ns_to_ktime(PERIOD)); 250 + /* 251 + * Strictly speaking the passed in period may not be 100% accurate for 252 + * all internal calculation, since some amount of time can be spent on 253 + * grabbing the forcewake. However the potential error from timer call- 254 + * back delay greatly dominates this so we keep it simple. 255 + */ 256 + engines_sample(i915, period_ns); 257 + frequency_sample(i915, period_ns); 258 + 259 + hrtimer_forward(hrtimer, now, ns_to_ktime(PERIOD)); 260 + 262 261 return HRTIMER_RESTART; 263 262 } 264 263 ··· 546 519 case I915_PMU_ACTUAL_FREQUENCY: 547 520 val = 548 521 div_u64(i915->pmu.sample[__I915_SAMPLE_FREQ_ACT].cur, 549 - FREQUENCY); 522 + USEC_PER_SEC /* to MHz */); 550 523 break; 551 524 case I915_PMU_REQUESTED_FREQUENCY: 552 525 val = 553 526 div_u64(i915->pmu.sample[__I915_SAMPLE_FREQ_REQ].cur, 554 - FREQUENCY); 527 + USEC_PER_SEC /* to MHz */); 555 528 break; 556 529 case I915_PMU_INTERRUPTS: 557 530 val = count_interrupts(i915);
+8
drivers/gpu/drm/i915/i915_pmu.h
··· 65 65 * event types. 66 66 */ 67 67 u64 enable; 68 + 69 + /** 70 + * @timer_last: 71 + * 72 + * Timestmap of the previous timer invocation. 73 + */ 74 + ktime_t timer_last; 75 + 68 76 /** 69 77 * @enable_count: Reference counts for the enabled events. 70 78 *