arch_topology: obtain cpu capacity using information from CPPC
authorIonela Voinescu <ionela.voinescu@arm.com>
Thu, 10 Mar 2022 14:54:50 +0000 (14:54 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 10 Mar 2022 19:21:58 +0000 (20:21 +0100)
Define topology_init_cpu_capacity_cppc() to use highest performance
values from _CPC objects to obtain and set maximum capacity information
for each CPU. acpi_cppc_processor_probe() is a good point at which to
trigger the initialization of CPU (u-arch) capacity values, as at this
point the highest performance values can be obtained from each CPU's
_CPC objects. Architectures can therefore use this functionality
through arch_init_invariance_cppc().

The performance scale used by CPPC is a unified scale for all CPUs in
the system. Therefore, by obtaining the raw highest performance values
from the _CPC objects, and normalizing them on the [0, 1024] capacity
scale, used by the task scheduler, we obtain the CPU capacity of each
CPU.

While an ACPI Notify(0x85) could alert about a change in the highest
performance value, which should in turn retrigger the CPU capacity
computations, this notification is not currently handled by the ACPI
processor driver. When supported, a call to arch_init_invariance_cppc()
would perform the update.

Signed-off-by: Ionela Voinescu <ionela.voinescu@arm.com>
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
Tested-by: Valentin Schneider <valentin.schneider@arm.com>
Tested-by: Yicong Yang <yangyicong@hisilicon.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/base/arch_topology.c
include/linux/arch_topology.h

index 976154140f0b00660df7f940f90267d1a2a63387..1d6636ebaac5bbbf7d2844777395acdcc61cefaf 100644 (file)
@@ -339,6 +339,46 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
        return !ret;
 }
 
+#ifdef CONFIG_ACPI_CPPC_LIB
+#include <acpi/cppc_acpi.h>
+
+void topology_init_cpu_capacity_cppc(void)
+{
+       struct cppc_perf_caps perf_caps;
+       int cpu;
+
+       if (likely(acpi_disabled || !acpi_cpc_valid()))
+               return;
+
+       raw_capacity = kcalloc(num_possible_cpus(), sizeof(*raw_capacity),
+                              GFP_KERNEL);
+       if (!raw_capacity)
+               return;
+
+       for_each_possible_cpu(cpu) {
+               if (!cppc_get_perf_caps(cpu, &perf_caps) &&
+                   (perf_caps.highest_perf >= perf_caps.nominal_perf) &&
+                   (perf_caps.highest_perf >= perf_caps.lowest_perf)) {
+                       raw_capacity[cpu] = perf_caps.highest_perf;
+                       pr_debug("cpu_capacity: CPU%d cpu_capacity=%u (raw).\n",
+                                cpu, raw_capacity[cpu]);
+                       continue;
+               }
+
+               pr_err("cpu_capacity: CPU%d missing/invalid highest performance.\n", cpu);
+               pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
+               goto exit;
+       }
+
+       topology_normalize_cpu_scale();
+       schedule_work(&update_topology_flags_work);
+       pr_debug("cpu_capacity: cpu_capacity initialization done\n");
+
+exit:
+       free_raw_capacity();
+}
+#endif
+
 #ifdef CONFIG_CPU_FREQ
 static cpumask_var_t cpus_to_visit;
 static void parsing_done_workfn(struct work_struct *work);
@@ -387,9 +427,8 @@ static int __init register_cpufreq_notifier(void)
        int ret;
 
        /*
-        * on ACPI-based systems we need to use the default cpu capacity
-        * until we have the necessary code to parse the cpu capacity, so
-        * skip registering cpufreq notifier.
+        * On ACPI-based systems skip registering cpufreq notifier as cpufreq
+        * information is not needed for cpu capacity initialization.
         */
        if (!acpi_disabled || !raw_capacity)
                return -EINVAL;
index cce6136b300a6445bb416e4d7f9691ab265657c0..58cbe18d825c222be4638bf891ad8362c11075ba 100644 (file)
 void topology_normalize_cpu_scale(void);
 int topology_update_cpu_topology(void);
 
+#ifdef CONFIG_ACPI_CPPC_LIB
+void topology_init_cpu_capacity_cppc(void);
+#endif
+
 struct device_node;
 bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu);