Merge tag 'pm-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
[linux-2.6-block.git] / drivers / base / arch_topology.c
index 63c1e76739f12648c9face8e841d756d693dec01..1eb81f113786f3875d590ca4941e5eef7e14bb51 100644 (file)
 #include <linux/string.h>
 #include <linux/sched/topology.h>
 #include <linux/cpuset.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
 
 DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
 
@@ -174,7 +179,7 @@ init_cpu_capacity_callback(struct notifier_block *nb,
        if (!raw_capacity)
                return 0;
 
-       if (val != CPUFREQ_NOTIFY)
+       if (val != CPUFREQ_CREATE_POLICY)
                return 0;
 
        pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
@@ -241,3 +246,296 @@ static void parsing_done_workfn(struct work_struct *work)
 #else
 core_initcall(free_raw_capacity);
 #endif
+
+#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
+static int __init get_cpu_for_node(struct device_node *node)
+{
+       struct device_node *cpu_node;
+       int cpu;
+
+       cpu_node = of_parse_phandle(node, "cpu", 0);
+       if (!cpu_node)
+               return -1;
+
+       cpu = of_cpu_node_to_id(cpu_node);
+       if (cpu >= 0)
+               topology_parse_cpu_capacity(cpu_node, cpu);
+       else
+               pr_crit("Unable to find CPU node for %pOF\n", cpu_node);
+
+       of_node_put(cpu_node);
+       return cpu;
+}
+
+static int __init parse_core(struct device_node *core, int package_id,
+                            int core_id)
+{
+       char name[10];
+       bool leaf = true;
+       int i = 0;
+       int cpu;
+       struct device_node *t;
+
+       do {
+               snprintf(name, sizeof(name), "thread%d", i);
+               t = of_get_child_by_name(core, name);
+               if (t) {
+                       leaf = false;
+                       cpu = get_cpu_for_node(t);
+                       if (cpu >= 0) {
+                               cpu_topology[cpu].package_id = package_id;
+                               cpu_topology[cpu].core_id = core_id;
+                               cpu_topology[cpu].thread_id = i;
+                       } else {
+                               pr_err("%pOF: Can't get CPU for thread\n",
+                                      t);
+                               of_node_put(t);
+                               return -EINVAL;
+                       }
+                       of_node_put(t);
+               }
+               i++;
+       } while (t);
+
+       cpu = get_cpu_for_node(core);
+       if (cpu >= 0) {
+               if (!leaf) {
+                       pr_err("%pOF: Core has both threads and CPU\n",
+                              core);
+                       return -EINVAL;
+               }
+
+               cpu_topology[cpu].package_id = package_id;
+               cpu_topology[cpu].core_id = core_id;
+       } else if (leaf) {
+               pr_err("%pOF: Can't get CPU for leaf core\n", core);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __init parse_cluster(struct device_node *cluster, int depth)
+{
+       char name[10];
+       bool leaf = true;
+       bool has_cores = false;
+       struct device_node *c;
+       static int package_id __initdata;
+       int core_id = 0;
+       int i, ret;
+
+       /*
+        * First check for child clusters; we currently ignore any
+        * information about the nesting of clusters and present the
+        * scheduler with a flat list of them.
+        */
+       i = 0;
+       do {
+               snprintf(name, sizeof(name), "cluster%d", i);
+               c = of_get_child_by_name(cluster, name);
+               if (c) {
+                       leaf = false;
+                       ret = parse_cluster(c, depth + 1);
+                       of_node_put(c);
+                       if (ret != 0)
+                               return ret;
+               }
+               i++;
+       } while (c);
+
+       /* Now check for cores */
+       i = 0;
+       do {
+               snprintf(name, sizeof(name), "core%d", i);
+               c = of_get_child_by_name(cluster, name);
+               if (c) {
+                       has_cores = true;
+
+                       if (depth == 0) {
+                               pr_err("%pOF: cpu-map children should be clusters\n",
+                                      c);
+                               of_node_put(c);
+                               return -EINVAL;
+                       }
+
+                       if (leaf) {
+                               ret = parse_core(c, package_id, core_id++);
+                       } else {
+                               pr_err("%pOF: Non-leaf cluster with core %s\n",
+                                      cluster, name);
+                               ret = -EINVAL;
+                       }
+
+                       of_node_put(c);
+                       if (ret != 0)
+                               return ret;
+               }
+               i++;
+       } while (c);
+
+       if (leaf && !has_cores)
+               pr_warn("%pOF: empty cluster\n", cluster);
+
+       if (leaf)
+               package_id++;
+
+       return 0;
+}
+
+static int __init parse_dt_topology(void)
+{
+       struct device_node *cn, *map;
+       int ret = 0;
+       int cpu;
+
+       cn = of_find_node_by_path("/cpus");
+       if (!cn) {
+               pr_err("No CPU information found in DT\n");
+               return 0;
+       }
+
+       /*
+        * When topology is provided cpu-map is essentially a root
+        * cluster with restricted subnodes.
+        */
+       map = of_get_child_by_name(cn, "cpu-map");
+       if (!map)
+               goto out;
+
+       ret = parse_cluster(map, 0);
+       if (ret != 0)
+               goto out_map;
+
+       topology_normalize_cpu_scale();
+
+       /*
+        * Check that all cores are in the topology; the SMP code will
+        * only mark cores described in the DT as possible.
+        */
+       for_each_possible_cpu(cpu)
+               if (cpu_topology[cpu].package_id == -1)
+                       ret = -EINVAL;
+
+out_map:
+       of_node_put(map);
+out:
+       of_node_put(cn);
+       return ret;
+}
+#endif
+
+/*
+ * cpu topology table
+ */
+struct cpu_topology cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+       const cpumask_t *core_mask = cpumask_of_node(cpu_to_node(cpu));
+
+       /* Find the smaller of NUMA, core or LLC siblings */
+       if (cpumask_subset(&cpu_topology[cpu].core_sibling, core_mask)) {
+               /* not numa in package, lets use the package siblings */
+               core_mask = &cpu_topology[cpu].core_sibling;
+       }
+       if (cpu_topology[cpu].llc_id != -1) {
+               if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
+                       core_mask = &cpu_topology[cpu].llc_sibling;
+       }
+
+       return core_mask;
+}
+
+void update_siblings_masks(unsigned int cpuid)
+{
+       struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+       int cpu;
+
+       /* update core and thread sibling masks */
+       for_each_online_cpu(cpu) {
+               cpu_topo = &cpu_topology[cpu];
+
+               if (cpuid_topo->llc_id == cpu_topo->llc_id) {
+                       cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
+                       cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
+               }
+
+               if (cpuid_topo->package_id != cpu_topo->package_id)
+                       continue;
+
+               cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+               cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+               if (cpuid_topo->core_id != cpu_topo->core_id)
+                       continue;
+
+               cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+               cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+       }
+}
+
+static void clear_cpu_topology(int cpu)
+{
+       struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+
+       cpumask_clear(&cpu_topo->llc_sibling);
+       cpumask_set_cpu(cpu, &cpu_topo->llc_sibling);
+
+       cpumask_clear(&cpu_topo->core_sibling);
+       cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
+       cpumask_clear(&cpu_topo->thread_sibling);
+       cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
+}
+
+void __init reset_cpu_topology(void)
+{
+       unsigned int cpu;
+
+       for_each_possible_cpu(cpu) {
+               struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+
+               cpu_topo->thread_id = -1;
+               cpu_topo->core_id = -1;
+               cpu_topo->package_id = -1;
+               cpu_topo->llc_id = -1;
+
+               clear_cpu_topology(cpu);
+       }
+}
+
+void remove_cpu_topology(unsigned int cpu)
+{
+       int sibling;
+
+       for_each_cpu(sibling, topology_core_cpumask(cpu))
+               cpumask_clear_cpu(cpu, topology_core_cpumask(sibling));
+       for_each_cpu(sibling, topology_sibling_cpumask(cpu))
+               cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
+       for_each_cpu(sibling, topology_llc_cpumask(cpu))
+               cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));
+
+       clear_cpu_topology(cpu);
+}
+
+__weak int __init parse_acpi_topology(void)
+{
+       return 0;
+}
+
+#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
+void __init init_cpu_topology(void)
+{
+       reset_cpu_topology();
+
+       /*
+        * Discard anything that was parsed if we hit an error so we
+        * don't use partial information.
+        */
+       if (parse_acpi_topology())
+               reset_cpu_topology();
+       else if (of_have_populated_dt() && parse_dt_topology())
+               reset_cpu_topology();
+}
+#endif