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>
		
			
				
	
	
		
			283 lines
		
	
	
	
		
			5.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
	
		
			5.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * arch/arm64/kernel/topology.c
 | 
						|
 *
 | 
						|
 * Copyright (C) 2011,2013,2014 Linaro Limited.
 | 
						|
 *
 | 
						|
 * Based on the arm32 version written by Vincent Guittot in turn based on
 | 
						|
 * arch/sh/kernel/topology.c
 | 
						|
 *
 | 
						|
 * This file is subject to the terms and conditions of the GNU General Public
 | 
						|
 * License.  See the file "COPYING" in the main directory of this archive
 | 
						|
 * for more details.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/cpu.h>
 | 
						|
#include <linux/cpumask.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/percpu.h>
 | 
						|
#include <linux/node.h>
 | 
						|
#include <linux/nodemask.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
 | 
						|
#include <asm/topology.h>
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
	for_each_possible_cpu(cpu) {
 | 
						|
		if (of_get_cpu_node(cpu, NULL) == cpu_node) {
 | 
						|
			of_node_put(cpu_node);
 | 
						|
			return cpu;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
 | 
						|
 | 
						|
	of_node_put(cpu_node);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int __init parse_core(struct device_node *core, int cluster_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].cluster_id = cluster_id;
 | 
						|
				cpu_topology[cpu].core_id = core_id;
 | 
						|
				cpu_topology[cpu].thread_id = i;
 | 
						|
			} else {
 | 
						|
				pr_err("%s: Can't get CPU for thread\n",
 | 
						|
				       t->full_name);
 | 
						|
				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("%s: Core has both threads and CPU\n",
 | 
						|
			       core->full_name);
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
 | 
						|
		cpu_topology[cpu].cluster_id = cluster_id;
 | 
						|
		cpu_topology[cpu].core_id = core_id;
 | 
						|
	} else if (leaf) {
 | 
						|
		pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
 | 
						|
		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 cluster_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("%s: cpu-map children should be clusters\n",
 | 
						|
				       c->full_name);
 | 
						|
				of_node_put(c);
 | 
						|
				return -EINVAL;
 | 
						|
			}
 | 
						|
 | 
						|
			if (leaf) {
 | 
						|
				ret = parse_core(c, cluster_id, core_id++);
 | 
						|
			} else {
 | 
						|
				pr_err("%s: Non-leaf cluster with core %s\n",
 | 
						|
				       cluster->full_name, name);
 | 
						|
				ret = -EINVAL;
 | 
						|
			}
 | 
						|
 | 
						|
			of_node_put(c);
 | 
						|
			if (ret != 0)
 | 
						|
				return ret;
 | 
						|
		}
 | 
						|
		i++;
 | 
						|
	} while (c);
 | 
						|
 | 
						|
	if (leaf && !has_cores)
 | 
						|
		pr_warn("%s: empty cluster\n", cluster->full_name);
 | 
						|
 | 
						|
	if (leaf)
 | 
						|
		cluster_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;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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].cluster_id == -1) {
 | 
						|
			pr_err("CPU%d: No topology information specified\n",
 | 
						|
			       cpu);
 | 
						|
			ret = -EINVAL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
out_map:
 | 
						|
	of_node_put(map);
 | 
						|
out:
 | 
						|
	of_node_put(cn);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * cpu topology table
 | 
						|
 */
 | 
						|
struct cpu_topology cpu_topology[NR_CPUS];
 | 
						|
EXPORT_SYMBOL_GPL(cpu_topology);
 | 
						|
 | 
						|
const struct cpumask *cpu_coregroup_mask(int cpu)
 | 
						|
{
 | 
						|
	return &cpu_topology[cpu].core_sibling;
 | 
						|
}
 | 
						|
 | 
						|
static void update_siblings_masks(unsigned int cpuid)
 | 
						|
{
 | 
						|
	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
 | 
						|
	int cpu;
 | 
						|
 | 
						|
	if (cpuid_topo->cluster_id == -1) {
 | 
						|
		/*
 | 
						|
		 * DT does not contain topology information for this cpu.
 | 
						|
		 */
 | 
						|
		pr_debug("CPU%u: No topology information configured\n", cpuid);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* update core and thread sibling masks */
 | 
						|
	for_each_possible_cpu(cpu) {
 | 
						|
		cpu_topo = &cpu_topology[cpu];
 | 
						|
 | 
						|
		if (cpuid_topo->cluster_id != cpu_topo->cluster_id)
 | 
						|
			continue;
 | 
						|
 | 
						|
		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
 | 
						|
		if (cpu != cpuid)
 | 
						|
			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);
 | 
						|
		if (cpu != cpuid)
 | 
						|
			cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void store_cpu_topology(unsigned int cpuid)
 | 
						|
{
 | 
						|
	update_siblings_masks(cpuid);
 | 
						|
}
 | 
						|
 | 
						|
static 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 = 0;
 | 
						|
		cpu_topo->cluster_id = -1;
 | 
						|
 | 
						|
		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 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_dt_topology())
 | 
						|
		reset_cpu_topology();
 | 
						|
}
 |