18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2017-2018 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/irq.h> 88c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "gt/intel_engine.h" 118c2ecf20Sopenharmony_ci#include "gt/intel_engine_pm.h" 128c2ecf20Sopenharmony_ci#include "gt/intel_engine_user.h" 138c2ecf20Sopenharmony_ci#include "gt/intel_gt_pm.h" 148c2ecf20Sopenharmony_ci#include "gt/intel_rc6.h" 158c2ecf20Sopenharmony_ci#include "gt/intel_rps.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "i915_drv.h" 188c2ecf20Sopenharmony_ci#include "i915_pmu.h" 198c2ecf20Sopenharmony_ci#include "intel_pm.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Frequency for the sampling timer for events which need it. */ 228c2ecf20Sopenharmony_ci#define FREQUENCY 200 238c2ecf20Sopenharmony_ci#define PERIOD max_t(u64, 10000, NSEC_PER_SEC / FREQUENCY) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define ENGINE_SAMPLE_MASK \ 268c2ecf20Sopenharmony_ci (BIT(I915_SAMPLE_BUSY) | \ 278c2ecf20Sopenharmony_ci BIT(I915_SAMPLE_WAIT) | \ 288c2ecf20Sopenharmony_ci BIT(I915_SAMPLE_SEMA)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define ENGINE_SAMPLE_BITS (1 << I915_PMU_SAMPLE_BITS) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic cpumask_t i915_pmu_cpumask; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic u8 engine_config_sample(u64 config) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci return config & I915_PMU_SAMPLE_MASK; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic u8 engine_event_sample(struct perf_event *event) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return engine_config_sample(event->attr.config); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic u8 engine_event_class(struct perf_event *event) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return (event->attr.config >> I915_PMU_CLASS_SHIFT) & 0xff; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic u8 engine_event_instance(struct perf_event *event) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return (event->attr.config >> I915_PMU_SAMPLE_BITS) & 0xff; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic bool is_engine_config(u64 config) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return config < __I915_PMU_OTHER(0); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned int config_enabled_bit(u64 config) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci if (is_engine_config(config)) 628c2ecf20Sopenharmony_ci return engine_config_sample(config); 638c2ecf20Sopenharmony_ci else 648c2ecf20Sopenharmony_ci return ENGINE_SAMPLE_BITS + (config - __I915_PMU_OTHER(0)); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic u64 config_enabled_mask(u64 config) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return BIT_ULL(config_enabled_bit(config)); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic bool is_engine_event(struct perf_event *event) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return is_engine_config(event->attr.config); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic unsigned int event_enabled_bit(struct perf_event *event) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return config_enabled_bit(event->attr.config); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic bool pmu_needs_timer(struct i915_pmu *pmu, bool gpu_active) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 858c2ecf20Sopenharmony_ci u64 enable; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * Only some counters need the sampling timer. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * We start with a bitmask of all currently enabled events. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci enable = pmu->enable; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Mask out all the ones which do not need the timer, or in 968c2ecf20Sopenharmony_ci * other words keep all the ones that could need the timer. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci enable &= config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY) | 998c2ecf20Sopenharmony_ci config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY) | 1008c2ecf20Sopenharmony_ci ENGINE_SAMPLE_MASK; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * When the GPU is idle per-engine counters do not need to be 1048c2ecf20Sopenharmony_ci * running so clear those bits out. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci if (!gpu_active) 1078c2ecf20Sopenharmony_ci enable &= ~ENGINE_SAMPLE_MASK; 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Also there is software busyness tracking available we do not 1108c2ecf20Sopenharmony_ci * need the timer for I915_SAMPLE_BUSY counter. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci else if (i915->caps.scheduler & I915_SCHEDULER_CAP_ENGINE_BUSY_STATS) 1138c2ecf20Sopenharmony_ci enable &= ~BIT(I915_SAMPLE_BUSY); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * If some bits remain it means we need the sampling timer running. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci return enable; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic u64 __get_rc6(struct intel_gt *gt) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 1248c2ecf20Sopenharmony_ci u64 val; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci val = intel_rc6_residency_ns(>->rc6, 1278c2ecf20Sopenharmony_ci IS_VALLEYVIEW(i915) ? 1288c2ecf20Sopenharmony_ci VLV_GT_RENDER_RC6 : 1298c2ecf20Sopenharmony_ci GEN6_GT_GFX_RC6); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (HAS_RC6p(i915)) 1328c2ecf20Sopenharmony_ci val += intel_rc6_residency_ns(>->rc6, GEN6_GT_GFX_RC6p); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (HAS_RC6pp(i915)) 1358c2ecf20Sopenharmony_ci val += intel_rc6_residency_ns(>->rc6, GEN6_GT_GFX_RC6pp); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return val; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PM) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic inline s64 ktime_since(const ktime_t kt) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci return ktime_to_ns(ktime_sub(ktime_get(), kt)); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic u64 get_rc6(struct intel_gt *gt) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 1508c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 1518c2ecf20Sopenharmony_ci unsigned long flags; 1528c2ecf20Sopenharmony_ci bool awake = false; 1538c2ecf20Sopenharmony_ci u64 val; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (intel_gt_pm_get_if_awake(gt)) { 1568c2ecf20Sopenharmony_ci val = __get_rc6(gt); 1578c2ecf20Sopenharmony_ci intel_gt_pm_put_async(gt); 1588c2ecf20Sopenharmony_ci awake = true; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (awake) { 1648c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6].cur = val; 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * We think we are runtime suspended. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Report the delta from when the device was suspended to now, 1708c2ecf20Sopenharmony_ci * on top of the last known real value, as the approximated RC6 1718c2ecf20Sopenharmony_ci * counter value. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci val = ktime_since(pmu->sleep_last); 1748c2ecf20Sopenharmony_ci val += pmu->sample[__I915_SAMPLE_RC6].cur; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (val < pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur) 1788c2ecf20Sopenharmony_ci val = pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur = val; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return val; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void init_rc6(struct i915_pmu *pmu) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 1908c2ecf20Sopenharmony_ci intel_wakeref_t wakeref; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci with_intel_runtime_pm(i915->gt.uncore->rpm, wakeref) { 1938c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt); 1948c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur = 1958c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6].cur; 1968c2ecf20Sopenharmony_ci pmu->sleep_last = ktime_get(); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void park_rc6(struct drm_i915_private *i915) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt); 2058c2ecf20Sopenharmony_ci pmu->sleep_last = ktime_get(); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#else 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic u64 get_rc6(struct intel_gt *gt) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci return __get_rc6(gt); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void init_rc6(struct i915_pmu *pmu) { } 2168c2ecf20Sopenharmony_cistatic void park_rc6(struct drm_i915_private *i915) {} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#endif 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void __i915_pmu_maybe_start_timer(struct i915_pmu *pmu) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if (!pmu->timer_enabled && pmu_needs_timer(pmu, true)) { 2238c2ecf20Sopenharmony_ci pmu->timer_enabled = true; 2248c2ecf20Sopenharmony_ci pmu->timer_last = ktime_get(); 2258c2ecf20Sopenharmony_ci hrtimer_start_range_ns(&pmu->timer, 2268c2ecf20Sopenharmony_ci ns_to_ktime(PERIOD), 0, 2278c2ecf20Sopenharmony_ci HRTIMER_MODE_REL_PINNED); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_civoid i915_pmu_gt_parked(struct drm_i915_private *i915) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (!pmu->base.event_init) 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci spin_lock_irq(&pmu->lock); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci park_rc6(i915); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * Signal sampling timer to stop if only engine events are enabled and 2448c2ecf20Sopenharmony_ci * GPU went idle. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci pmu->timer_enabled = pmu_needs_timer(pmu, false); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci spin_unlock_irq(&pmu->lock); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_civoid i915_pmu_gt_unparked(struct drm_i915_private *i915) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (!pmu->base.event_init) 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_lock_irq(&pmu->lock); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Re-enable sampling timer when GPU goes active. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci __i915_pmu_maybe_start_timer(pmu); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_unlock_irq(&pmu->lock); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void 2698c2ecf20Sopenharmony_ciadd_sample(struct i915_pmu_sample *sample, u32 val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci sample->cur += val; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic bool exclusive_mmio_access(const struct drm_i915_private *i915) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * We have to avoid concurrent mmio cache line access on gen7 or 2788c2ecf20Sopenharmony_ci * risk a machine hang. For a fun history lesson dig out the old 2798c2ecf20Sopenharmony_ci * userspace intel_gpu_top and run it on Ivybridge or Haswell! 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci return IS_GEN(i915, 7); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void engine_sample(struct intel_engine_cs *engine, unsigned int period_ns) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct intel_engine_pmu *pmu = &engine->pmu; 2878c2ecf20Sopenharmony_ci bool busy; 2888c2ecf20Sopenharmony_ci u32 val; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci val = ENGINE_READ_FW(engine, RING_CTL); 2918c2ecf20Sopenharmony_ci if (val == 0) /* powerwell off => engine idle */ 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (val & RING_WAIT) 2958c2ecf20Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_WAIT], period_ns); 2968c2ecf20Sopenharmony_ci if (val & RING_WAIT_SEMAPHORE) 2978c2ecf20Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_SEMA], period_ns); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* No need to sample when busy stats are supported. */ 3008c2ecf20Sopenharmony_ci if (intel_engine_supports_stats(engine)) 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * While waiting on a semaphore or event, MI_MODE reports the 3058c2ecf20Sopenharmony_ci * ring as idle. However, previously using the seqno, and with 3068c2ecf20Sopenharmony_ci * execlists sampling, we account for the ring waiting as the 3078c2ecf20Sopenharmony_ci * engine being busy. Therefore, we record the sample as being 3088c2ecf20Sopenharmony_ci * busy if either waiting or !idle. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci busy = val & (RING_WAIT_SEMAPHORE | RING_WAIT); 3118c2ecf20Sopenharmony_ci if (!busy) { 3128c2ecf20Sopenharmony_ci val = ENGINE_READ_FW(engine, RING_MI_MODE); 3138c2ecf20Sopenharmony_ci busy = !(val & MODE_IDLE); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci if (busy) 3168c2ecf20Sopenharmony_ci add_sample(&pmu->sample[I915_SAMPLE_BUSY], period_ns); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void 3208c2ecf20Sopenharmony_ciengines_sample(struct intel_gt *gt, unsigned int period_ns) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 3238c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 3248c2ecf20Sopenharmony_ci enum intel_engine_id id; 3258c2ecf20Sopenharmony_ci unsigned long flags; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if ((i915->pmu.enable & ENGINE_SAMPLE_MASK) == 0) 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!intel_gt_pm_is_awake(gt)) 3318c2ecf20Sopenharmony_ci return; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for_each_engine(engine, gt, id) { 3348c2ecf20Sopenharmony_ci if (!intel_engine_pm_get_if_awake(engine)) 3358c2ecf20Sopenharmony_ci continue; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (exclusive_mmio_access(i915)) { 3388c2ecf20Sopenharmony_ci spin_lock_irqsave(&engine->uncore->lock, flags); 3398c2ecf20Sopenharmony_ci engine_sample(engine, period_ns); 3408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&engine->uncore->lock, flags); 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci engine_sample(engine, period_ns); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci intel_engine_pm_put_async(engine); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void 3508c2ecf20Sopenharmony_ciadd_sample_mult(struct i915_pmu_sample *sample, u32 val, u32 mul) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci sample->cur += mul_u32_u32(val, mul); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic bool frequency_sampling_enabled(struct i915_pmu *pmu) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci return pmu->enable & 3588c2ecf20Sopenharmony_ci (config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY) | 3598c2ecf20Sopenharmony_ci config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY)); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void 3638c2ecf20Sopenharmony_cifrequency_sample(struct intel_gt *gt, unsigned int period_ns) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 3668c2ecf20Sopenharmony_ci struct intel_uncore *uncore = gt->uncore; 3678c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 3688c2ecf20Sopenharmony_ci struct intel_rps *rps = >->rps; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!frequency_sampling_enabled(pmu)) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Report 0/0 (actual/requested) frequency while parked. */ 3748c2ecf20Sopenharmony_ci if (!intel_gt_pm_get_if_awake(gt)) 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (pmu->enable & config_enabled_mask(I915_PMU_ACTUAL_FREQUENCY)) { 3788c2ecf20Sopenharmony_ci u32 val; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * We take a quick peek here without using forcewake 3828c2ecf20Sopenharmony_ci * so that we don't perturb the system under observation 3838c2ecf20Sopenharmony_ci * (forcewake => !rc6 => increased power use). We expect 3848c2ecf20Sopenharmony_ci * that if the read fails because it is outside of the 3858c2ecf20Sopenharmony_ci * mmio power well, then it will return 0 -- in which 3868c2ecf20Sopenharmony_ci * case we assume the system is running at the intended 3878c2ecf20Sopenharmony_ci * frequency. Fortunately, the read should rarely fail! 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci val = intel_uncore_read_fw(uncore, GEN6_RPSTAT1); 3908c2ecf20Sopenharmony_ci if (val) 3918c2ecf20Sopenharmony_ci val = intel_rps_get_cagf(rps, val); 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci val = rps->cur_freq; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci add_sample_mult(&pmu->sample[__I915_SAMPLE_FREQ_ACT], 3968c2ecf20Sopenharmony_ci intel_gpu_freq(rps, val), period_ns / 1000); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (pmu->enable & config_enabled_mask(I915_PMU_REQUESTED_FREQUENCY)) { 4008c2ecf20Sopenharmony_ci add_sample_mult(&pmu->sample[__I915_SAMPLE_FREQ_REQ], 4018c2ecf20Sopenharmony_ci intel_gpu_freq(rps, rps->cur_freq), 4028c2ecf20Sopenharmony_ci period_ns / 1000); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci intel_gt_pm_put_async(gt); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic enum hrtimer_restart i915_sample(struct hrtimer *hrtimer) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 4118c2ecf20Sopenharmony_ci container_of(hrtimer, struct drm_i915_private, pmu.timer); 4128c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 4138c2ecf20Sopenharmony_ci struct intel_gt *gt = &i915->gt; 4148c2ecf20Sopenharmony_ci unsigned int period_ns; 4158c2ecf20Sopenharmony_ci ktime_t now; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (!READ_ONCE(pmu->timer_enabled)) 4188c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci now = ktime_get(); 4218c2ecf20Sopenharmony_ci period_ns = ktime_to_ns(ktime_sub(now, pmu->timer_last)); 4228c2ecf20Sopenharmony_ci pmu->timer_last = now; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* 4258c2ecf20Sopenharmony_ci * Strictly speaking the passed in period may not be 100% accurate for 4268c2ecf20Sopenharmony_ci * all internal calculation, since some amount of time can be spent on 4278c2ecf20Sopenharmony_ci * grabbing the forcewake. However the potential error from timer call- 4288c2ecf20Sopenharmony_ci * back delay greatly dominates this so we keep it simple. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci engines_sample(gt, period_ns); 4318c2ecf20Sopenharmony_ci frequency_sample(gt, period_ns); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci hrtimer_forward(hrtimer, now, ns_to_ktime(PERIOD)); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic u64 count_interrupts(struct drm_i915_private *i915) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci /* open-coded kstat_irqs() */ 4418c2ecf20Sopenharmony_ci struct irq_desc *desc = irq_to_desc(i915->drm.pdev->irq); 4428c2ecf20Sopenharmony_ci u64 sum = 0; 4438c2ecf20Sopenharmony_ci int cpu; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!desc || !desc->kstat_irqs) 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) 4498c2ecf20Sopenharmony_ci sum += *per_cpu_ptr(desc->kstat_irqs, cpu); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return sum; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void i915_pmu_event_destroy(struct perf_event *event) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 4578c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci drm_WARN_ON(&i915->drm, event->parent); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int 4638c2ecf20Sopenharmony_ciengine_event_status(struct intel_engine_cs *engine, 4648c2ecf20Sopenharmony_ci enum drm_i915_pmu_engine_sample sample) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci switch (sample) { 4678c2ecf20Sopenharmony_ci case I915_SAMPLE_BUSY: 4688c2ecf20Sopenharmony_ci case I915_SAMPLE_WAIT: 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci case I915_SAMPLE_SEMA: 4718c2ecf20Sopenharmony_ci if (INTEL_GEN(engine->i915) < 6) 4728c2ecf20Sopenharmony_ci return -ENODEV; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci default: 4758c2ecf20Sopenharmony_ci return -ENOENT; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int 4828c2ecf20Sopenharmony_ciconfig_status(struct drm_i915_private *i915, u64 config) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci switch (config) { 4858c2ecf20Sopenharmony_ci case I915_PMU_ACTUAL_FREQUENCY: 4868c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) 4878c2ecf20Sopenharmony_ci /* Requires a mutex for sampling! */ 4888c2ecf20Sopenharmony_ci return -ENODEV; 4898c2ecf20Sopenharmony_ci fallthrough; 4908c2ecf20Sopenharmony_ci case I915_PMU_REQUESTED_FREQUENCY: 4918c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) < 6) 4928c2ecf20Sopenharmony_ci return -ENODEV; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case I915_PMU_INTERRUPTS: 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci case I915_PMU_RC6_RESIDENCY: 4978c2ecf20Sopenharmony_ci if (!HAS_RC6(i915)) 4988c2ecf20Sopenharmony_ci return -ENODEV; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci default: 5018c2ecf20Sopenharmony_ci return -ENOENT; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int engine_event_init(struct perf_event *event) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 5108c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 5118c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci engine = intel_engine_lookup_user(i915, engine_event_class(event), 5148c2ecf20Sopenharmony_ci engine_event_instance(event)); 5158c2ecf20Sopenharmony_ci if (!engine) 5168c2ecf20Sopenharmony_ci return -ENODEV; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return engine_event_status(engine, engine_event_sample(event)); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int i915_pmu_event_init(struct perf_event *event) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 5248c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 5258c2ecf20Sopenharmony_ci int ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (event->attr.type != event->pmu->type) 5288c2ecf20Sopenharmony_ci return -ENOENT; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* unsupported modes and filters */ 5318c2ecf20Sopenharmony_ci if (event->attr.sample_period) /* no sampling */ 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (has_branch_stack(event)) 5358c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (event->cpu < 0) 5388c2ecf20Sopenharmony_ci return -EINVAL; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* only allow running on one cpu at a time */ 5418c2ecf20Sopenharmony_ci if (!cpumask_test_cpu(event->cpu, &i915_pmu_cpumask)) 5428c2ecf20Sopenharmony_ci return -EINVAL; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (is_engine_event(event)) 5458c2ecf20Sopenharmony_ci ret = engine_event_init(event); 5468c2ecf20Sopenharmony_ci else 5478c2ecf20Sopenharmony_ci ret = config_status(i915, event->attr.config); 5488c2ecf20Sopenharmony_ci if (ret) 5498c2ecf20Sopenharmony_ci return ret; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!event->parent) 5528c2ecf20Sopenharmony_ci event->destroy = i915_pmu_event_destroy; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic u64 __i915_pmu_event_read(struct perf_event *event) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 5608c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 5618c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 5628c2ecf20Sopenharmony_ci u64 val = 0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (is_engine_event(event)) { 5658c2ecf20Sopenharmony_ci u8 sample = engine_event_sample(event); 5668c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci engine = intel_engine_lookup_user(i915, 5698c2ecf20Sopenharmony_ci engine_event_class(event), 5708c2ecf20Sopenharmony_ci engine_event_instance(event)); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (drm_WARN_ON_ONCE(&i915->drm, !engine)) { 5738c2ecf20Sopenharmony_ci /* Do nothing */ 5748c2ecf20Sopenharmony_ci } else if (sample == I915_SAMPLE_BUSY && 5758c2ecf20Sopenharmony_ci intel_engine_supports_stats(engine)) { 5768c2ecf20Sopenharmony_ci ktime_t unused; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci val = ktime_to_ns(intel_engine_get_busy_time(engine, 5798c2ecf20Sopenharmony_ci &unused)); 5808c2ecf20Sopenharmony_ci } else { 5818c2ecf20Sopenharmony_ci val = engine->pmu.sample[sample].cur; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci switch (event->attr.config) { 5858c2ecf20Sopenharmony_ci case I915_PMU_ACTUAL_FREQUENCY: 5868c2ecf20Sopenharmony_ci val = 5878c2ecf20Sopenharmony_ci div_u64(pmu->sample[__I915_SAMPLE_FREQ_ACT].cur, 5888c2ecf20Sopenharmony_ci USEC_PER_SEC /* to MHz */); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case I915_PMU_REQUESTED_FREQUENCY: 5918c2ecf20Sopenharmony_ci val = 5928c2ecf20Sopenharmony_ci div_u64(pmu->sample[__I915_SAMPLE_FREQ_REQ].cur, 5938c2ecf20Sopenharmony_ci USEC_PER_SEC /* to MHz */); 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case I915_PMU_INTERRUPTS: 5968c2ecf20Sopenharmony_ci val = count_interrupts(i915); 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case I915_PMU_RC6_RESIDENCY: 5998c2ecf20Sopenharmony_ci val = get_rc6(&i915->gt); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return val; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void i915_pmu_event_read(struct perf_event *event) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 6108c2ecf20Sopenharmony_ci u64 prev, new; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciagain: 6138c2ecf20Sopenharmony_ci prev = local64_read(&hwc->prev_count); 6148c2ecf20Sopenharmony_ci new = __i915_pmu_event_read(event); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (local64_cmpxchg(&hwc->prev_count, prev, new) != prev) 6178c2ecf20Sopenharmony_ci goto again; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci local64_add(new - prev, &event->count); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void i915_pmu_enable(struct perf_event *event) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 6258c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 6268c2ecf20Sopenharmony_ci unsigned int bit = event_enabled_bit(event); 6278c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 6288c2ecf20Sopenharmony_ci unsigned long flags; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * Update the bitmask of enabled events and increment 6348c2ecf20Sopenharmony_ci * the event reference counter. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(pmu->enable_count) != I915_PMU_MASK_BITS); 6378c2ecf20Sopenharmony_ci GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count)); 6388c2ecf20Sopenharmony_ci GEM_BUG_ON(pmu->enable_count[bit] == ~0); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci pmu->enable |= BIT_ULL(bit); 6418c2ecf20Sopenharmony_ci pmu->enable_count[bit]++; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* 6448c2ecf20Sopenharmony_ci * Start the sampling timer if needed and not already enabled. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci __i915_pmu_maybe_start_timer(pmu); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * For per-engine events the bitmask and reference counting 6508c2ecf20Sopenharmony_ci * is stored per engine. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci if (is_engine_event(event)) { 6538c2ecf20Sopenharmony_ci u8 sample = engine_event_sample(event); 6548c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci engine = intel_engine_lookup_user(i915, 6578c2ecf20Sopenharmony_ci engine_event_class(event), 6588c2ecf20Sopenharmony_ci engine_event_instance(event)); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.enable_count) != 6618c2ecf20Sopenharmony_ci I915_ENGINE_SAMPLE_COUNT); 6628c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.sample) != 6638c2ecf20Sopenharmony_ci I915_ENGINE_SAMPLE_COUNT); 6648c2ecf20Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count)); 6658c2ecf20Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample)); 6668c2ecf20Sopenharmony_ci GEM_BUG_ON(engine->pmu.enable_count[sample] == ~0); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci engine->pmu.enable |= BIT(sample); 6698c2ecf20Sopenharmony_ci engine->pmu.enable_count[sample]++; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* 6758c2ecf20Sopenharmony_ci * Store the current counter value so we can report the correct delta 6768c2ecf20Sopenharmony_ci * for all listeners. Even when the event was already enabled and has 6778c2ecf20Sopenharmony_ci * an existing non-zero value. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, __i915_pmu_event_read(event)); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic void i915_pmu_disable(struct perf_event *event) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = 6858c2ecf20Sopenharmony_ci container_of(event->pmu, typeof(*i915), pmu.base); 6868c2ecf20Sopenharmony_ci unsigned int bit = event_enabled_bit(event); 6878c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 6888c2ecf20Sopenharmony_ci unsigned long flags; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci spin_lock_irqsave(&pmu->lock, flags); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (is_engine_event(event)) { 6938c2ecf20Sopenharmony_ci u8 sample = engine_event_sample(event); 6948c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci engine = intel_engine_lookup_user(i915, 6978c2ecf20Sopenharmony_ci engine_event_class(event), 6988c2ecf20Sopenharmony_ci engine_event_instance(event)); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count)); 7018c2ecf20Sopenharmony_ci GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample)); 7028c2ecf20Sopenharmony_ci GEM_BUG_ON(engine->pmu.enable_count[sample] == 0); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * Decrement the reference count and clear the enabled 7068c2ecf20Sopenharmony_ci * bitmask when the last listener on an event goes away. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci if (--engine->pmu.enable_count[sample] == 0) 7098c2ecf20Sopenharmony_ci engine->pmu.enable &= ~BIT(sample); 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count)); 7138c2ecf20Sopenharmony_ci GEM_BUG_ON(pmu->enable_count[bit] == 0); 7148c2ecf20Sopenharmony_ci /* 7158c2ecf20Sopenharmony_ci * Decrement the reference count and clear the enabled 7168c2ecf20Sopenharmony_ci * bitmask when the last listener on an event goes away. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci if (--pmu->enable_count[bit] == 0) { 7198c2ecf20Sopenharmony_ci pmu->enable &= ~BIT_ULL(bit); 7208c2ecf20Sopenharmony_ci pmu->timer_enabled &= pmu_needs_timer(pmu, true); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pmu->lock, flags); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic void i915_pmu_event_start(struct perf_event *event, int flags) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci i915_pmu_enable(event); 7298c2ecf20Sopenharmony_ci event->hw.state = 0; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic void i915_pmu_event_stop(struct perf_event *event, int flags) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci if (flags & PERF_EF_UPDATE) 7358c2ecf20Sopenharmony_ci i915_pmu_event_read(event); 7368c2ecf20Sopenharmony_ci i915_pmu_disable(event); 7378c2ecf20Sopenharmony_ci event->hw.state = PERF_HES_STOPPED; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int i915_pmu_event_add(struct perf_event *event, int flags) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 7438c2ecf20Sopenharmony_ci i915_pmu_event_start(event, flags); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic void i915_pmu_event_del(struct perf_event *event, int flags) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci i915_pmu_event_stop(event, PERF_EF_UPDATE); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int i915_pmu_event_event_idx(struct perf_event *event) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci return 0; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistruct i915_str_attribute { 7598c2ecf20Sopenharmony_ci struct device_attribute attr; 7608c2ecf20Sopenharmony_ci const char *str; 7618c2ecf20Sopenharmony_ci}; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic ssize_t i915_pmu_format_show(struct device *dev, 7648c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct i915_str_attribute *eattr; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci eattr = container_of(attr, struct i915_str_attribute, attr); 7698c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", eattr->str); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci#define I915_PMU_FORMAT_ATTR(_name, _config) \ 7738c2ecf20Sopenharmony_ci (&((struct i915_str_attribute[]) { \ 7748c2ecf20Sopenharmony_ci { .attr = __ATTR(_name, 0444, i915_pmu_format_show, NULL), \ 7758c2ecf20Sopenharmony_ci .str = _config, } \ 7768c2ecf20Sopenharmony_ci })[0].attr.attr) 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic struct attribute *i915_pmu_format_attrs[] = { 7798c2ecf20Sopenharmony_ci I915_PMU_FORMAT_ATTR(i915_eventid, "config:0-20"), 7808c2ecf20Sopenharmony_ci NULL, 7818c2ecf20Sopenharmony_ci}; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic const struct attribute_group i915_pmu_format_attr_group = { 7848c2ecf20Sopenharmony_ci .name = "format", 7858c2ecf20Sopenharmony_ci .attrs = i915_pmu_format_attrs, 7868c2ecf20Sopenharmony_ci}; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistruct i915_ext_attribute { 7898c2ecf20Sopenharmony_ci struct device_attribute attr; 7908c2ecf20Sopenharmony_ci unsigned long val; 7918c2ecf20Sopenharmony_ci}; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic ssize_t i915_pmu_event_show(struct device *dev, 7948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct i915_ext_attribute *eattr; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci eattr = container_of(attr, struct i915_ext_attribute, attr); 7998c2ecf20Sopenharmony_ci return sprintf(buf, "config=0x%lx\n", eattr->val); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic ssize_t 8038c2ecf20Sopenharmony_cii915_pmu_get_attr_cpumask(struct device *dev, 8048c2ecf20Sopenharmony_ci struct device_attribute *attr, 8058c2ecf20Sopenharmony_ci char *buf) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, &i915_pmu_cpumask); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic DEVICE_ATTR(cpumask, 0444, i915_pmu_get_attr_cpumask, NULL); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic struct attribute *i915_cpumask_attrs[] = { 8138c2ecf20Sopenharmony_ci &dev_attr_cpumask.attr, 8148c2ecf20Sopenharmony_ci NULL, 8158c2ecf20Sopenharmony_ci}; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic const struct attribute_group i915_pmu_cpumask_attr_group = { 8188c2ecf20Sopenharmony_ci .attrs = i915_cpumask_attrs, 8198c2ecf20Sopenharmony_ci}; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci#define __event(__config, __name, __unit) \ 8228c2ecf20Sopenharmony_ci{ \ 8238c2ecf20Sopenharmony_ci .config = (__config), \ 8248c2ecf20Sopenharmony_ci .name = (__name), \ 8258c2ecf20Sopenharmony_ci .unit = (__unit), \ 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci#define __engine_event(__sample, __name) \ 8298c2ecf20Sopenharmony_ci{ \ 8308c2ecf20Sopenharmony_ci .sample = (__sample), \ 8318c2ecf20Sopenharmony_ci .name = (__name), \ 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic struct i915_ext_attribute * 8358c2ecf20Sopenharmony_ciadd_i915_attr(struct i915_ext_attribute *attr, const char *name, u64 config) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci sysfs_attr_init(&attr->attr.attr); 8388c2ecf20Sopenharmony_ci attr->attr.attr.name = name; 8398c2ecf20Sopenharmony_ci attr->attr.attr.mode = 0444; 8408c2ecf20Sopenharmony_ci attr->attr.show = i915_pmu_event_show; 8418c2ecf20Sopenharmony_ci attr->val = config; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci return ++attr; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_cistatic struct perf_pmu_events_attr * 8478c2ecf20Sopenharmony_ciadd_pmu_attr(struct perf_pmu_events_attr *attr, const char *name, 8488c2ecf20Sopenharmony_ci const char *str) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci sysfs_attr_init(&attr->attr.attr); 8518c2ecf20Sopenharmony_ci attr->attr.attr.name = name; 8528c2ecf20Sopenharmony_ci attr->attr.attr.mode = 0444; 8538c2ecf20Sopenharmony_ci attr->attr.show = perf_event_sysfs_show; 8548c2ecf20Sopenharmony_ci attr->event_str = str; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return ++attr; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic struct attribute ** 8608c2ecf20Sopenharmony_cicreate_event_attributes(struct i915_pmu *pmu) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 8638c2ecf20Sopenharmony_ci static const struct { 8648c2ecf20Sopenharmony_ci u64 config; 8658c2ecf20Sopenharmony_ci const char *name; 8668c2ecf20Sopenharmony_ci const char *unit; 8678c2ecf20Sopenharmony_ci } events[] = { 8688c2ecf20Sopenharmony_ci __event(I915_PMU_ACTUAL_FREQUENCY, "actual-frequency", "M"), 8698c2ecf20Sopenharmony_ci __event(I915_PMU_REQUESTED_FREQUENCY, "requested-frequency", "M"), 8708c2ecf20Sopenharmony_ci __event(I915_PMU_INTERRUPTS, "interrupts", NULL), 8718c2ecf20Sopenharmony_ci __event(I915_PMU_RC6_RESIDENCY, "rc6-residency", "ns"), 8728c2ecf20Sopenharmony_ci }; 8738c2ecf20Sopenharmony_ci static const struct { 8748c2ecf20Sopenharmony_ci enum drm_i915_pmu_engine_sample sample; 8758c2ecf20Sopenharmony_ci char *name; 8768c2ecf20Sopenharmony_ci } engine_events[] = { 8778c2ecf20Sopenharmony_ci __engine_event(I915_SAMPLE_BUSY, "busy"), 8788c2ecf20Sopenharmony_ci __engine_event(I915_SAMPLE_SEMA, "sema"), 8798c2ecf20Sopenharmony_ci __engine_event(I915_SAMPLE_WAIT, "wait"), 8808c2ecf20Sopenharmony_ci }; 8818c2ecf20Sopenharmony_ci unsigned int count = 0; 8828c2ecf20Sopenharmony_ci struct perf_pmu_events_attr *pmu_attr = NULL, *pmu_iter; 8838c2ecf20Sopenharmony_ci struct i915_ext_attribute *i915_attr = NULL, *i915_iter; 8848c2ecf20Sopenharmony_ci struct attribute **attr = NULL, **attr_iter; 8858c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 8868c2ecf20Sopenharmony_ci unsigned int i; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* Count how many counters we will be exposing. */ 8898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(events); i++) { 8908c2ecf20Sopenharmony_ci if (!config_status(i915, events[i].config)) 8918c2ecf20Sopenharmony_ci count++; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci for_each_uabi_engine(engine, i915) { 8958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(engine_events); i++) { 8968c2ecf20Sopenharmony_ci if (!engine_event_status(engine, 8978c2ecf20Sopenharmony_ci engine_events[i].sample)) 8988c2ecf20Sopenharmony_ci count++; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* Allocate attribute objects and table. */ 9038c2ecf20Sopenharmony_ci i915_attr = kcalloc(count, sizeof(*i915_attr), GFP_KERNEL); 9048c2ecf20Sopenharmony_ci if (!i915_attr) 9058c2ecf20Sopenharmony_ci goto err_alloc; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci pmu_attr = kcalloc(count, sizeof(*pmu_attr), GFP_KERNEL); 9088c2ecf20Sopenharmony_ci if (!pmu_attr) 9098c2ecf20Sopenharmony_ci goto err_alloc; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* Max one pointer of each attribute type plus a termination entry. */ 9128c2ecf20Sopenharmony_ci attr = kcalloc(count * 2 + 1, sizeof(*attr), GFP_KERNEL); 9138c2ecf20Sopenharmony_ci if (!attr) 9148c2ecf20Sopenharmony_ci goto err_alloc; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci i915_iter = i915_attr; 9178c2ecf20Sopenharmony_ci pmu_iter = pmu_attr; 9188c2ecf20Sopenharmony_ci attr_iter = attr; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* Initialize supported non-engine counters. */ 9218c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(events); i++) { 9228c2ecf20Sopenharmony_ci char *str; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (config_status(i915, events[i].config)) 9258c2ecf20Sopenharmony_ci continue; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci str = kstrdup(events[i].name, GFP_KERNEL); 9288c2ecf20Sopenharmony_ci if (!str) 9298c2ecf20Sopenharmony_ci goto err; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci *attr_iter++ = &i915_iter->attr.attr; 9328c2ecf20Sopenharmony_ci i915_iter = add_i915_attr(i915_iter, str, events[i].config); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (events[i].unit) { 9358c2ecf20Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s.unit", events[i].name); 9368c2ecf20Sopenharmony_ci if (!str) 9378c2ecf20Sopenharmony_ci goto err; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci *attr_iter++ = &pmu_iter->attr.attr; 9408c2ecf20Sopenharmony_ci pmu_iter = add_pmu_attr(pmu_iter, str, events[i].unit); 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Initialize supported engine counters. */ 9458c2ecf20Sopenharmony_ci for_each_uabi_engine(engine, i915) { 9468c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(engine_events); i++) { 9478c2ecf20Sopenharmony_ci char *str; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (engine_event_status(engine, 9508c2ecf20Sopenharmony_ci engine_events[i].sample)) 9518c2ecf20Sopenharmony_ci continue; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-%s", 9548c2ecf20Sopenharmony_ci engine->name, engine_events[i].name); 9558c2ecf20Sopenharmony_ci if (!str) 9568c2ecf20Sopenharmony_ci goto err; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci *attr_iter++ = &i915_iter->attr.attr; 9598c2ecf20Sopenharmony_ci i915_iter = 9608c2ecf20Sopenharmony_ci add_i915_attr(i915_iter, str, 9618c2ecf20Sopenharmony_ci __I915_PMU_ENGINE(engine->uabi_class, 9628c2ecf20Sopenharmony_ci engine->uabi_instance, 9638c2ecf20Sopenharmony_ci engine_events[i].sample)); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s-%s.unit", 9668c2ecf20Sopenharmony_ci engine->name, engine_events[i].name); 9678c2ecf20Sopenharmony_ci if (!str) 9688c2ecf20Sopenharmony_ci goto err; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci *attr_iter++ = &pmu_iter->attr.attr; 9718c2ecf20Sopenharmony_ci pmu_iter = add_pmu_attr(pmu_iter, str, "ns"); 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci pmu->i915_attr = i915_attr; 9768c2ecf20Sopenharmony_ci pmu->pmu_attr = pmu_attr; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return attr; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cierr:; 9818c2ecf20Sopenharmony_ci for (attr_iter = attr; *attr_iter; attr_iter++) 9828c2ecf20Sopenharmony_ci kfree((*attr_iter)->name); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cierr_alloc: 9858c2ecf20Sopenharmony_ci kfree(attr); 9868c2ecf20Sopenharmony_ci kfree(i915_attr); 9878c2ecf20Sopenharmony_ci kfree(pmu_attr); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci return NULL; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic void free_event_attributes(struct i915_pmu *pmu) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct attribute **attr_iter = pmu->events_attr_group.attrs; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci for (; *attr_iter; attr_iter++) 9978c2ecf20Sopenharmony_ci kfree((*attr_iter)->name); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci kfree(pmu->events_attr_group.attrs); 10008c2ecf20Sopenharmony_ci kfree(pmu->i915_attr); 10018c2ecf20Sopenharmony_ci kfree(pmu->pmu_attr); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci pmu->events_attr_group.attrs = NULL; 10048c2ecf20Sopenharmony_ci pmu->i915_attr = NULL; 10058c2ecf20Sopenharmony_ci pmu->pmu_attr = NULL; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int i915_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), cpuhp.node); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci GEM_BUG_ON(!pmu->base.event_init); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Select the first online CPU as a designated reader. */ 10158c2ecf20Sopenharmony_ci if (!cpumask_weight(&i915_pmu_cpumask)) 10168c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &i915_pmu_cpumask); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int i915_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), cpuhp.node); 10248c2ecf20Sopenharmony_ci unsigned int target; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci GEM_BUG_ON(!pmu->base.event_init); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (cpumask_test_and_clear_cpu(cpu, &i915_pmu_cpumask)) { 10298c2ecf20Sopenharmony_ci target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu); 10308c2ecf20Sopenharmony_ci /* Migrate events if there is a valid target */ 10318c2ecf20Sopenharmony_ci if (target < nr_cpu_ids) { 10328c2ecf20Sopenharmony_ci cpumask_set_cpu(target, &i915_pmu_cpumask); 10338c2ecf20Sopenharmony_ci perf_pmu_migrate_context(&pmu->base, cpu, target); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci return 0; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic int i915_pmu_register_cpuhp_state(struct i915_pmu *pmu) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci enum cpuhp_state slot; 10438c2ecf20Sopenharmony_ci int ret; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 10468c2ecf20Sopenharmony_ci "perf/x86/intel/i915:online", 10478c2ecf20Sopenharmony_ci i915_pmu_cpu_online, 10488c2ecf20Sopenharmony_ci i915_pmu_cpu_offline); 10498c2ecf20Sopenharmony_ci if (ret < 0) 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci slot = ret; 10538c2ecf20Sopenharmony_ci ret = cpuhp_state_add_instance(slot, &pmu->cpuhp.node); 10548c2ecf20Sopenharmony_ci if (ret) { 10558c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(slot); 10568c2ecf20Sopenharmony_ci return ret; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci pmu->cpuhp.slot = slot; 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic void i915_pmu_unregister_cpuhp_state(struct i915_pmu *pmu) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci drm_WARN_ON(&i915->drm, pmu->cpuhp.slot == CPUHP_INVALID); 10688c2ecf20Sopenharmony_ci drm_WARN_ON(&i915->drm, cpuhp_state_remove_instance(pmu->cpuhp.slot, &pmu->cpuhp.node)); 10698c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(pmu->cpuhp.slot); 10708c2ecf20Sopenharmony_ci pmu->cpuhp.slot = CPUHP_INVALID; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic bool is_igp(struct drm_i915_private *i915) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct pci_dev *pdev = i915->drm.pdev; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* IGP is 0000:00:02.0 */ 10788c2ecf20Sopenharmony_ci return pci_domain_nr(pdev->bus) == 0 && 10798c2ecf20Sopenharmony_ci pdev->bus->number == 0 && 10808c2ecf20Sopenharmony_ci PCI_SLOT(pdev->devfn) == 2 && 10818c2ecf20Sopenharmony_ci PCI_FUNC(pdev->devfn) == 0; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_civoid i915_pmu_register(struct drm_i915_private *i915) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 10878c2ecf20Sopenharmony_ci const struct attribute_group *attr_groups[] = { 10888c2ecf20Sopenharmony_ci &i915_pmu_format_attr_group, 10898c2ecf20Sopenharmony_ci &pmu->events_attr_group, 10908c2ecf20Sopenharmony_ci &i915_pmu_cpumask_attr_group, 10918c2ecf20Sopenharmony_ci NULL 10928c2ecf20Sopenharmony_ci }; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci int ret = -ENOMEM; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) <= 2) { 10978c2ecf20Sopenharmony_ci drm_info(&i915->drm, "PMU not supported for this GPU."); 10988c2ecf20Sopenharmony_ci return; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci spin_lock_init(&pmu->lock); 11028c2ecf20Sopenharmony_ci hrtimer_init(&pmu->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 11038c2ecf20Sopenharmony_ci pmu->timer.function = i915_sample; 11048c2ecf20Sopenharmony_ci pmu->cpuhp.slot = CPUHP_INVALID; 11058c2ecf20Sopenharmony_ci init_rc6(pmu); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (!is_igp(i915)) { 11088c2ecf20Sopenharmony_ci pmu->name = kasprintf(GFP_KERNEL, 11098c2ecf20Sopenharmony_ci "i915_%s", 11108c2ecf20Sopenharmony_ci dev_name(i915->drm.dev)); 11118c2ecf20Sopenharmony_ci if (pmu->name) { 11128c2ecf20Sopenharmony_ci /* tools/perf reserves colons as special. */ 11138c2ecf20Sopenharmony_ci strreplace((char *)pmu->name, ':', '_'); 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci } else { 11168c2ecf20Sopenharmony_ci pmu->name = "i915"; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci if (!pmu->name) 11198c2ecf20Sopenharmony_ci goto err; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci pmu->events_attr_group.name = "events"; 11228c2ecf20Sopenharmony_ci pmu->events_attr_group.attrs = create_event_attributes(pmu); 11238c2ecf20Sopenharmony_ci if (!pmu->events_attr_group.attrs) 11248c2ecf20Sopenharmony_ci goto err_name; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci pmu->base.attr_groups = kmemdup(attr_groups, sizeof(attr_groups), 11278c2ecf20Sopenharmony_ci GFP_KERNEL); 11288c2ecf20Sopenharmony_ci if (!pmu->base.attr_groups) 11298c2ecf20Sopenharmony_ci goto err_attr; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci pmu->base.module = THIS_MODULE; 11328c2ecf20Sopenharmony_ci pmu->base.task_ctx_nr = perf_invalid_context; 11338c2ecf20Sopenharmony_ci pmu->base.event_init = i915_pmu_event_init; 11348c2ecf20Sopenharmony_ci pmu->base.add = i915_pmu_event_add; 11358c2ecf20Sopenharmony_ci pmu->base.del = i915_pmu_event_del; 11368c2ecf20Sopenharmony_ci pmu->base.start = i915_pmu_event_start; 11378c2ecf20Sopenharmony_ci pmu->base.stop = i915_pmu_event_stop; 11388c2ecf20Sopenharmony_ci pmu->base.read = i915_pmu_event_read; 11398c2ecf20Sopenharmony_ci pmu->base.event_idx = i915_pmu_event_event_idx; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci ret = perf_pmu_register(&pmu->base, pmu->name, -1); 11428c2ecf20Sopenharmony_ci if (ret) 11438c2ecf20Sopenharmony_ci goto err_groups; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci ret = i915_pmu_register_cpuhp_state(pmu); 11468c2ecf20Sopenharmony_ci if (ret) 11478c2ecf20Sopenharmony_ci goto err_unreg; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci return; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cierr_unreg: 11528c2ecf20Sopenharmony_ci perf_pmu_unregister(&pmu->base); 11538c2ecf20Sopenharmony_cierr_groups: 11548c2ecf20Sopenharmony_ci kfree(pmu->base.attr_groups); 11558c2ecf20Sopenharmony_cierr_attr: 11568c2ecf20Sopenharmony_ci pmu->base.event_init = NULL; 11578c2ecf20Sopenharmony_ci free_event_attributes(pmu); 11588c2ecf20Sopenharmony_cierr_name: 11598c2ecf20Sopenharmony_ci if (!is_igp(i915)) 11608c2ecf20Sopenharmony_ci kfree(pmu->name); 11618c2ecf20Sopenharmony_cierr: 11628c2ecf20Sopenharmony_ci drm_notice(&i915->drm, "Failed to register PMU!\n"); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_civoid i915_pmu_unregister(struct drm_i915_private *i915) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct i915_pmu *pmu = &i915->pmu; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (!pmu->base.event_init) 11708c2ecf20Sopenharmony_ci return; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci drm_WARN_ON(&i915->drm, pmu->enable); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci hrtimer_cancel(&pmu->timer); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci i915_pmu_unregister_cpuhp_state(pmu); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci perf_pmu_unregister(&pmu->base); 11798c2ecf20Sopenharmony_ci pmu->base.event_init = NULL; 11808c2ecf20Sopenharmony_ci kfree(pmu->base.attr_groups); 11818c2ecf20Sopenharmony_ci if (!is_igp(i915)) 11828c2ecf20Sopenharmony_ci kfree(pmu->name); 11838c2ecf20Sopenharmony_ci free_event_attributes(pmu); 11848c2ecf20Sopenharmony_ci} 1185