From 23c296fb7eeea99dec035521c07ae54c28b8a267 Mon Sep 17 00:00:00 2001 From: Jinzhou Su Date: Wed, 9 Mar 2022 09:23:48 +0800 Subject: [PATCH] cpufreq: amd-pstate: Add more tracepoint for AMD P-State module Add frequency, mperf, aperf and tsc in the trace. This can be used to debug and tune the performance of AMD P-state driver. Use the time difference between amd_pstate_update to calculate CPU frequency. There could be sleep in arch_freq_get_on_cpu, so do not use it here. Signed-off-by: Jinzhou Su Co-developed-by: Huang Rui Signed-off-by: Huang Rui Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/amd-pstate-trace.h | 22 ++++++++++- drivers/cpufreq/amd-pstate.c | 59 +++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/amd-pstate-trace.h b/drivers/cpufreq/amd-pstate-trace.h index 647505957d4f..35f38ae67fb1 100644 --- a/drivers/cpufreq/amd-pstate-trace.h +++ b/drivers/cpufreq/amd-pstate-trace.h @@ -27,6 +27,10 @@ TRACE_EVENT(amd_pstate_perf, TP_PROTO(unsigned long min_perf, unsigned long target_perf, unsigned long capacity, + u64 freq, + u64 mperf, + u64 aperf, + u64 tsc, unsigned int cpu_id, bool changed, bool fast_switch @@ -35,6 +39,10 @@ TRACE_EVENT(amd_pstate_perf, TP_ARGS(min_perf, target_perf, capacity, + freq, + mperf, + aperf, + tsc, cpu_id, changed, fast_switch @@ -44,6 +52,10 @@ TRACE_EVENT(amd_pstate_perf, __field(unsigned long, min_perf) __field(unsigned long, target_perf) __field(unsigned long, capacity) + __field(unsigned long long, freq) + __field(unsigned long long, mperf) + __field(unsigned long long, aperf) + __field(unsigned long long, tsc) __field(unsigned int, cpu_id) __field(bool, changed) __field(bool, fast_switch) @@ -53,15 +65,23 @@ TRACE_EVENT(amd_pstate_perf, __entry->min_perf = min_perf; __entry->target_perf = target_perf; __entry->capacity = capacity; + __entry->freq = freq; + __entry->mperf = mperf; + __entry->aperf = aperf; + __entry->tsc = tsc; __entry->cpu_id = cpu_id; __entry->changed = changed; __entry->fast_switch = fast_switch; ), - TP_printk("amd_min_perf=%lu amd_des_perf=%lu amd_max_perf=%lu cpu_id=%u changed=%s fast_switch=%s", + TP_printk("amd_min_perf=%lu amd_des_perf=%lu amd_max_perf=%lu freq=%llu mperf=%llu aperf=%llu tsc=%llu cpu_id=%u changed=%s fast_switch=%s", (unsigned long)__entry->min_perf, (unsigned long)__entry->target_perf, (unsigned long)__entry->capacity, + (unsigned long long)__entry->freq, + (unsigned long long)__entry->mperf, + (unsigned long long)__entry->aperf, + (unsigned long long)__entry->tsc, (unsigned int)__entry->cpu_id, (__entry->changed) ? "true" : "false", (__entry->fast_switch) ? "true" : "false" diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 9ce75ed11f8e..7be38bc6a673 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -65,6 +65,18 @@ MODULE_PARM_DESC(shared_mem, static struct cpufreq_driver amd_pstate_driver; +/** + * struct amd_aperf_mperf + * @aperf: actual performance frequency clock count + * @mperf: maximum performance frequency clock count + * @tsc: time stamp counter + */ +struct amd_aperf_mperf { + u64 aperf; + u64 mperf; + u64 tsc; +}; + /** * struct amd_cpudata - private CPU data for AMD P-State * @cpu: CPU number @@ -81,6 +93,9 @@ static struct cpufreq_driver amd_pstate_driver; * @min_freq: the frequency that mapped to lowest_perf * @nominal_freq: the frequency that mapped to nominal_perf * @lowest_nonlinear_freq: the frequency that mapped to lowest_nonlinear_perf + * @cur: Difference of Aperf/Mperf/tsc count between last and current sample + * @prev: Last Aperf/Mperf/tsc count value read from register + * @freq: current cpu frequency value * @boost_supported: check whether the Processor or SBIOS supports boost mode * * The amd_cpudata is key private data for each CPU thread in AMD P-State, and @@ -102,6 +117,10 @@ struct amd_cpudata { u32 nominal_freq; u32 lowest_nonlinear_freq; + struct amd_aperf_mperf cur; + struct amd_aperf_mperf prev; + + u64 freq; bool boost_supported; }; @@ -211,6 +230,39 @@ static inline void amd_pstate_update_perf(struct amd_cpudata *cpudata, max_perf, fast_switch); } +static inline bool amd_pstate_sample(struct amd_cpudata *cpudata) +{ + u64 aperf, mperf, tsc; + unsigned long flags; + + local_irq_save(flags); + rdmsrl(MSR_IA32_APERF, aperf); + rdmsrl(MSR_IA32_MPERF, mperf); + tsc = rdtsc(); + + if (cpudata->prev.mperf == mperf || cpudata->prev.tsc == tsc) { + local_irq_restore(flags); + return false; + } + + local_irq_restore(flags); + + cpudata->cur.aperf = aperf; + cpudata->cur.mperf = mperf; + cpudata->cur.tsc = tsc; + cpudata->cur.aperf -= cpudata->prev.aperf; + cpudata->cur.mperf -= cpudata->prev.mperf; + cpudata->cur.tsc -= cpudata->prev.tsc; + + cpudata->prev.aperf = aperf; + cpudata->prev.mperf = mperf; + cpudata->prev.tsc = tsc; + + cpudata->freq = div64_u64((cpudata->cur.aperf * cpu_khz), cpudata->cur.mperf); + + return true; +} + static void amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf, u32 des_perf, u32 max_perf, bool fast_switch) { @@ -226,8 +278,11 @@ static void amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf, value &= ~AMD_CPPC_MAX_PERF(~0L); value |= AMD_CPPC_MAX_PERF(max_perf); - trace_amd_pstate_perf(min_perf, des_perf, max_perf, - cpudata->cpu, (value != prev), fast_switch); + if (trace_amd_pstate_perf_enabled() && amd_pstate_sample(cpudata)) { + trace_amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq, + cpudata->cur.mperf, cpudata->cur.aperf, cpudata->cur.tsc, + cpudata->cpu, (value != prev), fast_switch); + } if (value == prev) return;