162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * SPDX-License-Identifier: MIT 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright © 2017-2018 Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "gt/intel_engine.h" 1062306a36Sopenharmony_ci#include "gt/intel_engine_pm.h" 1162306a36Sopenharmony_ci#include "gt/intel_engine_regs.h" 1262306a36Sopenharmony_ci#include "gt/intel_engine_user.h" 1362306a36Sopenharmony_ci#include "gt/intel_gt.h" 1462306a36Sopenharmony_ci#include "gt/intel_gt_pm.h" 1562306a36Sopenharmony_ci#include "gt/intel_gt_regs.h" 1662306a36Sopenharmony_ci#include "gt/intel_rc6.h" 1762306a36Sopenharmony_ci#include "gt/intel_rps.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "i915_drv.h" 2062306a36Sopenharmony_ci#include "i915_pmu.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Frequency for the sampling timer for events which need it. */ 2362306a36Sopenharmony_ci#define FREQUENCY 200 2462306a36Sopenharmony_ci#define PERIOD max_t(u64, 10000, NSEC_PER_SEC / FREQUENCY) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ENGINE_SAMPLE_MASK \ 2762306a36Sopenharmony_ci (BIT(I915_SAMPLE_BUSY) | \ 2862306a36Sopenharmony_ci BIT(I915_SAMPLE_WAIT) | \ 2962306a36Sopenharmony_ci BIT(I915_SAMPLE_SEMA)) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic cpumask_t i915_pmu_cpumask; 3262306a36Sopenharmony_cistatic unsigned int i915_pmu_target_cpu = -1; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic u8 engine_config_sample(u64 config) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return config & I915_PMU_SAMPLE_MASK; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic u8 engine_event_sample(struct perf_event *event) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci return engine_config_sample(event->attr.config); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic u8 engine_event_class(struct perf_event *event) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return (event->attr.config >> I915_PMU_CLASS_SHIFT) & 0xff; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic u8 engine_event_instance(struct perf_event *event) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return (event->attr.config >> I915_PMU_SAMPLE_BITS) & 0xff; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic bool is_engine_config(const u64 config) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci return config < __I915_PMU_OTHER(0); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic unsigned int config_gt_id(const u64 config) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return config >> __I915_PMU_GT_SHIFT; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic u64 config_counter(const u64 config) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return config & ~(~0ULL << __I915_PMU_GT_SHIFT); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned int other_bit(const u64 config) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned int val; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci switch (config_counter(config)) { 7462306a36Sopenharmony_ci case I915_PMU_ACTUAL_FREQUENCY: 7562306a36Sopenharmony_ci val = __I915_PMU_ACTUAL_FREQUENCY_ENABLED; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case I915_PMU_REQUESTED_FREQUENCY: 7862306a36Sopenharmony_ci val = __I915_PMU_REQUESTED_FREQUENCY_ENABLED; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci case I915_PMU_RC6_RESIDENCY: 8162306a36Sopenharmony_ci val = __I915_PMU_RC6_RESIDENCY_ENABLED; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci default: 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Events that do not require sampling, or tracking state 8662306a36Sopenharmony_ci * transitions between enabled and disabled can be ignored. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci return -1; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return I915_ENGINE_SAMPLE_COUNT + 9262306a36Sopenharmony_ci config_gt_id(config) * __I915_PMU_TRACKED_EVENT_COUNT + 9362306a36Sopenharmony_ci val; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic unsigned int config_bit(const u64 config) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci if (is_engine_config(config)) 9962306a36Sopenharmony_ci return engine_config_sample(config); 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci return other_bit(config); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic u32 config_mask(const u64 config) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci unsigned int bit = config_bit(config); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (__builtin_constant_p(config)) 10962306a36Sopenharmony_ci BUILD_BUG_ON(bit > 11062306a36Sopenharmony_ci BITS_PER_TYPE(typeof_member(struct i915_pmu, 11162306a36Sopenharmony_ci enable)) - 1); 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci WARN_ON_ONCE(bit > 11462306a36Sopenharmony_ci BITS_PER_TYPE(typeof_member(struct i915_pmu, 11562306a36Sopenharmony_ci enable)) - 1); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return BIT(config_bit(config)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic bool is_engine_event(struct perf_event *event) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci return is_engine_config(event->attr.config); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic unsigned int event_bit(struct perf_event *event) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return config_bit(event->attr.config); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic u32 frequency_enabled_mask(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned int i; 13362306a36Sopenharmony_ci u32 mask = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < I915_PMU_MAX_GT; i++) 13662306a36Sopenharmony_ci mask |= config_mask(__I915_PMU_ACTUAL_FREQUENCY(i)) | 13762306a36Sopenharmony_ci config_mask(__I915_PMU_REQUESTED_FREQUENCY(i)); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return mask; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic bool pmu_needs_timer(struct i915_pmu *pmu) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 14562306a36Sopenharmony_ci u32 enable; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Only some counters need the sampling timer. 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * We start with a bitmask of all currently enabled events. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci enable = pmu->enable; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * Mask out all the ones which do not need the timer, or in 15662306a36Sopenharmony_ci * other words keep all the ones that could need the timer. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci enable &= frequency_enabled_mask() | ENGINE_SAMPLE_MASK; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Also there is software busyness tracking available we do not 16262306a36Sopenharmony_ci * need the timer for I915_SAMPLE_BUSY counter. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if (i915->caps.scheduler & I915_SCHEDULER_CAP_ENGINE_BUSY_STATS) 16562306a36Sopenharmony_ci enable &= ~BIT(I915_SAMPLE_BUSY); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * If some bits remain it means we need the sampling timer running. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci return enable; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic u64 __get_rc6(struct intel_gt *gt) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 17662306a36Sopenharmony_ci u64 val; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci val = intel_rc6_residency_ns(>->rc6, INTEL_RC6_RES_RC6); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (HAS_RC6p(i915)) 18162306a36Sopenharmony_ci val += intel_rc6_residency_ns(>->rc6, INTEL_RC6_RES_RC6p); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (HAS_RC6pp(i915)) 18462306a36Sopenharmony_ci val += intel_rc6_residency_ns(>->rc6, INTEL_RC6_RES_RC6pp); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return val; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline s64 ktime_since_raw(const ktime_t kt) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci return ktime_to_ns(ktime_sub(ktime_get_raw(), kt)); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic u64 read_sample(struct i915_pmu *pmu, unsigned int gt_id, int sample) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return pmu->sample[gt_id][sample].cur; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void 20062306a36Sopenharmony_cistore_sample(struct i915_pmu *pmu, unsigned int gt_id, int sample, u64 val) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci pmu->sample[gt_id][sample].cur = val; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void 20662306a36Sopenharmony_ciadd_sample_mult(struct i915_pmu *pmu, unsigned int gt_id, int sample, u32 val, u32 mul) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci pmu->sample[gt_id][sample].cur += mul_u32_u32(val, mul); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic u64 get_rc6(struct intel_gt *gt) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 21462306a36Sopenharmony_ci const unsigned int gt_id = gt->info.id; 21562306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 21662306a36Sopenharmony_ci unsigned long flags; 21762306a36Sopenharmony_ci bool awake = false; 21862306a36Sopenharmony_ci u64 val; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (intel_gt_pm_get_if_awake(gt)) { 22162306a36Sopenharmony_ci val = __get_rc6(gt); 22262306a36Sopenharmony_ci intel_gt_pm_put_async(gt); 22362306a36Sopenharmony_ci awake = true; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (awake) { 22962306a36Sopenharmony_ci store_sample(pmu, gt_id, __I915_SAMPLE_RC6, val); 23062306a36Sopenharmony_ci } else { 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * We think we are runtime suspended. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Report the delta from when the device was suspended to now, 23562306a36Sopenharmony_ci * on top of the last known real value, as the approximated RC6 23662306a36Sopenharmony_ci * counter value. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci val = ktime_since_raw(pmu->sleep_last[gt_id]); 23962306a36Sopenharmony_ci val += read_sample(pmu, gt_id, __I915_SAMPLE_RC6); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (val < read_sample(pmu, gt_id, __I915_SAMPLE_RC6_LAST_REPORTED)) 24362306a36Sopenharmony_ci val = read_sample(pmu, gt_id, __I915_SAMPLE_RC6_LAST_REPORTED); 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci store_sample(pmu, gt_id, __I915_SAMPLE_RC6_LAST_REPORTED, val); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return val; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void init_rc6(struct i915_pmu *pmu) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 25562306a36Sopenharmony_ci struct intel_gt *gt; 25662306a36Sopenharmony_ci unsigned int i; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci for_each_gt(gt, i915, i) { 25962306a36Sopenharmony_ci intel_wakeref_t wakeref; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci with_intel_runtime_pm(gt->uncore->rpm, wakeref) { 26262306a36Sopenharmony_ci u64 val = __get_rc6(gt); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci store_sample(pmu, i, __I915_SAMPLE_RC6, val); 26562306a36Sopenharmony_ci store_sample(pmu, i, __I915_SAMPLE_RC6_LAST_REPORTED, 26662306a36Sopenharmony_ci val); 26762306a36Sopenharmony_ci pmu->sleep_last[i] = ktime_get_raw(); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void park_rc6(struct intel_gt *gt) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct i915_pmu *pmu = >->i915->pmu; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci store_sample(pmu, gt->info.id, __I915_SAMPLE_RC6, __get_rc6(gt)); 27762306a36Sopenharmony_ci pmu->sleep_last[gt->info.id] = ktime_get_raw(); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void __i915_pmu_maybe_start_timer(struct i915_pmu *pmu) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (!pmu->timer_enabled && pmu_needs_timer(pmu)) { 28362306a36Sopenharmony_ci pmu->timer_enabled = true; 28462306a36Sopenharmony_ci pmu->timer_last = ktime_get(); 28562306a36Sopenharmony_ci hrtimer_start_range_ns(&pmu->timer, 28662306a36Sopenharmony_ci ns_to_ktime(PERIOD), 0, 28762306a36Sopenharmony_ci HRTIMER_MODE_REL_PINNED); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_civoid i915_pmu_gt_parked(struct intel_gt *gt) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct i915_pmu *pmu = >->i915->pmu; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!pmu->base.event_init) 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci spin_lock_irq(&pmu->lock); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci park_rc6(gt); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * Signal sampling timer to stop if only engine events are enabled and 30462306a36Sopenharmony_ci * GPU went idle. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci pmu->unparked &= ~BIT(gt->info.id); 30762306a36Sopenharmony_ci if (pmu->unparked == 0) 30862306a36Sopenharmony_ci pmu->timer_enabled = false; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci spin_unlock_irq(&pmu->lock); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_civoid i915_pmu_gt_unparked(struct intel_gt *gt) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct i915_pmu *pmu = >->i915->pmu; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!pmu->base.event_init) 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci spin_lock_irq(&pmu->lock); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * Re-enable sampling timer when GPU goes active. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if (pmu->unparked == 0) 32662306a36Sopenharmony_ci __i915_pmu_maybe_start_timer(pmu); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci pmu->unparked |= BIT(gt->info.id); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci spin_unlock_irq(&pmu->lock); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void 33462306a36Sopenharmony_ciadd_sample(struct i915_pmu_sample *sample, u32 val) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci sample->cur += val; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic bool exclusive_mmio_access(const struct drm_i915_private *i915) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * We have to avoid concurrent mmio cache line access on gen7 or 34362306a36Sopenharmony_ci * risk a machine hang. For a fun history lesson dig out the old 34462306a36Sopenharmony_ci * userspace intel_gpu_top and run it on Ivybridge or Haswell! 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci return GRAPHICS_VER(i915) == 7; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void engine_sample(struct intel_engine_cs *engine, unsigned int period_ns) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct intel_engine_pmu *pmu = &engine->pmu; 35262306a36Sopenharmony_ci bool busy; 35362306a36Sopenharmony_ci u32 val; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci val = ENGINE_READ_FW(engine, RING_CTL); 35662306a36Sopenharmony_ci if (val == 0) /* powerwell off => engine idle */ 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (val & RING_WAIT) 36062306a36Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_WAIT], period_ns); 36162306a36Sopenharmony_ci if (val & RING_WAIT_SEMAPHORE) 36262306a36Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_SEMA], period_ns); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* No need to sample when busy stats are supported. */ 36562306a36Sopenharmony_ci if (intel_engine_supports_stats(engine)) 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * While waiting on a semaphore or event, MI_MODE reports the 37062306a36Sopenharmony_ci * ring as idle. However, previously using the seqno, and with 37162306a36Sopenharmony_ci * execlists sampling, we account for the ring waiting as the 37262306a36Sopenharmony_ci * engine being busy. Therefore, we record the sample as being 37362306a36Sopenharmony_ci * busy if either waiting or !idle. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci busy = val & (RING_WAIT_SEMAPHORE | RING_WAIT); 37662306a36Sopenharmony_ci if (!busy) { 37762306a36Sopenharmony_ci val = ENGINE_READ_FW(engine, RING_MI_MODE); 37862306a36Sopenharmony_ci busy = !(val & MODE_IDLE); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci if (busy) 38162306a36Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_BUSY], period_ns); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void 38562306a36Sopenharmony_ciengines_sample(struct intel_gt *gt, unsigned int period_ns) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 38862306a36Sopenharmony_ci struct intel_engine_cs *engine; 38962306a36Sopenharmony_ci enum intel_engine_id id; 39062306a36Sopenharmony_ci unsigned long flags; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if ((i915->pmu.enable & ENGINE_SAMPLE_MASK) == 0) 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (!intel_gt_pm_is_awake(gt)) 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for_each_engine(engine, gt, id) { 39962306a36Sopenharmony_ci if (!engine->pmu.enable) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!intel_engine_pm_get_if_awake(engine)) 40362306a36Sopenharmony_ci continue; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (exclusive_mmio_access(i915)) { 40662306a36Sopenharmony_ci spin_lock_irqsave(&engine->uncore->lock, flags); 40762306a36Sopenharmony_ci engine_sample(engine, period_ns); 40862306a36Sopenharmony_ci spin_unlock_irqrestore(&engine->uncore->lock, flags); 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci engine_sample(engine, period_ns); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci intel_engine_pm_put_async(engine); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic bool 41862306a36Sopenharmony_cifrequency_sampling_enabled(struct i915_pmu *pmu, unsigned int gt) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci return pmu->enable & 42162306a36Sopenharmony_ci (config_mask(__I915_PMU_ACTUAL_FREQUENCY(gt)) | 42262306a36Sopenharmony_ci config_mask(__I915_PMU_REQUESTED_FREQUENCY(gt))); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void 42662306a36Sopenharmony_cifrequency_sample(struct intel_gt *gt, unsigned int period_ns) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 42962306a36Sopenharmony_ci const unsigned int gt_id = gt->info.id; 43062306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 43162306a36Sopenharmony_ci struct intel_rps *rps = >->rps; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!frequency_sampling_enabled(pmu, gt_id)) 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Report 0/0 (actual/requested) frequency while parked. */ 43762306a36Sopenharmony_ci if (!intel_gt_pm_get_if_awake(gt)) 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (pmu->enable & config_mask(__I915_PMU_ACTUAL_FREQUENCY(gt_id))) { 44162306a36Sopenharmony_ci u32 val; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * We take a quick peek here without using forcewake 44562306a36Sopenharmony_ci * so that we don't perturb the system under observation 44662306a36Sopenharmony_ci * (forcewake => !rc6 => increased power use). We expect 44762306a36Sopenharmony_ci * that if the read fails because it is outside of the 44862306a36Sopenharmony_ci * mmio power well, then it will return 0 -- in which 44962306a36Sopenharmony_ci * case we assume the system is running at the intended 45062306a36Sopenharmony_ci * frequency. Fortunately, the read should rarely fail! 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci val = intel_rps_read_actual_frequency_fw(rps); 45362306a36Sopenharmony_ci if (!val) 45462306a36Sopenharmony_ci val = intel_gpu_freq(rps, rps->cur_freq); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci add_sample_mult(pmu, gt_id, __I915_SAMPLE_FREQ_ACT, 45762306a36Sopenharmony_ci val, period_ns / 1000); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (pmu->enable & config_mask(__I915_PMU_REQUESTED_FREQUENCY(gt_id))) { 46162306a36Sopenharmony_ci add_sample_mult(pmu, gt_id, __I915_SAMPLE_FREQ_REQ, 46262306a36Sopenharmony_ci intel_rps_get_requested_frequency(rps), 46362306a36Sopenharmony_ci period_ns / 1000); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci intel_gt_pm_put_async(gt); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic enum hrtimer_restart i915_sample(struct hrtimer *hrtimer) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct drm_i915_private *i915 = 47262306a36Sopenharmony_ci container_of(hrtimer, struct drm_i915_private, pmu.timer); 47362306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 47462306a36Sopenharmony_ci unsigned int period_ns; 47562306a36Sopenharmony_ci struct intel_gt *gt; 47662306a36Sopenharmony_ci unsigned int i; 47762306a36Sopenharmony_ci ktime_t now; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (!READ_ONCE(pmu->timer_enabled)) 48062306a36Sopenharmony_ci return HRTIMER_NORESTART; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci now = ktime_get(); 48362306a36Sopenharmony_ci period_ns = ktime_to_ns(ktime_sub(now, pmu->timer_last)); 48462306a36Sopenharmony_ci pmu->timer_last = now; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* 48762306a36Sopenharmony_ci * Strictly speaking the passed in period may not be 100% accurate for 48862306a36Sopenharmony_ci * all internal calculation, since some amount of time can be spent on 48962306a36Sopenharmony_ci * grabbing the forcewake. However the potential error from timer call- 49062306a36Sopenharmony_ci * back delay greatly dominates this so we keep it simple. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for_each_gt(gt, i915, i) { 49462306a36Sopenharmony_ci if (!(pmu->unparked & BIT(i))) 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci engines_sample(gt, period_ns); 49862306a36Sopenharmony_ci frequency_sample(gt, period_ns); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci hrtimer_forward(hrtimer, now, ns_to_ktime(PERIOD)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return HRTIMER_RESTART; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void i915_pmu_event_destroy(struct perf_event *event) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct drm_i915_private *i915 = 50962306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci drm_WARN_ON(&i915->drm, event->parent); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci drm_dev_put(&i915->drm); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int 51762306a36Sopenharmony_ciengine_event_status(struct intel_engine_cs *engine, 51862306a36Sopenharmony_ci enum drm_i915_pmu_engine_sample sample) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci switch (sample) { 52162306a36Sopenharmony_ci case I915_SAMPLE_BUSY: 52262306a36Sopenharmony_ci case I915_SAMPLE_WAIT: 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case I915_SAMPLE_SEMA: 52562306a36Sopenharmony_ci if (GRAPHICS_VER(engine->i915) < 6) 52662306a36Sopenharmony_ci return -ENODEV; 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci default: 52962306a36Sopenharmony_ci return -ENOENT; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int 53662306a36Sopenharmony_ciconfig_status(struct drm_i915_private *i915, u64 config) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct intel_gt *gt = to_gt(i915); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci unsigned int gt_id = config_gt_id(config); 54162306a36Sopenharmony_ci unsigned int max_gt_id = HAS_EXTRA_GT_LIST(i915) ? 1 : 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (gt_id > max_gt_id) 54462306a36Sopenharmony_ci return -ENOENT; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci switch (config_counter(config)) { 54762306a36Sopenharmony_ci case I915_PMU_ACTUAL_FREQUENCY: 54862306a36Sopenharmony_ci if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) 54962306a36Sopenharmony_ci /* Requires a mutex for sampling! */ 55062306a36Sopenharmony_ci return -ENODEV; 55162306a36Sopenharmony_ci fallthrough; 55262306a36Sopenharmony_ci case I915_PMU_REQUESTED_FREQUENCY: 55362306a36Sopenharmony_ci if (GRAPHICS_VER(i915) < 6) 55462306a36Sopenharmony_ci return -ENODEV; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci case I915_PMU_INTERRUPTS: 55762306a36Sopenharmony_ci if (gt_id) 55862306a36Sopenharmony_ci return -ENOENT; 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case I915_PMU_RC6_RESIDENCY: 56162306a36Sopenharmony_ci if (!gt->rc6.supported) 56262306a36Sopenharmony_ci return -ENODEV; 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci case I915_PMU_SOFTWARE_GT_AWAKE_TIME: 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci default: 56762306a36Sopenharmony_ci return -ENOENT; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int engine_event_init(struct perf_event *event) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct drm_i915_private *i915 = 57662306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 57762306a36Sopenharmony_ci struct intel_engine_cs *engine; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci engine = intel_engine_lookup_user(i915, engine_event_class(event), 58062306a36Sopenharmony_ci engine_event_instance(event)); 58162306a36Sopenharmony_ci if (!engine) 58262306a36Sopenharmony_ci return -ENODEV; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return engine_event_status(engine, engine_event_sample(event)); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int i915_pmu_event_init(struct perf_event *event) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct drm_i915_private *i915 = 59062306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 59162306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 59262306a36Sopenharmony_ci int ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (pmu->closed) 59562306a36Sopenharmony_ci return -ENODEV; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 59862306a36Sopenharmony_ci return -ENOENT; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* unsupported modes and filters */ 60162306a36Sopenharmony_ci if (event->attr.sample_period) /* no sampling */ 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (has_branch_stack(event)) 60562306a36Sopenharmony_ci return -EOPNOTSUPP; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (event->cpu < 0) 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* only allow running on one cpu at a time */ 61162306a36Sopenharmony_ci if (!cpumask_test_cpu(event->cpu, &i915_pmu_cpumask)) 61262306a36Sopenharmony_ci return -EINVAL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (is_engine_event(event)) 61562306a36Sopenharmony_ci ret = engine_event_init(event); 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci ret = config_status(i915, event->attr.config); 61862306a36Sopenharmony_ci if (ret) 61962306a36Sopenharmony_ci return ret; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!event->parent) { 62262306a36Sopenharmony_ci drm_dev_get(&i915->drm); 62362306a36Sopenharmony_ci event->destroy = i915_pmu_event_destroy; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic u64 __i915_pmu_event_read(struct perf_event *event) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct drm_i915_private *i915 = 63262306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 63362306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 63462306a36Sopenharmony_ci u64 val = 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (is_engine_event(event)) { 63762306a36Sopenharmony_ci u8 sample = engine_event_sample(event); 63862306a36Sopenharmony_ci struct intel_engine_cs *engine; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci engine = intel_engine_lookup_user(i915, 64162306a36Sopenharmony_ci engine_event_class(event), 64262306a36Sopenharmony_ci engine_event_instance(event)); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (drm_WARN_ON_ONCE(&i915->drm, !engine)) { 64562306a36Sopenharmony_ci /* Do nothing */ 64662306a36Sopenharmony_ci } else if (sample == I915_SAMPLE_BUSY && 64762306a36Sopenharmony_ci intel_engine_supports_stats(engine)) { 64862306a36Sopenharmony_ci ktime_t unused; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci val = ktime_to_ns(intel_engine_get_busy_time(engine, 65162306a36Sopenharmony_ci &unused)); 65262306a36Sopenharmony_ci } else { 65362306a36Sopenharmony_ci val = engine->pmu.sample[sample].cur; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci const unsigned int gt_id = config_gt_id(event->attr.config); 65762306a36Sopenharmony_ci const u64 config = config_counter(event->attr.config); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci switch (config) { 66062306a36Sopenharmony_ci case I915_PMU_ACTUAL_FREQUENCY: 66162306a36Sopenharmony_ci val = 66262306a36Sopenharmony_ci div_u64(read_sample(pmu, gt_id, 66362306a36Sopenharmony_ci __I915_SAMPLE_FREQ_ACT), 66462306a36Sopenharmony_ci USEC_PER_SEC /* to MHz */); 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case I915_PMU_REQUESTED_FREQUENCY: 66762306a36Sopenharmony_ci val = 66862306a36Sopenharmony_ci div_u64(read_sample(pmu, gt_id, 66962306a36Sopenharmony_ci __I915_SAMPLE_FREQ_REQ), 67062306a36Sopenharmony_ci USEC_PER_SEC /* to MHz */); 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci case I915_PMU_INTERRUPTS: 67362306a36Sopenharmony_ci val = READ_ONCE(pmu->irq_count); 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case I915_PMU_RC6_RESIDENCY: 67662306a36Sopenharmony_ci val = get_rc6(i915->gt[gt_id]); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case I915_PMU_SOFTWARE_GT_AWAKE_TIME: 67962306a36Sopenharmony_ci val = ktime_to_ns(intel_gt_get_awake_time(to_gt(i915))); 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return val; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void i915_pmu_event_read(struct perf_event *event) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct drm_i915_private *i915 = 69062306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 69162306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 69262306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 69362306a36Sopenharmony_ci u64 prev, new; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (pmu->closed) { 69662306a36Sopenharmony_ci event->hw.state = PERF_HES_STOPPED; 69762306a36Sopenharmony_ci return; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ciagain: 70062306a36Sopenharmony_ci prev = local64_read(&hwc->prev_count); 70162306a36Sopenharmony_ci new = __i915_pmu_event_read(event); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (local64_cmpxchg(&hwc->prev_count, prev, new) != prev) 70462306a36Sopenharmony_ci goto again; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci local64_add(new - prev, &event->count); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void i915_pmu_enable(struct perf_event *event) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct drm_i915_private *i915 = 71262306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 71362306a36Sopenharmony_ci const unsigned int bit = event_bit(event); 71462306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 71562306a36Sopenharmony_ci unsigned long flags; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (bit == -1) 71862306a36Sopenharmony_ci goto update; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* 72362306a36Sopenharmony_ci * Update the bitmask of enabled events and increment 72462306a36Sopenharmony_ci * the event reference counter. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(pmu->enable_count) != I915_PMU_MASK_BITS); 72762306a36Sopenharmony_ci GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count)); 72862306a36Sopenharmony_ci GEM_BUG_ON(pmu->enable_count[bit] == ~0); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pmu->enable |= BIT(bit); 73162306a36Sopenharmony_ci pmu->enable_count[bit]++; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* 73462306a36Sopenharmony_ci * Start the sampling timer if needed and not already enabled. 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci __i915_pmu_maybe_start_timer(pmu); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * For per-engine events the bitmask and reference counting 74062306a36Sopenharmony_ci * is stored per engine. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci if (is_engine_event(event)) { 74362306a36Sopenharmony_ci u8 sample = engine_event_sample(event); 74462306a36Sopenharmony_ci struct intel_engine_cs *engine; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci engine = intel_engine_lookup_user(i915, 74762306a36Sopenharmony_ci engine_event_class(event), 74862306a36Sopenharmony_ci engine_event_instance(event)); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.enable_count) != 75162306a36Sopenharmony_ci I915_ENGINE_SAMPLE_COUNT); 75262306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.sample) != 75362306a36Sopenharmony_ci I915_ENGINE_SAMPLE_COUNT); 75462306a36Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count)); 75562306a36Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample)); 75662306a36Sopenharmony_ci GEM_BUG_ON(engine->pmu.enable_count[sample] == ~0); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci engine->pmu.enable |= BIT(sample); 75962306a36Sopenharmony_ci engine->pmu.enable_count[sample]++; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ciupdate: 76562306a36Sopenharmony_ci /* 76662306a36Sopenharmony_ci * Store the current counter value so we can report the correct delta 76762306a36Sopenharmony_ci * for all listeners. Even when the event was already enabled and has 76862306a36Sopenharmony_ci * an existing non-zero value. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci local64_set(&event->hw.prev_count, __i915_pmu_event_read(event)); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic void i915_pmu_disable(struct perf_event *event) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct drm_i915_private *i915 = 77662306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 77762306a36Sopenharmony_ci const unsigned int bit = event_bit(event); 77862306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 77962306a36Sopenharmony_ci unsigned long flags; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (bit == -1) 78262306a36Sopenharmony_ci return; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (is_engine_event(event)) { 78762306a36Sopenharmony_ci u8 sample = engine_event_sample(event); 78862306a36Sopenharmony_ci struct intel_engine_cs *engine; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci engine = intel_engine_lookup_user(i915, 79162306a36Sopenharmony_ci engine_event_class(event), 79262306a36Sopenharmony_ci engine_event_instance(event)); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count)); 79562306a36Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample)); 79662306a36Sopenharmony_ci GEM_BUG_ON(engine->pmu.enable_count[sample] == 0); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* 79962306a36Sopenharmony_ci * Decrement the reference count and clear the enabled 80062306a36Sopenharmony_ci * bitmask when the last listener on an event goes away. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci if (--engine->pmu.enable_count[sample] == 0) 80362306a36Sopenharmony_ci engine->pmu.enable &= ~BIT(sample); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count)); 80762306a36Sopenharmony_ci GEM_BUG_ON(pmu->enable_count[bit] == 0); 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * Decrement the reference count and clear the enabled 81062306a36Sopenharmony_ci * bitmask when the last listener on an event goes away. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci if (--pmu->enable_count[bit] == 0) { 81362306a36Sopenharmony_ci pmu->enable &= ~BIT(bit); 81462306a36Sopenharmony_ci pmu->timer_enabled &= pmu_needs_timer(pmu); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic void i915_pmu_event_start(struct perf_event *event, int flags) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct drm_i915_private *i915 = 82362306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 82462306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (pmu->closed) 82762306a36Sopenharmony_ci return; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci i915_pmu_enable(event); 83062306a36Sopenharmony_ci event->hw.state = 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic void i915_pmu_event_stop(struct perf_event *event, int flags) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct drm_i915_private *i915 = 83662306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 83762306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (pmu->closed) 84062306a36Sopenharmony_ci goto out; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (flags & PERF_EF_UPDATE) 84362306a36Sopenharmony_ci i915_pmu_event_read(event); 84462306a36Sopenharmony_ci i915_pmu_disable(event); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ciout: 84762306a36Sopenharmony_ci event->hw.state = PERF_HES_STOPPED; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int i915_pmu_event_add(struct perf_event *event, int flags) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct drm_i915_private *i915 = 85362306a36Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 85462306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (pmu->closed) 85762306a36Sopenharmony_ci return -ENODEV; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (flags & PERF_EF_START) 86062306a36Sopenharmony_ci i915_pmu_event_start(event, flags); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void i915_pmu_event_del(struct perf_event *event, int flags) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci i915_pmu_event_stop(event, PERF_EF_UPDATE); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int i915_pmu_event_event_idx(struct perf_event *event) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci return 0; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistruct i915_str_attribute { 87662306a36Sopenharmony_ci struct device_attribute attr; 87762306a36Sopenharmony_ci const char *str; 87862306a36Sopenharmony_ci}; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic ssize_t i915_pmu_format_show(struct device *dev, 88162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct i915_str_attribute *eattr; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci eattr = container_of(attr, struct i915_str_attribute, attr); 88662306a36Sopenharmony_ci return sprintf(buf, "%s\n", eattr->str); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci#define I915_PMU_FORMAT_ATTR(_name, _config) \ 89062306a36Sopenharmony_ci (&((struct i915_str_attribute[]) { \ 89162306a36Sopenharmony_ci { .attr = __ATTR(_name, 0444, i915_pmu_format_show, NULL), \ 89262306a36Sopenharmony_ci .str = _config, } \ 89362306a36Sopenharmony_ci })[0].attr.attr) 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic struct attribute *i915_pmu_format_attrs[] = { 89662306a36Sopenharmony_ci I915_PMU_FORMAT_ATTR(i915_eventid, "config:0-20"), 89762306a36Sopenharmony_ci NULL, 89862306a36Sopenharmony_ci}; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic const struct attribute_group i915_pmu_format_attr_group = { 90162306a36Sopenharmony_ci .name = "format", 90262306a36Sopenharmony_ci .attrs = i915_pmu_format_attrs, 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistruct i915_ext_attribute { 90662306a36Sopenharmony_ci struct device_attribute attr; 90762306a36Sopenharmony_ci unsigned long val; 90862306a36Sopenharmony_ci}; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic ssize_t i915_pmu_event_show(struct device *dev, 91162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct i915_ext_attribute *eattr; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci eattr = container_of(attr, struct i915_ext_attribute, attr); 91662306a36Sopenharmony_ci return sprintf(buf, "config=0x%lx\n", eattr->val); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev, 92062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &i915_pmu_cpumask); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic struct attribute *i915_cpumask_attrs[] = { 92862306a36Sopenharmony_ci &dev_attr_cpumask.attr, 92962306a36Sopenharmony_ci NULL, 93062306a36Sopenharmony_ci}; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic const struct attribute_group i915_pmu_cpumask_attr_group = { 93362306a36Sopenharmony_ci .attrs = i915_cpumask_attrs, 93462306a36Sopenharmony_ci}; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci#define __event(__counter, __name, __unit) \ 93762306a36Sopenharmony_ci{ \ 93862306a36Sopenharmony_ci .counter = (__counter), \ 93962306a36Sopenharmony_ci .name = (__name), \ 94062306a36Sopenharmony_ci .unit = (__unit), \ 94162306a36Sopenharmony_ci .global = false, \ 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci#define __global_event(__counter, __name, __unit) \ 94562306a36Sopenharmony_ci{ \ 94662306a36Sopenharmony_ci .counter = (__counter), \ 94762306a36Sopenharmony_ci .name = (__name), \ 94862306a36Sopenharmony_ci .unit = (__unit), \ 94962306a36Sopenharmony_ci .global = true, \ 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci#define __engine_event(__sample, __name) \ 95362306a36Sopenharmony_ci{ \ 95462306a36Sopenharmony_ci .sample = (__sample), \ 95562306a36Sopenharmony_ci .name = (__name), \ 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic struct i915_ext_attribute * 95962306a36Sopenharmony_ciadd_i915_attr(struct i915_ext_attribute *attr, const char *name, u64 config) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci sysfs_attr_init(&attr->attr.attr); 96262306a36Sopenharmony_ci attr->attr.attr.name = name; 96362306a36Sopenharmony_ci attr->attr.attr.mode = 0444; 96462306a36Sopenharmony_ci attr->attr.show = i915_pmu_event_show; 96562306a36Sopenharmony_ci attr->val = config; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return ++attr; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic struct perf_pmu_events_attr * 97162306a36Sopenharmony_ciadd_pmu_attr(struct perf_pmu_events_attr *attr, const char *name, 97262306a36Sopenharmony_ci const char *str) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci sysfs_attr_init(&attr->attr.attr); 97562306a36Sopenharmony_ci attr->attr.attr.name = name; 97662306a36Sopenharmony_ci attr->attr.attr.mode = 0444; 97762306a36Sopenharmony_ci attr->attr.show = perf_event_sysfs_show; 97862306a36Sopenharmony_ci attr->event_str = str; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return ++attr; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic struct attribute ** 98462306a36Sopenharmony_cicreate_event_attributes(struct i915_pmu *pmu) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 98762306a36Sopenharmony_ci static const struct { 98862306a36Sopenharmony_ci unsigned int counter; 98962306a36Sopenharmony_ci const char *name; 99062306a36Sopenharmony_ci const char *unit; 99162306a36Sopenharmony_ci bool global; 99262306a36Sopenharmony_ci } events[] = { 99362306a36Sopenharmony_ci __event(0, "actual-frequency", "M"), 99462306a36Sopenharmony_ci __event(1, "requested-frequency", "M"), 99562306a36Sopenharmony_ci __global_event(2, "interrupts", NULL), 99662306a36Sopenharmony_ci __event(3, "rc6-residency", "ns"), 99762306a36Sopenharmony_ci __event(4, "software-gt-awake-time", "ns"), 99862306a36Sopenharmony_ci }; 99962306a36Sopenharmony_ci static const struct { 100062306a36Sopenharmony_ci enum drm_i915_pmu_engine_sample sample; 100162306a36Sopenharmony_ci char *name; 100262306a36Sopenharmony_ci } engine_events[] = { 100362306a36Sopenharmony_ci __engine_event(I915_SAMPLE_BUSY, "busy"), 100462306a36Sopenharmony_ci __engine_event(I915_SAMPLE_SEMA, "sema"), 100562306a36Sopenharmony_ci __engine_event(I915_SAMPLE_WAIT, "wait"), 100662306a36Sopenharmony_ci }; 100762306a36Sopenharmony_ci unsigned int count = 0; 100862306a36Sopenharmony_ci struct perf_pmu_events_attr *pmu_attr = NULL, *pmu_iter; 100962306a36Sopenharmony_ci struct i915_ext_attribute *i915_attr = NULL, *i915_iter; 101062306a36Sopenharmony_ci struct attribute **attr = NULL, **attr_iter; 101162306a36Sopenharmony_ci struct intel_engine_cs *engine; 101262306a36Sopenharmony_ci struct intel_gt *gt; 101362306a36Sopenharmony_ci unsigned int i, j; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* Count how many counters we will be exposing. */ 101662306a36Sopenharmony_ci for_each_gt(gt, i915, j) { 101762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(events); i++) { 101862306a36Sopenharmony_ci u64 config = ___I915_PMU_OTHER(j, events[i].counter); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (!config_status(i915, config)) 102162306a36Sopenharmony_ci count++; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci for_each_uabi_engine(engine, i915) { 102662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(engine_events); i++) { 102762306a36Sopenharmony_ci if (!engine_event_status(engine, 102862306a36Sopenharmony_ci engine_events[i].sample)) 102962306a36Sopenharmony_ci count++; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Allocate attribute objects and table. */ 103462306a36Sopenharmony_ci i915_attr = kcalloc(count, sizeof(*i915_attr), GFP_KERNEL); 103562306a36Sopenharmony_ci if (!i915_attr) 103662306a36Sopenharmony_ci goto err_alloc; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci pmu_attr = kcalloc(count, sizeof(*pmu_attr), GFP_KERNEL); 103962306a36Sopenharmony_ci if (!pmu_attr) 104062306a36Sopenharmony_ci goto err_alloc; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Max one pointer of each attribute type plus a termination entry. */ 104362306a36Sopenharmony_ci attr = kcalloc(count * 2 + 1, sizeof(*attr), GFP_KERNEL); 104462306a36Sopenharmony_ci if (!attr) 104562306a36Sopenharmony_ci goto err_alloc; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci i915_iter = i915_attr; 104862306a36Sopenharmony_ci pmu_iter = pmu_attr; 104962306a36Sopenharmony_ci attr_iter = attr; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* Initialize supported non-engine counters. */ 105262306a36Sopenharmony_ci for_each_gt(gt, i915, j) { 105362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(events); i++) { 105462306a36Sopenharmony_ci u64 config = ___I915_PMU_OTHER(j, events[i].counter); 105562306a36Sopenharmony_ci char *str; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (config_status(i915, config)) 105862306a36Sopenharmony_ci continue; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (events[i].global || !HAS_EXTRA_GT_LIST(i915)) 106162306a36Sopenharmony_ci str = kstrdup(events[i].name, GFP_KERNEL); 106262306a36Sopenharmony_ci else 106362306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-gt%u", 106462306a36Sopenharmony_ci events[i].name, j); 106562306a36Sopenharmony_ci if (!str) 106662306a36Sopenharmony_ci goto err; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci *attr_iter++ = &i915_iter->attr.attr; 106962306a36Sopenharmony_ci i915_iter = add_i915_attr(i915_iter, str, config); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (events[i].unit) { 107262306a36Sopenharmony_ci if (events[i].global || !HAS_EXTRA_GT_LIST(i915)) 107362306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s.unit", 107462306a36Sopenharmony_ci events[i].name); 107562306a36Sopenharmony_ci else 107662306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-gt%u.unit", 107762306a36Sopenharmony_ci events[i].name, j); 107862306a36Sopenharmony_ci if (!str) 107962306a36Sopenharmony_ci goto err; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci *attr_iter++ = &pmu_iter->attr.attr; 108262306a36Sopenharmony_ci pmu_iter = add_pmu_attr(pmu_iter, str, 108362306a36Sopenharmony_ci events[i].unit); 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* Initialize supported engine counters. */ 108962306a36Sopenharmony_ci for_each_uabi_engine(engine, i915) { 109062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(engine_events); i++) { 109162306a36Sopenharmony_ci char *str; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (engine_event_status(engine, 109462306a36Sopenharmony_ci engine_events[i].sample)) 109562306a36Sopenharmony_ci continue; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-%s", 109862306a36Sopenharmony_ci engine->name, engine_events[i].name); 109962306a36Sopenharmony_ci if (!str) 110062306a36Sopenharmony_ci goto err; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci *attr_iter++ = &i915_iter->attr.attr; 110362306a36Sopenharmony_ci i915_iter = 110462306a36Sopenharmony_ci add_i915_attr(i915_iter, str, 110562306a36Sopenharmony_ci __I915_PMU_ENGINE(engine->uabi_class, 110662306a36Sopenharmony_ci engine->uabi_instance, 110762306a36Sopenharmony_ci engine_events[i].sample)); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-%s.unit", 111062306a36Sopenharmony_ci engine->name, engine_events[i].name); 111162306a36Sopenharmony_ci if (!str) 111262306a36Sopenharmony_ci goto err; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci *attr_iter++ = &pmu_iter->attr.attr; 111562306a36Sopenharmony_ci pmu_iter = add_pmu_attr(pmu_iter, str, "ns"); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci pmu->i915_attr = i915_attr; 112062306a36Sopenharmony_ci pmu->pmu_attr = pmu_attr; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci return attr; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cierr:; 112562306a36Sopenharmony_ci for (attr_iter = attr; *attr_iter; attr_iter++) 112662306a36Sopenharmony_ci kfree((*attr_iter)->name); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cierr_alloc: 112962306a36Sopenharmony_ci kfree(attr); 113062306a36Sopenharmony_ci kfree(i915_attr); 113162306a36Sopenharmony_ci kfree(pmu_attr); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci return NULL; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic void free_event_attributes(struct i915_pmu *pmu) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct attribute **attr_iter = pmu->events_attr_group.attrs; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci for (; *attr_iter; attr_iter++) 114162306a36Sopenharmony_ci kfree((*attr_iter)->name); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci kfree(pmu->events_attr_group.attrs); 114462306a36Sopenharmony_ci kfree(pmu->i915_attr); 114562306a36Sopenharmony_ci kfree(pmu->pmu_attr); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci pmu->events_attr_group.attrs = NULL; 114862306a36Sopenharmony_ci pmu->i915_attr = NULL; 114962306a36Sopenharmony_ci pmu->pmu_attr = NULL; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic int i915_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), cpuhp.node); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci GEM_BUG_ON(!pmu->base.event_init); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Select the first online CPU as a designated reader. */ 115962306a36Sopenharmony_ci if (cpumask_empty(&i915_pmu_cpumask)) 116062306a36Sopenharmony_ci cpumask_set_cpu(cpu, &i915_pmu_cpumask); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic int i915_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), cpuhp.node); 116862306a36Sopenharmony_ci unsigned int target = i915_pmu_target_cpu; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci GEM_BUG_ON(!pmu->base.event_init); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* 117362306a36Sopenharmony_ci * Unregistering an instance generates a CPU offline event which we must 117462306a36Sopenharmony_ci * ignore to avoid incorrectly modifying the shared i915_pmu_cpumask. 117562306a36Sopenharmony_ci */ 117662306a36Sopenharmony_ci if (pmu->closed) 117762306a36Sopenharmony_ci return 0; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (cpumask_test_and_clear_cpu(cpu, &i915_pmu_cpumask)) { 118062306a36Sopenharmony_ci target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci /* Migrate events if there is a valid target */ 118362306a36Sopenharmony_ci if (target < nr_cpu_ids) { 118462306a36Sopenharmony_ci cpumask_set_cpu(target, &i915_pmu_cpumask); 118562306a36Sopenharmony_ci i915_pmu_target_cpu = target; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (target < nr_cpu_ids && target != pmu->cpuhp.cpu) { 119062306a36Sopenharmony_ci perf_pmu_migrate_context(&pmu->base, cpu, target); 119162306a36Sopenharmony_ci pmu->cpuhp.cpu = target; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci return 0; 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic enum cpuhp_state cpuhp_slot = CPUHP_INVALID; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ciint i915_pmu_init(void) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci int ret; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 120462306a36Sopenharmony_ci "perf/x86/intel/i915:online", 120562306a36Sopenharmony_ci i915_pmu_cpu_online, 120662306a36Sopenharmony_ci i915_pmu_cpu_offline); 120762306a36Sopenharmony_ci if (ret < 0) 120862306a36Sopenharmony_ci pr_notice("Failed to setup cpuhp state for i915 PMU! (%d)\n", 120962306a36Sopenharmony_ci ret); 121062306a36Sopenharmony_ci else 121162306a36Sopenharmony_ci cpuhp_slot = ret; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return 0; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_civoid i915_pmu_exit(void) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci if (cpuhp_slot != CPUHP_INVALID) 121962306a36Sopenharmony_ci cpuhp_remove_multi_state(cpuhp_slot); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int i915_pmu_register_cpuhp_state(struct i915_pmu *pmu) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci if (cpuhp_slot == CPUHP_INVALID) 122562306a36Sopenharmony_ci return -EINVAL; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return cpuhp_state_add_instance(cpuhp_slot, &pmu->cpuhp.node); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void i915_pmu_unregister_cpuhp_state(struct i915_pmu *pmu) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci cpuhp_state_remove_instance(cpuhp_slot, &pmu->cpuhp.node); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic bool is_igp(struct drm_i915_private *i915) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(i915->drm.dev); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* IGP is 0000:00:02.0 */ 124062306a36Sopenharmony_ci return pci_domain_nr(pdev->bus) == 0 && 124162306a36Sopenharmony_ci pdev->bus->number == 0 && 124262306a36Sopenharmony_ci PCI_SLOT(pdev->devfn) == 2 && 124362306a36Sopenharmony_ci PCI_FUNC(pdev->devfn) == 0; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_civoid i915_pmu_register(struct drm_i915_private *i915) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 124962306a36Sopenharmony_ci const struct attribute_group *attr_groups[] = { 125062306a36Sopenharmony_ci &i915_pmu_format_attr_group, 125162306a36Sopenharmony_ci &pmu->events_attr_group, 125262306a36Sopenharmony_ci &i915_pmu_cpumask_attr_group, 125362306a36Sopenharmony_ci NULL 125462306a36Sopenharmony_ci }; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci int ret = -ENOMEM; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (GRAPHICS_VER(i915) <= 2) { 125962306a36Sopenharmony_ci drm_info(&i915->drm, "PMU not supported for this GPU."); 126062306a36Sopenharmony_ci return; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci spin_lock_init(&pmu->lock); 126462306a36Sopenharmony_ci hrtimer_init(&pmu->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 126562306a36Sopenharmony_ci pmu->timer.function = i915_sample; 126662306a36Sopenharmony_ci pmu->cpuhp.cpu = -1; 126762306a36Sopenharmony_ci init_rc6(pmu); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (!is_igp(i915)) { 127062306a36Sopenharmony_ci pmu->name = kasprintf(GFP_KERNEL, 127162306a36Sopenharmony_ci "i915_%s", 127262306a36Sopenharmony_ci dev_name(i915->drm.dev)); 127362306a36Sopenharmony_ci if (pmu->name) { 127462306a36Sopenharmony_ci /* tools/perf reserves colons as special. */ 127562306a36Sopenharmony_ci strreplace((char *)pmu->name, ':', '_'); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci } else { 127862306a36Sopenharmony_ci pmu->name = "i915"; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci if (!pmu->name) 128162306a36Sopenharmony_ci goto err; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci pmu->events_attr_group.name = "events"; 128462306a36Sopenharmony_ci pmu->events_attr_group.attrs = create_event_attributes(pmu); 128562306a36Sopenharmony_ci if (!pmu->events_attr_group.attrs) 128662306a36Sopenharmony_ci goto err_name; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci pmu->base.attr_groups = kmemdup(attr_groups, sizeof(attr_groups), 128962306a36Sopenharmony_ci GFP_KERNEL); 129062306a36Sopenharmony_ci if (!pmu->base.attr_groups) 129162306a36Sopenharmony_ci goto err_attr; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci pmu->base.module = THIS_MODULE; 129462306a36Sopenharmony_ci pmu->base.task_ctx_nr = perf_invalid_context; 129562306a36Sopenharmony_ci pmu->base.event_init = i915_pmu_event_init; 129662306a36Sopenharmony_ci pmu->base.add = i915_pmu_event_add; 129762306a36Sopenharmony_ci pmu->base.del = i915_pmu_event_del; 129862306a36Sopenharmony_ci pmu->base.start = i915_pmu_event_start; 129962306a36Sopenharmony_ci pmu->base.stop = i915_pmu_event_stop; 130062306a36Sopenharmony_ci pmu->base.read = i915_pmu_event_read; 130162306a36Sopenharmony_ci pmu->base.event_idx = i915_pmu_event_event_idx; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci ret = perf_pmu_register(&pmu->base, pmu->name, -1); 130462306a36Sopenharmony_ci if (ret) 130562306a36Sopenharmony_ci goto err_groups; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci ret = i915_pmu_register_cpuhp_state(pmu); 130862306a36Sopenharmony_ci if (ret) 130962306a36Sopenharmony_ci goto err_unreg; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci return; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cierr_unreg: 131462306a36Sopenharmony_ci perf_pmu_unregister(&pmu->base); 131562306a36Sopenharmony_cierr_groups: 131662306a36Sopenharmony_ci kfree(pmu->base.attr_groups); 131762306a36Sopenharmony_cierr_attr: 131862306a36Sopenharmony_ci pmu->base.event_init = NULL; 131962306a36Sopenharmony_ci free_event_attributes(pmu); 132062306a36Sopenharmony_cierr_name: 132162306a36Sopenharmony_ci if (!is_igp(i915)) 132262306a36Sopenharmony_ci kfree(pmu->name); 132362306a36Sopenharmony_cierr: 132462306a36Sopenharmony_ci drm_notice(&i915->drm, "Failed to register PMU!\n"); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_civoid i915_pmu_unregister(struct drm_i915_private *i915) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (!pmu->base.event_init) 133262306a36Sopenharmony_ci return; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci * "Disconnect" the PMU callbacks - since all are atomic synchronize_rcu 133662306a36Sopenharmony_ci * ensures all currently executing ones will have exited before we 133762306a36Sopenharmony_ci * proceed with unregistration. 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_ci pmu->closed = true; 134062306a36Sopenharmony_ci synchronize_rcu(); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci hrtimer_cancel(&pmu->timer); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci i915_pmu_unregister_cpuhp_state(pmu); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci perf_pmu_unregister(&pmu->base); 134762306a36Sopenharmony_ci pmu->base.event_init = NULL; 134862306a36Sopenharmony_ci kfree(pmu->base.attr_groups); 134962306a36Sopenharmony_ci if (!is_igp(i915)) 135062306a36Sopenharmony_ci kfree(pmu->name); 135162306a36Sopenharmony_ci free_event_attributes(pmu); 135262306a36Sopenharmony_ci} 1353