|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 1/3] xen/device-tree: Parse 'cpu-map' node for CPU topology exploration
Parse the 'cpu-map' node in the Device Tree to extract CPU topology
information. If the 'cpu-map' node is absent, fall back to
generating the topology data from the NUMA information. This
generation assumes exactly one socket per NUMA node and that SMT
is unsupported.
Signed-off-by: Hirokazu Takahashi <taka@xxxxxxxxxxxxx>
---
xen/arch/arm/smpboot.c | 6 +
xen/common/Kconfig | 7 +
xen/common/device-tree/Makefile | 1 +
xen/common/device-tree/cpu-topology.c | 342 ++++++++++++++++++++++++++
xen/include/xen/cpu-topology.h | 38 +++
5 files changed, 394 insertions(+)
create mode 100644 xen/common/device-tree/cpu-topology.c
create mode 100644 xen/include/xen/cpu-topology.h
diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
index 7f3cfa812e..c071f1494f 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -24,6 +24,7 @@
#include <xen/warning.h>
#include <xen/irq.h>
#include <xen/console.h>
+#include <xen/cpu-topology.h>
#include <asm/cpuerrata.h>
#include <asm/gic.h>
#include <asm/procinfo.h>
@@ -242,6 +243,8 @@ static void __init dt_smp_init_cpus(void)
}
else
tmp_map[i] = hwid;
+
+ map_cpuid_to_node(i, cpu); /* pass the info to dt_init_cpu_topology()
*/
}
if ( !bootcpu_valid )
@@ -275,7 +278,10 @@ void __init smp_init_cpus(void)
}
if ( acpi_disabled )
+ {
dt_smp_init_cpus();
+ dt_init_cpu_topology();
+ }
else
acpi_smp_init_cpus();
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 5ff71480ee..9f9b48a4b7 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -188,6 +188,13 @@ config VM_EVENT
config NEEDS_LIBELF
bool
+config DT_CPU_TOPOLOGY
+ bool "Device tree based CPU topology support (UNSUPPORTED)" if
UNSUPPORTED
+ depends on HAS_DEVICE_TREE_DISCOVERY
+ help
+ Retrieve CPU topology information from the device tree to optimize
+ virtual CPU scheduling.
+
config NUMA
bool
diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile
index 9036e455d6..38bc5d5306 100644
--- a/xen/common/device-tree/Makefile
+++ b/xen/common/device-tree/Makefile
@@ -1,6 +1,7 @@
obj-y += bootfdt.init.o
obj-$(CONFIG_HAS_DEVICE_TREE_DISCOVERY) += bootinfo-fdt.init.o
obj-$(CONFIG_HAS_DEVICE_TREE_DISCOVERY) += bootinfo.init.o
+obj-$(CONFIG_DT_CPU_TOPOLOGY) += cpu-topology.o
obj-y += device-tree.o
obj-$(CONFIG_DOMAIN_BUILD_HELPERS) += domain-build.init.o
obj-$(filter $(CONFIG_DOM0LESS_BOOT),$(CONFIG_HAS_DEVICE_TREE_DISCOVERY)) +=
dom0less-build.init.o
diff --git a/xen/common/device-tree/cpu-topology.c
b/xen/common/device-tree/cpu-topology.c
new file mode 100644
index 0000000000..bbdf0d1fe8
--- /dev/null
+++ b/xen/common/device-tree/cpu-topology.c
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Derived from Linux kernel 7.0's $drivers/base/arch_topology.c
+ * Parse cpu topology information.
+ *
+ * Copyright (c) 2026 VA Linux Systems Japan K.K.
+ * Author: Hirokazu Takahashi <taka@xxxxxxxxxxxxx>
+ */
+
+#include <xen/cpu.h>
+#include <xen/cpumask.h>
+#include <xen/delay.h>
+#include <xen/device_tree.h>
+#include <xen/cpu-topology.h>
+#include <xen/numa.h>
+#include <xen/domain_page.h>
+#include <xen/errno.h>
+#include <xen/init.h>
+
+struct cpu_map {
+ unsigned int thread_id;
+ unsigned int core_id;
+ unsigned int cluster_id;
+ unsigned int package_id;
+};
+
+struct cpu_topology *cpu_topology;
+static struct cpu_map __initdata cpu_map[NR_CPUS] = {
+ [0 ... NR_CPUS-1] = {~0U, ~0U, ~0U, 0U}
+};
+static struct dt_device_node * __initdata dt_cpu_table[NR_CPUS];
+static const unsigned int __initdata invalid_topo_id = (~0U);
+
+static void __init setup_siblings_masks(unsigned int cpuid)
+{
+ struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
+ struct cpu_map *cpuid_map = &cpu_map[cpuid];
+ unsigned int cpu;
+
+ /* Update core and thread sibling masks */
+ for_each_possible_cpu( cpu )
+ {
+ struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+ struct cpu_map *map = &cpu_map[cpu];
+
+ if ( cpuid_map->package_id != map->package_id )
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if ( cpuid_map->cluster_id != map->cluster_id )
+ continue;
+
+ if ( cpuid_map->cluster_id != invalid_topo_id )
+ {
+ cpumask_set_cpu(cpu, &cpuid_topo->cluster_sibling);
+ cpumask_set_cpu(cpuid, &cpu_topo->cluster_sibling);
+ }
+
+ if ( cpuid_map->core_id != map->core_id )
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+}
+
+static struct dt_device_node * __init dt_find_child_node_by_name(struct
dt_device_node *from, const char *name)
+{
+ struct dt_device_node *np;
+ const struct dt_device_node *dt = from;
+
+ dt_for_each_child_node( dt, np )
+ if ( np->name && (dt_node_cmp(np->name, name) == 0) )
+ break;
+
+ return np;
+}
+
+void __init map_cpuid_to_node(unsigned int cpuid, struct dt_device_node
*cpu_node)
+{
+ if ( cpuid < NR_CPUS )
+ dt_cpu_table[cpuid] = cpu_node;
+}
+
+static unsigned int __init cpu_node_to_id(struct dt_device_node *cpu_node)
+{
+ unsigned int cpu;
+ bool found = false;
+
+ for_each_possible_cpu( cpu )
+ {
+ found = (cpu_node == dt_cpu_table[cpu]);
+ if ( found )
+ return cpu;
+ }
+
+ return invalid_topo_id;
+}
+
+/*
+ * This function returns the logic cpu number of the node.
+ */
+static unsigned int __init get_cpu_for_node(struct dt_device_node *node)
+{
+ struct dt_device_node *cpu_node = dt_parse_phandle(node, "cpu", 0);
+
+ if ( !cpu_node )
+ return invalid_topo_id;
+
+ return cpu_node_to_id(cpu_node);
+}
+
+static int __init parse_core(struct dt_device_node *core,
+ unsigned int package_id, unsigned int cluster_id,
+ unsigned int core_id)
+{
+ char name[20];
+ bool leaf = true;
+ unsigned int i = 0U;
+ unsigned int cpu;
+
+ do {
+ struct dt_device_node *t;
+
+ snprintf(name, sizeof(name), "thread%u", i);
+ t = dt_find_child_node_by_name(core, name);
+
+ if ( !t )
+ break;
+
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if ( cpu != invalid_topo_id )
+ {
+ cpu_map[cpu].package_id = package_id;
+ cpu_map[cpu].cluster_id = cluster_id;
+ cpu_map[cpu].core_id = core_id;
+ cpu_map[cpu].thread_id = i;
+ }
+ else
+ {
+ printk(XENLOG_ERR "ERROR: %pOF: Can't get CPU for thread\n", t);
+ return -EINVAL;
+ }
+ i++;
+ } while ( true );
+
+ cpu = get_cpu_for_node(core);
+
+ if ( cpu != invalid_topo_id )
+ {
+ if ( !leaf )
+ {
+ printk(XENLOG_ERR "ERROR: %pOF: Core has both threads and CPU\n",
+ core);
+ return -EINVAL;
+ }
+
+ cpu_map[cpu].package_id = package_id;
+ cpu_map[cpu].cluster_id = cluster_id;
+ cpu_map[cpu].core_id = core_id;
+ cpu_map[cpu].thread_id = 0U;
+ }
+ else if ( leaf )
+ {
+ printk(XENLOG_ERR "ERROR: %pOF: Can't get CPU for leaf core\n", core);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init parse_cluster(struct dt_device_node *cluster,
+ unsigned int package_id, unsigned int cluster_id,
+ unsigned int depth)
+{
+ char name[20];
+ bool leaf = true;
+ bool has_cores = false;
+ unsigned int core_id = 0U;
+ unsigned int i;
+ int 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 = 0U;
+ do {
+ struct dt_device_node *c;
+
+ snprintf(name, sizeof(name), "cluster%u", i);
+ c = dt_find_child_node_by_name(cluster, name);
+
+ if ( !c )
+ break;
+
+ leaf = false;
+ ret = parse_cluster(c, package_id, i, depth + 1U);
+ if ( depth > 0U )
+ printk(XENLOG_WARNING "WARNING: Topology for clusters of clusters
not yet supported\n");
+ if ( ret != 0 )
+ return ret;
+ i++;
+ } while ( true );
+
+ /* Now check for cores */
+ i = 0U;
+ do {
+ struct dt_device_node *c;
+
+ snprintf(name, sizeof(name), "core%u", i);
+ c = dt_find_child_node_by_name(cluster, name);
+
+ if ( !c )
+ break;
+
+ has_cores = true;
+
+ if ( depth == 0U )
+ {
+ printk(XENLOG_ERR "ERROR: %pOF: cpu-map children should be
clusters\n", c);
+ return -EINVAL;
+ }
+
+ if ( leaf )
+ {
+ ret = parse_core(c, package_id, cluster_id, core_id++);
+ if ( ret != 0 )
+ return ret;
+ }
+ else
+ {
+ printk(XENLOG_ERR "ERROR: %pOF: Non-leaf cluster with core %s\n",
+ cluster, name);
+ return -EINVAL;
+ }
+
+ i++;
+ } while ( true );
+
+ if ( leaf && !has_cores )
+ printk(XENLOG_WARNING "WARNING: %pOF: empty cluster\n", cluster);
+
+ return 0;
+}
+
+static int __init parse_socket(struct dt_device_node *socket)
+{
+ char name[20];
+ bool has_socket = false;
+ unsigned int package_id = 0U;
+ int ret;
+
+ do {
+ struct dt_device_node *c;
+
+ snprintf(name, sizeof(name), "socket%u", package_id);
+ c = dt_find_child_node_by_name(socket, name);
+
+ if ( !c )
+ break;
+
+ has_socket = true;
+ ret = parse_cluster(c, package_id, invalid_topo_id, 0U);
+ if ( ret != 0 )
+ return ret;
+
+ package_id++;
+ } while ( true );
+
+ if ( !has_socket )
+ ret = parse_cluster(socket, 0U, invalid_topo_id, 0U);
+
+ return ret;
+}
+
+/*
+ * Generate cpu topology information when cpu-map node doesn't exist.
+ * It assumes that the cpu doesn't have SMT and all CPUs on a NUMA
+ * node belong to the same socket.
+ */
+static void __init fixup_topology(void)
+{
+ unsigned int cpu;
+ unsigned int clid = 0U;
+ unsigned int pkgid = 0U;
+
+ for_each_possible_cpu( cpu )
+ {
+ struct cpu_map *map = &cpu_map[cpu];
+
+ map->package_id = cpu_to_node(cpu);
+ if ( map->package_id != pkgid )
+ {
+ pkgid = map->package_id;
+ clid = 0U;
+ }
+ map->cluster_id = clid++;
+ map->core_id = 0U;
+ map->thread_id = 0U;
+ }
+}
+
+int __init parse_dt_topology(void)
+{
+ struct dt_device_node *cpus;
+ struct dt_device_node *map;
+
+ cpus = dt_find_node_by_path("/cpus");
+
+ if ( !cpus )
+ {
+ printk(XENLOG_ERR "ERROR: No CPU information found in DT\n");
+ return -EINVAL;
+ }
+
+ map = dt_find_child_node_by_name(cpus, "cpu-map");
+ if ( !map )
+ return -ENOENT;
+
+ return parse_socket(map);
+}
+
+void __init dt_init_cpu_topology(void)
+{
+ unsigned int cpu;
+ const unsigned int nr_cpus = cpumask_last(&cpu_possible_map) + 1U;
+
+ cpu_topology = xzalloc_array(struct cpu_topology, nr_cpus);
+ if ( !cpu_topology )
+ panic("Failed to allocate memory for cpu_topology array\n");
+
+ if (parse_dt_topology())
+ fixup_topology();
+
+ for_each_possible_cpu( cpu )
+ setup_siblings_masks(cpu);
+}
diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h
new file mode 100644
index 0000000000..1c03f4deaa
--- /dev/null
+++ b/xen/include/xen/cpu-topology.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef XEN_CPU_TOPOLOGY_H
+#define XEN_CPU_TOPOLOGY_H
+
+#include <xen/types.h>
+#include <xen/device_tree.h>
+
+struct cpu_topology {
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+ cpumask_t cluster_sibling;
+};
+
+
+#ifdef CONFIG_DT_CPU_TOPOLOGY
+
+extern struct cpu_topology *cpu_topology;
+void map_cpuid_to_node(unsigned int cpuid, struct dt_device_node *cpu_node);
+void dt_init_cpu_topology(void);
+
+#elif CONFIG_DEVICE_TREE_PARSE
+
+static inline void map_cpuid_to_node(unsigned int cpuid, struct dt_device_node
*cpu_node) {}
+static inline void dt_init_cpu_topology(void) {}
+
+#endif /* CONFIG_DEVICE_TREE_PARSE */
+
+#endif /* XEN_CPU_TOPOLOGY_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
2.43.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |