|
16 | 16 | #include <linux/tick.h>
|
17 | 17 | #include <linux/slab.h>
|
18 | 18 | #include <linux/sched/cpufreq.h>
|
| 19 | +#include <linux/sched/smt.h> |
19 | 20 | #include <linux/list.h>
|
20 | 21 | #include <linux/cpu.h>
|
21 | 22 | #include <linux/cpufreq.h>
|
@@ -215,6 +216,7 @@ struct global_params {
|
215 | 216 | * @hwp_req_cached: Cached value of the last HWP Request MSR
|
216 | 217 | * @hwp_cap_cached: Cached value of the last HWP Capabilities MSR
|
217 | 218 | * @last_io_update: Last time when IO wake flag was set
|
| 219 | + * @capacity_perf: Highest perf used for scale invariance |
218 | 220 | * @sched_flags: Store scheduler flags for possible cross CPU update
|
219 | 221 | * @hwp_boost_min: Last HWP boosted min performance
|
220 | 222 | * @suspended: Whether or not the driver has been suspended.
|
@@ -253,6 +255,7 @@ struct cpudata {
|
253 | 255 | u64 hwp_req_cached;
|
254 | 256 | u64 hwp_cap_cached;
|
255 | 257 | u64 last_io_update;
|
| 258 | + unsigned int capacity_perf; |
256 | 259 | unsigned int sched_flags;
|
257 | 260 | u32 hwp_boost_min;
|
258 | 261 | bool suspended;
|
@@ -295,6 +298,7 @@ static int hwp_mode_bdw __ro_after_init;
|
295 | 298 | static bool per_cpu_limits __ro_after_init;
|
296 | 299 | static bool hwp_forced __ro_after_init;
|
297 | 300 | static bool hwp_boost __read_mostly;
|
| 301 | +static bool hwp_is_hybrid; |
298 | 302 |
|
299 | 303 | static struct cpufreq_driver *intel_pstate_driver __read_mostly;
|
300 | 304 |
|
@@ -934,6 +938,139 @@ static struct freq_attr *hwp_cpufreq_attrs[] = {
|
934 | 938 | NULL,
|
935 | 939 | };
|
936 | 940 |
|
| 941 | +static struct cpudata *hybrid_max_perf_cpu __read_mostly; |
| 942 | +/* |
| 943 | + * Protects hybrid_max_perf_cpu, the capacity_perf fields in struct cpudata, |
| 944 | + * and the x86 arch scale-invariance information from concurrent updates. |
| 945 | + */ |
| 946 | +static DEFINE_MUTEX(hybrid_capacity_lock); |
| 947 | + |
| 948 | +static void hybrid_set_cpu_capacity(struct cpudata *cpu) |
| 949 | +{ |
| 950 | + arch_set_cpu_capacity(cpu->cpu, cpu->capacity_perf, |
| 951 | + hybrid_max_perf_cpu->capacity_perf, |
| 952 | + cpu->capacity_perf, |
| 953 | + cpu->pstate.max_pstate_physical); |
| 954 | + |
| 955 | + pr_debug("CPU%d: perf = %u, max. perf = %u, base perf = %d\n", cpu->cpu, |
| 956 | + cpu->capacity_perf, hybrid_max_perf_cpu->capacity_perf, |
| 957 | + cpu->pstate.max_pstate_physical); |
| 958 | +} |
| 959 | + |
| 960 | +static void hybrid_clear_cpu_capacity(unsigned int cpunum) |
| 961 | +{ |
| 962 | + arch_set_cpu_capacity(cpunum, 1, 1, 1, 1); |
| 963 | +} |
| 964 | + |
| 965 | +static void hybrid_get_capacity_perf(struct cpudata *cpu) |
| 966 | +{ |
| 967 | + if (READ_ONCE(global.no_turbo)) { |
| 968 | + cpu->capacity_perf = cpu->pstate.max_pstate_physical; |
| 969 | + return; |
| 970 | + } |
| 971 | + |
| 972 | + cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); |
| 973 | +} |
| 974 | + |
| 975 | +static void hybrid_set_capacity_of_cpus(void) |
| 976 | +{ |
| 977 | + int cpunum; |
| 978 | + |
| 979 | + for_each_online_cpu(cpunum) { |
| 980 | + struct cpudata *cpu = all_cpu_data[cpunum]; |
| 981 | + |
| 982 | + if (cpu) |
| 983 | + hybrid_set_cpu_capacity(cpu); |
| 984 | + } |
| 985 | +} |
| 986 | + |
| 987 | +static void hybrid_update_cpu_capacity_scaling(void) |
| 988 | +{ |
| 989 | + struct cpudata *max_perf_cpu = NULL; |
| 990 | + unsigned int max_cap_perf = 0; |
| 991 | + int cpunum; |
| 992 | + |
| 993 | + for_each_online_cpu(cpunum) { |
| 994 | + struct cpudata *cpu = all_cpu_data[cpunum]; |
| 995 | + |
| 996 | + if (!cpu) |
| 997 | + continue; |
| 998 | + |
| 999 | + /* |
| 1000 | + * During initialization, CPU performance at full capacity needs |
| 1001 | + * to be determined. |
| 1002 | + */ |
| 1003 | + if (!hybrid_max_perf_cpu) |
| 1004 | + hybrid_get_capacity_perf(cpu); |
| 1005 | + |
| 1006 | + /* |
| 1007 | + * If hybrid_max_perf_cpu is not NULL at this point, it is |
| 1008 | + * being replaced, so don't take it into account when looking |
| 1009 | + * for the new one. |
| 1010 | + */ |
| 1011 | + if (cpu == hybrid_max_perf_cpu) |
| 1012 | + continue; |
| 1013 | + |
| 1014 | + if (cpu->capacity_perf > max_cap_perf) { |
| 1015 | + max_cap_perf = cpu->capacity_perf; |
| 1016 | + max_perf_cpu = cpu; |
| 1017 | + } |
| 1018 | + } |
| 1019 | + |
| 1020 | + if (max_perf_cpu) { |
| 1021 | + hybrid_max_perf_cpu = max_perf_cpu; |
| 1022 | + hybrid_set_capacity_of_cpus(); |
| 1023 | + } else { |
| 1024 | + pr_info("Found no CPUs with nonzero maximum performance\n"); |
| 1025 | + /* Revert to the flat CPU capacity structure. */ |
| 1026 | + for_each_online_cpu(cpunum) |
| 1027 | + hybrid_clear_cpu_capacity(cpunum); |
| 1028 | + } |
| 1029 | +} |
| 1030 | + |
| 1031 | +static void __hybrid_init_cpu_capacity_scaling(void) |
| 1032 | +{ |
| 1033 | + hybrid_max_perf_cpu = NULL; |
| 1034 | + hybrid_update_cpu_capacity_scaling(); |
| 1035 | +} |
| 1036 | + |
| 1037 | +static void hybrid_init_cpu_capacity_scaling(void) |
| 1038 | +{ |
| 1039 | + bool disable_itmt = false; |
| 1040 | + |
| 1041 | + mutex_lock(&hybrid_capacity_lock); |
| 1042 | + |
| 1043 | + /* |
| 1044 | + * If hybrid_max_perf_cpu is set at this point, the hybrid CPU capacity |
| 1045 | + * scaling has been enabled already and the driver is just changing the |
| 1046 | + * operation mode. |
| 1047 | + */ |
| 1048 | + if (hybrid_max_perf_cpu) { |
| 1049 | + __hybrid_init_cpu_capacity_scaling(); |
| 1050 | + goto unlock; |
| 1051 | + } |
| 1052 | + |
| 1053 | + /* |
| 1054 | + * On hybrid systems, use asym capacity instead of ITMT, but because |
| 1055 | + * the capacity of SMT threads is not deterministic even approximately, |
| 1056 | + * do not do that when SMT is in use. |
| 1057 | + */ |
| 1058 | + if (hwp_is_hybrid && !sched_smt_active() && arch_enable_hybrid_capacity_scale()) { |
| 1059 | + __hybrid_init_cpu_capacity_scaling(); |
| 1060 | + disable_itmt = true; |
| 1061 | + } |
| 1062 | + |
| 1063 | +unlock: |
| 1064 | + mutex_unlock(&hybrid_capacity_lock); |
| 1065 | + |
| 1066 | + /* |
| 1067 | + * Disabling ITMT causes sched domains to be rebuilt to disable asym |
| 1068 | + * packing and enable asym capacity. |
| 1069 | + */ |
| 1070 | + if (disable_itmt) |
| 1071 | + sched_clear_itmt_support(); |
| 1072 | +} |
| 1073 | + |
937 | 1074 | static void __intel_pstate_get_hwp_cap(struct cpudata *cpu)
|
938 | 1075 | {
|
939 | 1076 | u64 cap;
|
@@ -962,6 +1099,43 @@ static void intel_pstate_get_hwp_cap(struct cpudata *cpu)
|
962 | 1099 | }
|
963 | 1100 | }
|
964 | 1101 |
|
| 1102 | +static void hybrid_update_capacity(struct cpudata *cpu) |
| 1103 | +{ |
| 1104 | + unsigned int max_cap_perf; |
| 1105 | + |
| 1106 | + mutex_lock(&hybrid_capacity_lock); |
| 1107 | + |
| 1108 | + if (!hybrid_max_perf_cpu) |
| 1109 | + goto unlock; |
| 1110 | + |
| 1111 | + /* |
| 1112 | + * The maximum performance of the CPU may have changed, but assume |
| 1113 | + * that the performance of the other CPUs has not changed. |
| 1114 | + */ |
| 1115 | + max_cap_perf = hybrid_max_perf_cpu->capacity_perf; |
| 1116 | + |
| 1117 | + intel_pstate_get_hwp_cap(cpu); |
| 1118 | + |
| 1119 | + hybrid_get_capacity_perf(cpu); |
| 1120 | + /* Should hybrid_max_perf_cpu be replaced by this CPU? */ |
| 1121 | + if (cpu->capacity_perf > max_cap_perf) { |
| 1122 | + hybrid_max_perf_cpu = cpu; |
| 1123 | + hybrid_set_capacity_of_cpus(); |
| 1124 | + goto unlock; |
| 1125 | + } |
| 1126 | + |
| 1127 | + /* If this CPU is hybrid_max_perf_cpu, should it be replaced? */ |
| 1128 | + if (cpu == hybrid_max_perf_cpu && cpu->capacity_perf < max_cap_perf) { |
| 1129 | + hybrid_update_cpu_capacity_scaling(); |
| 1130 | + goto unlock; |
| 1131 | + } |
| 1132 | + |
| 1133 | + hybrid_set_cpu_capacity(cpu); |
| 1134 | + |
| 1135 | +unlock: |
| 1136 | + mutex_unlock(&hybrid_capacity_lock); |
| 1137 | +} |
| 1138 | + |
965 | 1139 | static void intel_pstate_hwp_set(unsigned int cpu)
|
966 | 1140 | {
|
967 | 1141 | struct cpudata *cpu_data = all_cpu_data[cpu];
|
@@ -1070,6 +1244,22 @@ static void intel_pstate_hwp_offline(struct cpudata *cpu)
|
1070 | 1244 | value |= HWP_ENERGY_PERF_PREFERENCE(HWP_EPP_POWERSAVE);
|
1071 | 1245 |
|
1072 | 1246 | wrmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value);
|
| 1247 | + |
| 1248 | + mutex_lock(&hybrid_capacity_lock); |
| 1249 | + |
| 1250 | + if (!hybrid_max_perf_cpu) { |
| 1251 | + mutex_unlock(&hybrid_capacity_lock); |
| 1252 | + |
| 1253 | + return; |
| 1254 | + } |
| 1255 | + |
| 1256 | + if (hybrid_max_perf_cpu == cpu) |
| 1257 | + hybrid_update_cpu_capacity_scaling(); |
| 1258 | + |
| 1259 | + mutex_unlock(&hybrid_capacity_lock); |
| 1260 | + |
| 1261 | + /* Reset the capacity of the CPU going offline to the initial value. */ |
| 1262 | + hybrid_clear_cpu_capacity(cpu->cpu); |
1073 | 1263 | }
|
1074 | 1264 |
|
1075 | 1265 | #define POWER_CTL_EE_ENABLE 1
|
@@ -1165,21 +1355,46 @@ static void __intel_pstate_update_max_freq(struct cpudata *cpudata,
|
1165 | 1355 | static void intel_pstate_update_limits(unsigned int cpu)
|
1166 | 1356 | {
|
1167 | 1357 | struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
|
| 1358 | + struct cpudata *cpudata; |
1168 | 1359 |
|
1169 | 1360 | if (!policy)
|
1170 | 1361 | return;
|
1171 | 1362 |
|
1172 |
| - __intel_pstate_update_max_freq(all_cpu_data[cpu], policy); |
| 1363 | + cpudata = all_cpu_data[cpu]; |
| 1364 | + |
| 1365 | + __intel_pstate_update_max_freq(cpudata, policy); |
| 1366 | + |
| 1367 | + /* Prevent the driver from being unregistered now. */ |
| 1368 | + mutex_lock(&intel_pstate_driver_lock); |
1173 | 1369 |
|
1174 | 1370 | cpufreq_cpu_release(policy);
|
| 1371 | + |
| 1372 | + hybrid_update_capacity(cpudata); |
| 1373 | + |
| 1374 | + mutex_unlock(&intel_pstate_driver_lock); |
1175 | 1375 | }
|
1176 | 1376 |
|
1177 | 1377 | static void intel_pstate_update_limits_for_all(void)
|
1178 | 1378 | {
|
1179 | 1379 | int cpu;
|
1180 | 1380 |
|
1181 |
| - for_each_possible_cpu(cpu) |
1182 |
| - intel_pstate_update_limits(cpu); |
| 1381 | + for_each_possible_cpu(cpu) { |
| 1382 | + struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu); |
| 1383 | + |
| 1384 | + if (!policy) |
| 1385 | + continue; |
| 1386 | + |
| 1387 | + __intel_pstate_update_max_freq(all_cpu_data[cpu], policy); |
| 1388 | + |
| 1389 | + cpufreq_cpu_release(policy); |
| 1390 | + } |
| 1391 | + |
| 1392 | + mutex_lock(&hybrid_capacity_lock); |
| 1393 | + |
| 1394 | + if (hybrid_max_perf_cpu) |
| 1395 | + __hybrid_init_cpu_capacity_scaling(); |
| 1396 | + |
| 1397 | + mutex_unlock(&hybrid_capacity_lock); |
1183 | 1398 | }
|
1184 | 1399 |
|
1185 | 1400 | /************************** sysfs begin ************************/
|
@@ -1618,6 +1833,13 @@ static void intel_pstate_notify_work(struct work_struct *work)
|
1618 | 1833 | __intel_pstate_update_max_freq(cpudata, policy);
|
1619 | 1834 |
|
1620 | 1835 | cpufreq_cpu_release(policy);
|
| 1836 | + |
| 1837 | + /* |
| 1838 | + * The driver will not be unregistered while this function is |
| 1839 | + * running, so update the capacity without acquiring the driver |
| 1840 | + * lock. |
| 1841 | + */ |
| 1842 | + hybrid_update_capacity(cpudata); |
1621 | 1843 | }
|
1622 | 1844 |
|
1623 | 1845 | wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
|
@@ -2034,8 +2256,10 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
|
2034 | 2256 |
|
2035 | 2257 | if (pstate_funcs.get_cpu_scaling) {
|
2036 | 2258 | cpu->pstate.scaling = pstate_funcs.get_cpu_scaling(cpu->cpu);
|
2037 |
| - if (cpu->pstate.scaling != perf_ctl_scaling) |
| 2259 | + if (cpu->pstate.scaling != perf_ctl_scaling) { |
2038 | 2260 | intel_pstate_hybrid_hwp_adjust(cpu);
|
| 2261 | + hwp_is_hybrid = true; |
| 2262 | + } |
2039 | 2263 | } else {
|
2040 | 2264 | cpu->pstate.scaling = perf_ctl_scaling;
|
2041 | 2265 | }
|
@@ -2707,6 +2931,8 @@ static int intel_pstate_cpu_online(struct cpufreq_policy *policy)
|
2707 | 2931 | */
|
2708 | 2932 | intel_pstate_hwp_reenable(cpu);
|
2709 | 2933 | cpu->suspended = false;
|
| 2934 | + |
| 2935 | + hybrid_update_capacity(cpu); |
2710 | 2936 | }
|
2711 | 2937 |
|
2712 | 2938 | return 0;
|
@@ -3147,6 +3373,8 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver)
|
3147 | 3373 |
|
3148 | 3374 | global.min_perf_pct = min_perf_pct_min();
|
3149 | 3375 |
|
| 3376 | + hybrid_init_cpu_capacity_scaling(); |
| 3377 | + |
3150 | 3378 | return 0;
|
3151 | 3379 | }
|
3152 | 3380 |
|
|
0 commit comments