[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 07/21] xen/device-tree: Parse 'cpu-map' node for CPU topology exploration


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Hirokazu Takahashi <taka@xxxxxxxxxxxxx>
  • Date: Sun, 24 May 2026 09:01:55 +0900
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=valinux.co.jp; dmarc=pass action=none header.from=valinux.co.jp; dkim=pass header.d=valinux.co.jp; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=sCyLbL/Ss/VTqlq+c6wx572rq1avvD+OA4b+ce+lLNM=; b=OVcEObM3eBJqrvT3cQ5fX8J5arpp3wKcO+P2K2QglREiNLTeLUDh060xMd3mZ7NPvG19jOLYlgslEST/NCyZdKpjX5k9SF1GQpUTbvf4Ecj7JUvuHoegUBHjn99LPW0+8oh6BOtX59GoQ0MhXzmRa3PUinLlfgZ549nV80/c5hKRmpr5m3I2El34ja27NNnNXRADx7c9o5EEqcwhS0/Zi0FI9UjgLT/gz1ncKPUf/nX7dvfXhhwcEY2Z/lQKAZlzrfiPsV2Sw6WRwZ0vwYQ8WtVu4uVy4uODVw60kNj9uFxMVew9uTExHEncwGC/w5GUMMRIE7cNzCxwkJxMn/Ki7Q==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ja6y0onw41r2rJDQOemoDSgy66eGM6ASzMkFlWk6Y23mauRNldBo5Z/xQ1NL4lOT39TavGtlHrjHupmnHwxYbekGXf4dgNfQGNxzz3RRYvSDZ+26kbGqJEvkIA3rSeAHt9rBbGkVYhGB4fiQYtbCCjSQDgbrdvO0qaeLcKESxkh7nrWY+shUy54TO/NXZCwfQVIxaeIzZlFYIkkNuuN/VGMy8nTFPdDEkInYAEQVI8xEk6uwMzhIwmw+kQomLFQUotA1EAvnVnvnhjwWdgw4IKW8GEXPvp7OXErdudjTjZH8rXCMY5VJlY9Zp1W/n+KFwKZF03O83fmUgBwQRlT+7A==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=valinux.co.jp header.i="@valinux.co.jp" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=valinux.co.jp;
  • Cc: andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, michal.orzel@xxxxxxx, jbeulich@xxxxxxxx, julien@xxxxxxx, roger.pau@xxxxxxxxxx, sstabellini@xxxxxxxxxx, jgross@xxxxxxxx, bertrand.marquis@xxxxxxx, Volodymyr_Babchuk@xxxxxxxx, dfaggioli@xxxxxxxx, gwd@xxxxxxxxxxxxxx, Hirokazu Takahashi <taka@xxxxxxxxxxxxx>
  • Delivery-date: Sun, 24 May 2026 00:02:46 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

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.
---
 xen/arch/arm/smpboot.c                |   6 +
 xen/common/Kconfig                    |   7 +
 xen/common/device-tree/Makefile       |   1 +
 xen/common/device-tree/cpu_topology.c | 307 ++++++++++++++++++++++++++
 xen/include/xen/cpu_topology.h        |  42 ++++
 5 files changed, 363 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 eafa195504..ff8b0d07e9 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>
@@ -248,6 +249,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() 
*/
+
 #ifdef CONFIG_DEVICE_TREE_NUMA
         if ( tmp_map[i] != MPIDR_INVALID )
         {
@@ -294,7 +297,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 2365b421bf..014192b2cd 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 && ARM
+       depends on HAS_DEVICE_TREE_DISCOVERY
+       help
+         Retrieve CPU topology information from the device tree to optimize
+         virtual CPU scheduling.
+
 config DEVICE_TREE_NUMA
        bool "Device tree based NUMA support (UNSUPPORTED)" if UNSUPPORTED && 
ARM
        depends on HAS_DEVICE_TREE_DISCOVERY
diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile
index fab038d357..dbfce294fb 100644
--- a/xen/common/device-tree/Makefile
+++ b/xen/common/device-tree/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_DOMAIN_BUILD_HELPERS) += kernel.o
 obj-$(CONFIG_STATIC_EVTCHN) += static-evtchn.init.o
 obj-$(CONFIG_STATIC_MEMORY) += static-memory.init.o
 obj-$(CONFIG_STATIC_SHM) += static-shmem.init.o
+obj-$(CONFIG_DT_CPU_TOPOLOGY) += cpu_topology.o
 obj-$(CONFIG_DEVICE_TREE_NUMA) += numa.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..6c78a74778
--- /dev/null
+++ b/xen/common/device-tree/cpu_topology.c
@@ -0,0 +1,307 @@
+/* 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>
+
+static struct dt_device_node * __initdata dt_cpu_table[NR_CPUS];
+
+struct cpu_topology __ro_after_init cpu_topology[NR_CPUS] = {
+    [0 ... NR_CPUS-1] = {-1, -1, -1, 0}
+};
+
+static void __init setup_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_possible_cpu(cpu) {
+        cpu_topo = &cpu_topology[cpu];
+
+        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->cluster_id != cpu_topo->cluster_id)
+            continue;
+
+        if (cpuid_topo->cluster_id >= 0) {
+            cpumask_set_cpu(cpu, &cpuid_topo->cluster_sibling);
+            cpumask_set_cpu(cpuid, &cpu_topo->cluster_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 struct dt_device_node * __init dt_find_child_node_by_name(struct 
dt_device_node *from, const char *name)
+{
+    struct dt_device_node *np;
+    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(u32 cpuid, struct dt_device_node *cpu_node)
+{
+    dt_cpu_table[cpuid] = cpu_node;
+}
+
+static int __init cpu_node_to_id(struct dt_device_node *cpu_node)
+{
+    int cpu;
+    bool found = false;
+
+    for_each_possible_cpu(cpu) {
+        found = (cpu_node == dt_cpu_table[cpu]);
+        if (found)
+            return cpu;
+    }
+
+    return -ENODEV;
+}
+
+/*
+ * This function returns the logic cpu number of the node.
+ */
+static 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 -ENOENT;
+
+    return cpu_node_to_id(cpu_node);
+}
+
+static int __init parse_core(struct dt_device_node *core, int package_id,
+                int cluster_id, int core_id)
+{
+    char name[20];
+    bool leaf = true;
+    int i = 0;
+    int cpu;
+
+    do {
+        struct dt_device_node *t;
+
+        snprintf(name, sizeof(name), "thread%d", i);
+        t = dt_find_child_node_by_name(core, name);
+
+        if (!t)
+            break;
+
+        leaf = false;
+        cpu = get_cpu_for_node(t);
+        if (cpu >= 0) {
+            cpu_topology[cpu].package_id = package_id;
+            cpu_topology[cpu].cluster_id = cluster_id;
+            cpu_topology[cpu].core_id = core_id;
+            cpu_topology[cpu].thread_id = i;
+        } else if (cpu != -ENODEV) {
+            printk(XENLOG_ERR "ERROR: %pOF: Can't get CPU for thread\n", t);
+            return -EINVAL;
+        }
+        i++;
+    } while (1);
+
+    cpu = get_cpu_for_node(core);
+    if (cpu >= 0) {
+        if (!leaf) {
+            printk(XENLOG_ERR "ERROR: %pOF: Core has both threads and CPU\n",
+                   core);
+            return -EINVAL;
+        }
+
+        cpu_topology[cpu].package_id = package_id;
+        cpu_topology[cpu].cluster_id = cluster_id;
+        cpu_topology[cpu].core_id = core_id;
+    } else if (leaf && cpu != -ENODEV) {
+        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, int package_id,
+                int cluster_id, int depth)
+{
+    char name[20];
+    bool leaf = true;
+    bool has_cores = false;
+    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 {
+        struct dt_device_node *c;
+
+        snprintf(name, sizeof(name), "cluster%d", i);
+        c = dt_find_child_node_by_name(cluster, name);
+
+        if (!c)
+            break;
+
+        leaf = false;
+        ret = parse_cluster(c, package_id, i, depth + 1);
+        if (depth > 0)
+            printk(XENLOG_WARNING "WARNING: Topology for clusters of clusters 
not yet supported\n");
+        if (ret != 0)
+            return ret;
+        i++;
+    } while (1);
+
+    /* Now check for cores */
+    i = 0;
+    do {
+        struct dt_device_node *c;
+
+        snprintf(name, sizeof(name), "core%d", i);
+        c = dt_find_child_node_by_name(cluster, name);
+
+        if (!c)
+            break;
+
+        has_cores = true;
+
+        if (depth == 0) {
+            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 (1);
+
+    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;
+    int package_id = 0, ret;
+
+    do {
+        struct dt_device_node *c;
+
+        snprintf(name, sizeof(name), "socket%d", package_id);
+        c = dt_find_child_node_by_name(socket, name);
+
+        if (!c)
+            break;
+
+        has_socket = true;
+        ret = parse_cluster(c, package_id, -1, 0);
+        if (ret != 0)
+            return ret;
+
+        package_id++;
+    } while (1);
+
+    if (!has_socket)
+        ret = parse_cluster(socket, 0, -1, 0);
+
+    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)
+{
+    int cpu;
+    int clid = 0;
+    int pkgid = 0;
+
+    for_each_possible_cpu(cpu)
+    {
+        struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+
+        cpu_topo->package_id = cpu_to_node(cpu);
+        if (cpu_topo->package_id != pkgid)
+        {
+            pkgid = cpu_topo->package_id;
+            clid = 0;
+        }
+        cpu_topo->cluster_id = clid++;
+        cpu_topo->core_id = 0;
+        cpu_topo->thread_id = 0;
+    }
+}
+
+
+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)
+{
+    int cpu;
+
+    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..0cdceb9bd0
--- /dev/null
+++ b/xen/include/xen/cpu_topology.h
@@ -0,0 +1,42 @@
+/* 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 {
+    int thread_id;
+    int core_id;
+    int cluster_id;
+    int package_id;
+    cpumask_t thread_sibling;
+    cpumask_t core_sibling;
+    cpumask_t cluster_sibling;
+};
+
+
+#ifdef CONFIG_DT_CPU_TOPOLOGY
+extern struct cpu_topology cpu_topology[NR_CPUS];
+
+void map_cpuid_to_node(u32 cpuid, struct dt_device_node *cpu_node);
+void dt_init_cpu_topology(void);
+
+#else /* CONFIG_DT_CPU_TOPOLOGY */
+
+static inline void map_cpuid_to_node(u32 cpuid, struct dt_device_node 
*cpu_node) {}
+static inline void dt_init_cpu_topology(void) {}
+
+#endif /* CONFIG_DT_CPU_TOPOLOGY */
+
+#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




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.