Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: topology: Add support for topology DT bindings

Add support for parsing the explicit topology bindings to discover the
topology of the system.

Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.

Signed-off-by: Mark Brown <broonie@linaro.org>
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Brown and committed by
Catalin Marinas
ebdc9447 c31bf048

+196 -8
+196 -8
arch/arm64/kernel/topology.c
··· 17 17 #include <linux/percpu.h> 18 18 #include <linux/node.h> 19 19 #include <linux/nodemask.h> 20 + #include <linux/of.h> 20 21 #include <linux/sched.h> 21 22 22 23 #include <asm/topology.h> 24 + 25 + static int __init get_cpu_for_node(struct device_node *node) 26 + { 27 + struct device_node *cpu_node; 28 + int cpu; 29 + 30 + cpu_node = of_parse_phandle(node, "cpu", 0); 31 + if (!cpu_node) 32 + return -1; 33 + 34 + for_each_possible_cpu(cpu) { 35 + if (of_get_cpu_node(cpu, NULL) == cpu_node) { 36 + of_node_put(cpu_node); 37 + return cpu; 38 + } 39 + } 40 + 41 + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name); 42 + 43 + of_node_put(cpu_node); 44 + return -1; 45 + } 46 + 47 + static int __init parse_core(struct device_node *core, int cluster_id, 48 + int core_id) 49 + { 50 + char name[10]; 51 + bool leaf = true; 52 + int i = 0; 53 + int cpu; 54 + struct device_node *t; 55 + 56 + do { 57 + snprintf(name, sizeof(name), "thread%d", i); 58 + t = of_get_child_by_name(core, name); 59 + if (t) { 60 + leaf = false; 61 + cpu = get_cpu_for_node(t); 62 + if (cpu >= 0) { 63 + cpu_topology[cpu].cluster_id = cluster_id; 64 + cpu_topology[cpu].core_id = core_id; 65 + cpu_topology[cpu].thread_id = i; 66 + } else { 67 + pr_err("%s: Can't get CPU for thread\n", 68 + t->full_name); 69 + of_node_put(t); 70 + return -EINVAL; 71 + } 72 + of_node_put(t); 73 + } 74 + i++; 75 + } while (t); 76 + 77 + cpu = get_cpu_for_node(core); 78 + if (cpu >= 0) { 79 + if (!leaf) { 80 + pr_err("%s: Core has both threads and CPU\n", 81 + core->full_name); 82 + return -EINVAL; 83 + } 84 + 85 + cpu_topology[cpu].cluster_id = cluster_id; 86 + cpu_topology[cpu].core_id = core_id; 87 + } else if (leaf) { 88 + pr_err("%s: Can't get CPU for leaf core\n", core->full_name); 89 + return -EINVAL; 90 + } 91 + 92 + return 0; 93 + } 94 + 95 + static int __init parse_cluster(struct device_node *cluster, int depth) 96 + { 97 + char name[10]; 98 + bool leaf = true; 99 + bool has_cores = false; 100 + struct device_node *c; 101 + static int cluster_id __initdata; 102 + int core_id = 0; 103 + int i, ret; 104 + 105 + /* 106 + * First check for child clusters; we currently ignore any 107 + * information about the nesting of clusters and present the 108 + * scheduler with a flat list of them. 109 + */ 110 + i = 0; 111 + do { 112 + snprintf(name, sizeof(name), "cluster%d", i); 113 + c = of_get_child_by_name(cluster, name); 114 + if (c) { 115 + leaf = false; 116 + ret = parse_cluster(c, depth + 1); 117 + of_node_put(c); 118 + if (ret != 0) 119 + return ret; 120 + } 121 + i++; 122 + } while (c); 123 + 124 + /* Now check for cores */ 125 + i = 0; 126 + do { 127 + snprintf(name, sizeof(name), "core%d", i); 128 + c = of_get_child_by_name(cluster, name); 129 + if (c) { 130 + has_cores = true; 131 + 132 + if (depth == 0) { 133 + pr_err("%s: cpu-map children should be clusters\n", 134 + c->full_name); 135 + of_node_put(c); 136 + return -EINVAL; 137 + } 138 + 139 + if (leaf) { 140 + ret = parse_core(c, cluster_id, core_id++); 141 + } else { 142 + pr_err("%s: Non-leaf cluster with core %s\n", 143 + cluster->full_name, name); 144 + ret = -EINVAL; 145 + } 146 + 147 + of_node_put(c); 148 + if (ret != 0) 149 + return ret; 150 + } 151 + i++; 152 + } while (c); 153 + 154 + if (leaf && !has_cores) 155 + pr_warn("%s: empty cluster\n", cluster->full_name); 156 + 157 + if (leaf) 158 + cluster_id++; 159 + 160 + return 0; 161 + } 162 + 163 + static int __init parse_dt_topology(void) 164 + { 165 + struct device_node *cn, *map; 166 + int ret = 0; 167 + int cpu; 168 + 169 + cn = of_find_node_by_path("/cpus"); 170 + if (!cn) { 171 + pr_err("No CPU information found in DT\n"); 172 + return 0; 173 + } 174 + 175 + /* 176 + * When topology is provided cpu-map is essentially a root 177 + * cluster with restricted subnodes. 178 + */ 179 + map = of_get_child_by_name(cn, "cpu-map"); 180 + if (!map) 181 + goto out; 182 + 183 + ret = parse_cluster(map, 0); 184 + if (ret != 0) 185 + goto out_map; 186 + 187 + /* 188 + * Check that all cores are in the topology; the SMP code will 189 + * only mark cores described in the DT as possible. 190 + */ 191 + for_each_possible_cpu(cpu) { 192 + if (cpu_topology[cpu].cluster_id == -1) { 193 + pr_err("CPU%d: No topology information specified\n", 194 + cpu); 195 + ret = -EINVAL; 196 + } 197 + } 198 + 199 + out_map: 200 + of_node_put(map); 201 + out: 202 + of_node_put(cn); 203 + return ret; 204 + } 23 205 24 206 /* 25 207 * cpu topology table ··· 221 39 222 40 if (cpuid_topo->cluster_id == -1) { 223 41 /* 224 - * DT does not contain topology information for this cpu 225 - * reset it to default behaviour 42 + * DT does not contain topology information for this cpu. 226 43 */ 227 44 pr_debug("CPU%u: No topology information configured\n", cpuid); 228 45 return; ··· 252 71 update_siblings_masks(cpuid); 253 72 } 254 73 255 - /* 256 - * init_cpu_topology is called at boot when only one cpu is running 257 - * which prevent simultaneous write access to cpu_topology array 258 - */ 259 - void __init init_cpu_topology(void) 74 + static void __init reset_cpu_topology(void) 260 75 { 261 76 unsigned int cpu; 262 77 263 - /* init core mask and power*/ 264 78 for_each_possible_cpu(cpu) { 265 79 struct cpu_topology *cpu_topo = &cpu_topology[cpu]; 266 80 ··· 268 92 cpumask_clear(&cpu_topo->thread_sibling); 269 93 cpumask_set_cpu(cpu, &cpu_topo->thread_sibling); 270 94 } 95 + } 96 + 97 + void __init init_cpu_topology(void) 98 + { 99 + reset_cpu_topology(); 100 + 101 + /* 102 + * Discard anything that was parsed if we hit an error so we 103 + * don't use partial information. 104 + */ 105 + if (parse_dt_topology()) 106 + reset_cpu_topology(); 271 107 }