Skip to content

Commit eb3693f

Browse files
committed
cpufreq: intel_pstate: hybrid: CPU-specific scaling factor
The scaling factor between HWP performance levels and CPU frequency may be different for different types of CPUs in a hybrid processor and in general the HWP performance levels need not correspond to "P-states" representing values that would be written to MSR_IA32_PERF_CTL if HWP was disabled. However, the policy limits control in cpufreq is defined in terms of CPU frequency, so it is necessary to map the frequency limits set through that interface to HWP performance levels with reasonable accuracy and the behavior of that interface on hybrid processors has to be compatible with its behavior on non-hybrid ones. To address this problem, use the observations that (1) on hybrid processors the sysfs interface can operate by mapping frequency to "P-states" and translating those "P-states" to specific HWP performance levels of the given CPU and (2) the scaling factor between the MSR_IA32_PERF_CTL "P-states" and CPU frequency can be regarded as a known value. Moreover, the mapping between the HWP performance levels and CPU frequency can be assumed to be linear and such that HWP performance level 0 correspond to the frequency value of 0, so it is only necessary to know the frequency corresponding to one specific HWP performance level to compute the scaling factor applicable to all of them. One possibility is to take the nominal performance value from CPPC, if available, and use cpu_khz as the corresponding frequency. If the CPPC capabilities interface is not there or the nominal performance value provided by it is out of range, though, something else needs to be done. Namely, the guaranteed performance level either from CPPC or from MSR_HWP_CAPABILITIES can be used instead, but the corresponding frequency needs to be determined. That can be done by computing the product of the (known) scaling factor between the MSR_IA32_PERF_CTL P-states and CPU frequency (the PERF_CTL scaling factor) and the P-state value referred to as the "TDP ratio". If the HWP-to-frequency scaling factor value obtained in one of the ways above turns out to be euqal to the PERF_CTL scaling factor, it can be assumed that the number of HWP performance levels is equal to the number of P-states and the given CPU can be handled as though this was not a hybrid processor. Otherwise, one more adjustment may still need to be made, because the HWP-to-frequency scaling factor computed so far may not be accurate enough (e.g. because the CPPC information does not match the exact behavior of the processor). Specifically, in that case the frequency corresponding to the highest HWP performance value from MSR_HWP_CAPABILITIES (computed as the product of that value and the HWP-to-frequency scaling factor) cannot exceed the frequency that corresponds to the maximum 1-core turbo P-state value from MSR_TURBO_RATIO_LIMIT (computed as the procuct of that value and the PERF_CTL scaling factor) and the HWP-to-frequency scaling factor may need to be adjusted accordingly. Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent c3d175e commit eb3693f

File tree

1 file changed

+210
-23
lines changed

1 file changed

+210
-23
lines changed

drivers/cpufreq/intel_pstate.c

Lines changed: 210 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,10 @@ struct sample {
121121
* @max_pstate_physical:This is physical Max P state for a processor
122122
* This can be higher than the max_pstate which can
123123
* be limited by platform thermal design power limits
124-
* @scaling: Scaling factor to convert frequency to cpufreq
125-
* frequency units
124+
* @perf_ctl_scaling: PERF_CTL P-state to frequency scaling factor
125+
* @scaling: Scaling factor between performance and frequency
126126
* @turbo_pstate: Max Turbo P state possible for this platform
127+
* @min_freq: @min_pstate frequency in cpufreq units
127128
* @max_freq: @max_pstate frequency in cpufreq units
128129
* @turbo_freq: @turbo_pstate frequency in cpufreq units
129130
*
@@ -134,8 +135,10 @@ struct pstate_data {
134135
int min_pstate;
135136
int max_pstate;
136137
int max_pstate_physical;
138+
int perf_ctl_scaling;
137139
int scaling;
138140
int turbo_pstate;
141+
unsigned int min_freq;
139142
unsigned int max_freq;
140143
unsigned int turbo_freq;
141144
};
@@ -489,6 +492,149 @@ static int intel_pstate_get_cppc_guranteed(int cpu)
489492
}
490493
#endif /* CONFIG_ACPI_CPPC_LIB */
491494

495+
static bool intel_pstate_cppc_perf_valid(u32 perf, struct cppc_perf_caps *caps)
496+
{
497+
return perf && perf <= caps->highest_perf && perf >= caps->lowest_perf;
498+
}
499+
500+
static bool intel_pstate_cppc_perf_caps(struct cpudata *cpu,
501+
struct cppc_perf_caps *caps)
502+
{
503+
if (cppc_get_perf_caps(cpu->cpu, caps))
504+
return false;
505+
506+
return caps->highest_perf && caps->lowest_perf <= caps->highest_perf;
507+
}
508+
509+
static void intel_pstate_hybrid_hwp_perf_ctl_parity(struct cpudata *cpu)
510+
{
511+
pr_debug("CPU%d: Using PERF_CTL scaling for HWP\n", cpu->cpu);
512+
513+
cpu->pstate.scaling = cpu->pstate.perf_ctl_scaling;
514+
}
515+
516+
/**
517+
* intel_pstate_hybrid_hwp_calibrate - Calibrate HWP performance levels.
518+
* @cpu: Target CPU.
519+
*
520+
* On hybrid processors, HWP may expose more performance levels than there are
521+
* P-states accessible through the PERF_CTL interface. If that happens, the
522+
* scaling factor between HWP performance levels and CPU frequency will be less
523+
* than the scaling factor between P-state values and CPU frequency.
524+
*
525+
* In that case, the scaling factor between HWP performance levels and CPU
526+
* frequency needs to be determined which can be done with the help of the
527+
* observation that certain HWP performance levels should correspond to certain
528+
* P-states, like for example the HWP highest performance should correspond
529+
* to the maximum turbo P-state of the CPU.
530+
*/
531+
static void intel_pstate_hybrid_hwp_calibrate(struct cpudata *cpu)
532+
{
533+
struct cppc_perf_caps caps;
534+
int perf_ctl_max_phys = cpu->pstate.max_pstate_physical;
535+
int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling;
536+
int perf_ctl_turbo = pstate_funcs.get_turbo();
537+
int turbo_freq = perf_ctl_turbo * perf_ctl_scaling;
538+
int perf_ctl_max = pstate_funcs.get_max();
539+
int max_freq = perf_ctl_max * perf_ctl_scaling;
540+
int scaling = INT_MAX;
541+
int freq;
542+
543+
pr_debug("CPU%d: perf_ctl_max_phys = %d\n", cpu->cpu, perf_ctl_max_phys);
544+
pr_debug("CPU%d: perf_ctl_max = %d\n", cpu->cpu, perf_ctl_max);
545+
pr_debug("CPU%d: perf_ctl_turbo = %d\n", cpu->cpu, perf_ctl_turbo);
546+
pr_debug("CPU%d: perf_ctl_scaling = %d\n", cpu->cpu, perf_ctl_scaling);
547+
548+
pr_debug("CPU%d: HWP_CAP guaranteed = %d\n", cpu->cpu, cpu->pstate.max_pstate);
549+
pr_debug("CPU%d: HWP_CAP highest = %d\n", cpu->cpu, cpu->pstate.turbo_pstate);
550+
551+
if (intel_pstate_cppc_perf_caps(cpu, &caps)) {
552+
if (intel_pstate_cppc_perf_valid(caps.nominal_perf, &caps)) {
553+
pr_debug("CPU%d: Using CPPC nominal\n", cpu->cpu);
554+
555+
/*
556+
* If the CPPC nominal performance is valid, it can be
557+
* assumed to correspond to cpu_khz.
558+
*/
559+
if (caps.nominal_perf == perf_ctl_max_phys) {
560+
intel_pstate_hybrid_hwp_perf_ctl_parity(cpu);
561+
return;
562+
}
563+
scaling = DIV_ROUND_UP(cpu_khz, caps.nominal_perf);
564+
} else if (intel_pstate_cppc_perf_valid(caps.guaranteed_perf, &caps)) {
565+
pr_debug("CPU%d: Using CPPC guaranteed\n", cpu->cpu);
566+
567+
/*
568+
* If the CPPC guaranteed performance is valid, it can
569+
* be assumed to correspond to max_freq.
570+
*/
571+
if (caps.guaranteed_perf == perf_ctl_max) {
572+
intel_pstate_hybrid_hwp_perf_ctl_parity(cpu);
573+
return;
574+
}
575+
scaling = DIV_ROUND_UP(max_freq, caps.guaranteed_perf);
576+
}
577+
}
578+
/*
579+
* If using the CPPC data to compute the HWP-to-frequency scaling factor
580+
* doesn't work, use the HWP_CAP gauranteed perf for this purpose with
581+
* the assumption that it corresponds to max_freq.
582+
*/
583+
if (scaling > perf_ctl_scaling) {
584+
pr_debug("CPU%d: Using HWP_CAP guaranteed\n", cpu->cpu);
585+
586+
if (cpu->pstate.max_pstate == perf_ctl_max) {
587+
intel_pstate_hybrid_hwp_perf_ctl_parity(cpu);
588+
return;
589+
}
590+
scaling = DIV_ROUND_UP(max_freq, cpu->pstate.max_pstate);
591+
if (scaling > perf_ctl_scaling) {
592+
/*
593+
* This should not happen, because it would mean that
594+
* the number of HWP perf levels was less than the
595+
* number of P-states, so use the PERF_CTL scaling in
596+
* that case.
597+
*/
598+
pr_debug("CPU%d: scaling (%d) out of range\n", cpu->cpu,
599+
scaling);
600+
601+
intel_pstate_hybrid_hwp_perf_ctl_parity(cpu);
602+
return;
603+
}
604+
}
605+
606+
/*
607+
* If the product of the HWP performance scaling factor obtained above
608+
* and the HWP_CAP highest performance is greater than the maximum turbo
609+
* frequency corresponding to the pstate_funcs.get_turbo() return value,
610+
* the scaling factor is too high, so recompute it so that the HWP_CAP
611+
* highest performance corresponds to the maximum turbo frequency.
612+
*/
613+
if (turbo_freq < cpu->pstate.turbo_pstate * scaling) {
614+
pr_debug("CPU%d: scaling too high (%d)\n", cpu->cpu, scaling);
615+
616+
cpu->pstate.turbo_freq = turbo_freq;
617+
scaling = DIV_ROUND_UP(turbo_freq, cpu->pstate.turbo_pstate);
618+
}
619+
620+
cpu->pstate.scaling = scaling;
621+
622+
pr_debug("CPU%d: HWP-to-frequency scaling factor: %d\n", cpu->cpu, scaling);
623+
624+
cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling,
625+
perf_ctl_scaling);
626+
627+
freq = perf_ctl_max_phys * perf_ctl_scaling;
628+
cpu->pstate.max_pstate_physical = DIV_ROUND_UP(freq, scaling);
629+
630+
cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling;
631+
/*
632+
* Cast the min P-state value retrieved via pstate_funcs.get_min() to
633+
* the effective range of HWP performance levels.
634+
*/
635+
cpu->pstate.min_pstate = DIV_ROUND_UP(cpu->pstate.min_freq, scaling);
636+
}
637+
492638
static inline void update_turbo_state(void)
493639
{
494640
u64 misc_en;
@@ -795,19 +941,22 @@ cpufreq_freq_attr_rw(energy_performance_preference);
795941

796942
static ssize_t show_base_frequency(struct cpufreq_policy *policy, char *buf)
797943
{
798-
struct cpudata *cpu;
799-
u64 cap;
800-
int ratio;
944+
struct cpudata *cpu = all_cpu_data[policy->cpu];
945+
int ratio, freq;
801946

802947
ratio = intel_pstate_get_cppc_guranteed(policy->cpu);
803948
if (ratio <= 0) {
949+
u64 cap;
950+
804951
rdmsrl_on_cpu(policy->cpu, MSR_HWP_CAPABILITIES, &cap);
805952
ratio = HWP_GUARANTEED_PERF(cap);
806953
}
807954

808-
cpu = all_cpu_data[policy->cpu];
955+
freq = ratio * cpu->pstate.scaling;
956+
if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling)
957+
freq = rounddown(freq, cpu->pstate.perf_ctl_scaling);
809958

810-
return sprintf(buf, "%d\n", ratio * cpu->pstate.scaling);
959+
return sprintf(buf, "%d\n", freq);
811960
}
812961

813962
cpufreq_freq_attr_ro(base_frequency);
@@ -831,9 +980,20 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu)
831980

832981
static void intel_pstate_get_hwp_cap(struct cpudata *cpu)
833982
{
983+
int scaling = cpu->pstate.scaling;
984+
834985
__intel_pstate_get_hwp_cap(cpu);
835-
cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling;
836-
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling;
986+
987+
cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling;
988+
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling;
989+
if (scaling != cpu->pstate.perf_ctl_scaling) {
990+
int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling;
991+
992+
cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq,
993+
perf_ctl_scaling);
994+
cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq,
995+
perf_ctl_scaling);
996+
}
837997
}
838998

839999
static void intel_pstate_hwp_set(unsigned int cpu)
@@ -1724,19 +1884,33 @@ static void intel_pstate_max_within_limits(struct cpudata *cpu)
17241884

17251885
static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
17261886
{
1887+
bool hybrid_cpu = boot_cpu_has(X86_FEATURE_HYBRID_CPU);
1888+
int perf_ctl_max_phys = pstate_funcs.get_max_physical();
1889+
int perf_ctl_scaling = hybrid_cpu ? cpu_khz / perf_ctl_max_phys :
1890+
pstate_funcs.get_scaling();
1891+
17271892
cpu->pstate.min_pstate = pstate_funcs.get_min();
1728-
cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical();
1729-
cpu->pstate.scaling = pstate_funcs.get_scaling();
1893+
cpu->pstate.max_pstate_physical = perf_ctl_max_phys;
1894+
cpu->pstate.perf_ctl_scaling = perf_ctl_scaling;
17301895

17311896
if (hwp_active && !hwp_mode_bdw) {
17321897
__intel_pstate_get_hwp_cap(cpu);
1898+
1899+
if (hybrid_cpu)
1900+
intel_pstate_hybrid_hwp_calibrate(cpu);
1901+
else
1902+
cpu->pstate.scaling = perf_ctl_scaling;
17331903
} else {
1904+
cpu->pstate.scaling = perf_ctl_scaling;
17341905
cpu->pstate.max_pstate = pstate_funcs.get_max();
17351906
cpu->pstate.turbo_pstate = pstate_funcs.get_turbo();
17361907
}
17371908

1738-
cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling;
1739-
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling;
1909+
if (cpu->pstate.scaling == perf_ctl_scaling) {
1910+
cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling;
1911+
cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling;
1912+
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling;
1913+
}
17401914

17411915
if (pstate_funcs.get_aperf_mperf_shift)
17421916
cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift();
@@ -2206,23 +2380,34 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu,
22062380
unsigned int policy_min,
22072381
unsigned int policy_max)
22082382
{
2209-
int scaling = cpu->pstate.scaling;
2383+
int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling;
22102384
int32_t max_policy_perf, min_policy_perf;
22112385

2386+
max_policy_perf = policy_max / perf_ctl_scaling;
2387+
if (policy_max == policy_min) {
2388+
min_policy_perf = max_policy_perf;
2389+
} else {
2390+
min_policy_perf = policy_min / perf_ctl_scaling;
2391+
min_policy_perf = clamp_t(int32_t, min_policy_perf,
2392+
0, max_policy_perf);
2393+
}
2394+
22122395
/*
22132396
* HWP needs some special consideration, because HWP_REQUEST uses
22142397
* abstract values to represent performance rather than pure ratios.
22152398
*/
2216-
if (hwp_active)
2399+
if (hwp_active) {
22172400
intel_pstate_get_hwp_cap(cpu);
22182401

2219-
max_policy_perf = policy_max / scaling;
2220-
if (policy_max == policy_min) {
2221-
min_policy_perf = max_policy_perf;
2222-
} else {
2223-
min_policy_perf = policy_min / scaling;
2224-
min_policy_perf = clamp_t(int32_t, min_policy_perf,
2225-
0, max_policy_perf);
2402+
if (cpu->pstate.scaling != perf_ctl_scaling) {
2403+
int scaling = cpu->pstate.scaling;
2404+
int freq;
2405+
2406+
freq = max_policy_perf * perf_ctl_scaling;
2407+
max_policy_perf = DIV_ROUND_UP(freq, scaling);
2408+
freq = min_policy_perf * perf_ctl_scaling;
2409+
min_policy_perf = DIV_ROUND_UP(freq, scaling);
2410+
}
22262411
}
22272412

22282413
pr_debug("cpu:%d min_policy_perf:%d max_policy_perf:%d\n",
@@ -2416,7 +2601,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
24162601
cpu->min_perf_ratio = 0;
24172602

24182603
/* cpuinfo and default policy values */
2419-
policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling;
2604+
policy->cpuinfo.min_freq = cpu->pstate.min_freq;
24202605
update_turbo_state();
24212606
global.turbo_disabled_mf = global.turbo_disabled;
24222607
policy->cpuinfo.max_freq = global.turbo_disabled ?
@@ -3146,6 +3331,8 @@ static int __init intel_pstate_init(void)
31463331
}
31473332

31483333
pr_info("HWP enabled\n");
3334+
} else if (boot_cpu_has(X86_FEATURE_HYBRID_CPU)) {
3335+
pr_warn("Problematic setup: Hybrid processor with disabled HWP\n");
31493336
}
31503337

31513338
return 0;

0 commit comments

Comments
 (0)