:p
atchew
Login
Hello, This patch series introduces Device-Tree based CPU topology support for ARM Xen. This version incorporates the feedback received from Jan on the v3 submission. The main focus of this update includes transitioning towards a generic Kconfig structure, untangling header dependencies and various code cleanups. Future Work: - Support for the "credit2_runqueue=cluster" option. - CPU topology support for RISC-V and PPC (once SMP support is available). - Topology information generation from the ACPI PPTT. Changes in v4: - Only display the CPU topology configuration prompt in common/Kconfig if the architecture defines HAS_GENERIC_CPU_TOPOLOGY. - Move the definition of the global 'cpu_topology' pointer to common/cpu-topology.c. - Update the Makefile to explicitly build objects as .init.o when all functions and data within a file are annotated with __init/__initdata, ensuring their memory is reclaimed after system initialization. - Add an error log in the CPU-to-node mapping function for out-of-bounds cases. - Use ARRAY_SIZE() instead of raw macros when guarding array accesses. - Rename variables and functions to avoid ambiguous or misleading terms: - Avoid 'cpuid' to prevent confusion with x86 CPUID features/instructions. - Avoid 'node' where it could be confused with a NUMA node, explicitly renaming them to clarify they refer to a Device Tree node. - Move local variable declarations into the narrowest possible scope. - Replace the unsupported "%pOF" printk format specifier with "%s" and explicit node name retrieval. - Remove #include <dt-cpu-topology.h> from cpu-topology.h, and ensure the header directly includes only what its definitions require. - Remove #include <xen/device_tree.h> from dt-cpu-topology.h, replacing it with a forward declaration of 'struct dt_device_node'. - Use 'const' qualifiers for pointer declarations where the pointed-to structure is not modified. - Explicitly #include <asm/processor.h> in cpu-topology.h to guarantee that arch-specific definitions of cpu_to_core() and cpu_to_socket() take precedence over the generic fallbacks. - Introduce inline initialization functions for cpu_sibling_mask and cpu_core_mask in cpu-topology.h, providing separate variants for both when CONFIG_GENERIC_CPU_TOPOLOGY is enabled and disabled. Changes in v3: - Use (nr_cpu_ids - 1) as the maximum CPU ID here. The fix for the sparse map mismatch issue on ARM Xen has been split out into a separate patch. - Switch topology sibling masks to cpumask_var_t for dynamic allocation. - Allow the system to keep running with a degraded fallback even if the topology table allocation fails. - Remove the temporary definitions of cpu_to_core() and cpu_to_socket() from RISC-V and PPC processor.h. - Minimize the use of #ifdef blocks, leveraging compiler Dead Code Elimination (DCE) where possible. - Clean up the code to follow the Xen coding style. Please let me know if I missed any style nits! - Verify successful builds across x86, RISC-V, and PPC environments. Changes in v2: - Generate topology information even when ACPI is enabled. Note that this is a temporary implementation and doesn't yet parse the PPTT (Processor Properties Topology Table). - Added support for cpu-map node in Device Tree that doesn't contain explicit cluster node definitions. Changes in v1 from the previous series "Introduce Device Tree based NUMA support for ARM Xen": 1. Optimized Memory Allocation: The series now allocates only the minimum required memory area to manage the essential data for the CPUs. 2. Flexible Device Tree Parsing: The parsing logic no longer depends on the definition order of the 'cpu' nodes and 'cpu-map' nodes in the Device Tree. They can now be read correctly even if their orders do not match. 3. CPU Hotplug Readiness: To support future CPU hotplug, the system assumes that inactive CPUs are also described in the Device Tree. Xen will pre-load and generate the topology information for these inactive CPUs during the boot phase so it stays available in memory. Thank you, Hirokazu Takahashi Hirokazu Takahashi (3): xen/device-tree: Parse 'cpu-map' node for CPU topology exploration xen/sched: Link CPU topology to scheduler xen/sched: Make cpu_nr_siblings() architecture-specific xen/arch/arm/Kconfig | 1 + xen/arch/arm/include/asm/processor.h | 4 - xen/arch/arm/smpboot.c | 15 +- xen/arch/ppc/include/asm/processor.h | 4 - xen/arch/riscv/include/asm/processor.h | 4 - xen/arch/x86/include/asm/processor.h | 1 + xen/common/Kconfig | 18 ++ xen/common/Makefile | 1 + xen/common/cpu-topology.c | 62 ++++ xen/common/device-tree/Makefile | 1 + xen/common/device-tree/cpu-topology.c | 406 +++++++++++++++++++++++++ xen/common/sched/credit2.c | 21 +- xen/common/sysctl.c | 1 + xen/drivers/acpi/Kconfig | 3 + xen/drivers/acpi/Makefile | 1 + xen/drivers/acpi/topology.c | 41 +++ xen/include/xen/acpi.h | 2 + xen/include/xen/cpu-topology.h | 83 +++++ xen/include/xen/dt-cpu-topology.h | 29 ++ 19 files changed, 662 insertions(+), 36 deletions(-) create mode 100644 xen/common/cpu-topology.c create mode 100644 xen/common/device-tree/cpu-topology.c create mode 100644 xen/drivers/acpi/topology.c create mode 100644 xen/include/xen/cpu-topology.h create mode 100644 xen/include/xen/dt-cpu-topology.h -- 2.43.0
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@valinux.co.jp> --- Changes in v4: - Only display the CPU topology configuration prompt in common/Kconfig if the architecture defines HAS_GENERIC_CPU_TOPOLOGY. - Move the definition of the global 'cpu_topology' pointer to common/cpu-topology.c. - Update the Makefile to explicitly build objects as .init.o when all functions and data within a file are annotated with __init/__initdata, ensuring their memory is reclaimed after system initialization. - Add an error log in the CPU-to-node mapping function for out-of-bounds cases. - Use ARRAY_SIZE() instead of raw macros when guarding array accesses. - Rename variables and functions to avoid ambiguous or misleading terms: - Avoid 'cpuid' to prevent confusion with x86 CPUID features/instructions. - Avoid 'node' where it could be confused with a NUMA node, explicitly renaming them to clarify they refer to a Device Tree node. - Move local variable declarations into the narrowest possible scope. - Replace the unsupported "%pOF" printk format specifier with "%s" and explicit node name retrieval. - Remove #include <dt-cpu-topology.h> from cpu-topology.h, and ensure the header directly includes only what its definitions require. - Remove #include <xen/device_tree.h> from dt-cpu-topology.h, replacing it with a forward declaration of 'struct dt_device_node'. - Use 'const' qualifiers for pointer declarations where the pointed-to structure is not modified. Changes in v3: - Use (nr_cpu_ids - 1) as the maximum CPU ID here. The fix for the sparse map mismatch issue on ARM Xen has been split out into a separate patch. - Switch topology sibling masks to cpumask_var_t for dynamic allocation. - Allow the system to keep running with a degraded fallback even if the topology table allocation fails. - Clean up the code to follow the Xen coding style. Please let me know if I missed any style nits! - Verify successful builds across x86, RISC-V, and PPC environments. Changes in v2: - Generate topology information even when ACPI is enabled. Note that this is a temporary implementation and doesn't yet parse the PPTT (Processor Properties Topology Table). - Added support for cpu-map node in Device Tree that doesn't contain explicit cluster node definitions. xen/arch/arm/Kconfig | 1 + xen/arch/arm/smpboot.c | 7 + xen/common/Kconfig | 18 ++ xen/common/Makefile | 1 + xen/common/cpu-topology.c | 62 +++++ xen/common/device-tree/Makefile | 1 + xen/common/device-tree/cpu-topology.c | 355 ++++++++++++++++++++++++++ xen/drivers/acpi/Kconfig | 3 + xen/drivers/acpi/Makefile | 1 + xen/drivers/acpi/topology.c | 38 +++ xen/include/xen/acpi.h | 2 + xen/include/xen/cpu-topology.h | 35 +++ xen/include/xen/dt-cpu-topology.h | 29 +++ 13 files changed, 553 insertions(+) create mode 100644 xen/common/cpu-topology.c create mode 100644 xen/common/device-tree/cpu-topology.c create mode 100644 xen/drivers/acpi/topology.c create mode 100644 xen/include/xen/cpu-topology.h create mode 100644 xen/include/xen/dt-cpu-topology.h diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config ARM select HAS_GRANT_CACHE_FLUSH if GRANT_TABLE select HAS_STACK_PROTECTOR select HAS_UBSAN + select HAS_GENERIC_CPU_TOPOLOGY config ARCH_DEFCONFIG string diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -XXX,XX +XXX,XX @@ #include <xen/acpi.h> #include <xen/cpu.h> +#include <xen/cpu-topology.h> #include <xen/cpumask.h> #include <xen/delay.h> #include <xen/device_tree.h> #include <xen/domain_page.h> +#include <xen/dt-cpu-topology.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/mm.h> @@ -XXX,XX +XXX,XX @@ static void __init dt_smp_init_cpus(void) } else tmp_map[i] = hwid; + + /* Pass the info to dt_init_cpu_topology() */ + map_cpu_to_dt_node(i, cpu); } if ( !bootcpu_valid ) @@ -XXX,XX +XXX,XX @@ void __init smp_init_cpus(void) else acpi_smp_init_cpus(); + init_cpu_topology(); + if ( opt_hmp_unsafe ) warning_add("WARNING: HMP COMPUTING HAS BEEN ENABLED.\n" "It has implications on the security and stability of the system,\n" diff --git a/xen/common/Kconfig b/xen/common/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Kconfig +++ b/xen/common/Kconfig @@ -XXX,XX +XXX,XX @@ config VM_EVENT config NEEDS_LIBELF bool +config HAS_GENERIC_CPU_TOPOLOGY + bool + +config DT_CPU_TOPOLOGY + bool + +config GENERIC_CPU_TOPOLOGY + bool "CPU topology support (UNSUPPORTED)" if UNSUPPORTED + depends on HAS_GENERIC_CPU_TOPOLOGY + select DT_CPU_TOPOLOGY if DEVICE_TREE_PARSE + select ACPI_CPU_TOPOLOGY if ACPI + help + Retrieve CPU topology information from the device tree to optimize + virtual CPU scheduling. + + Note: Implementation for parsing CPU topology from the ACPI PPTT + is currently missing. + config NUMA bool diff --git a/xen/common/Makefile b/xen/common/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_GENERIC_BUG_FRAME) += bug.o obj-$(CONFIG_HYPFS_CONFIG) += config_data.o obj-$(CONFIG_CORE_PARKING) += core_parking.o obj-y += cpu.o +obj-$(CONFIG_GENERIC_CPU_TOPOLOGY) += cpu-topology.o obj-$(CONFIG_DEBUG_TRACE) += debugtrace.o obj-$(CONFIG_HAS_DEVICE_TREE_DISCOVERY) += device.o obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o diff --git a/xen/common/cpu-topology.c b/xen/common/cpu-topology.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/common/cpu-topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/dt-cpu-topology.h> +#include <xen/init.h> + +struct cpu_topology *__ro_after_init cpu_topology; + +static void __init free_topology_table(void) +{ + unsigned int cpu; + + for ( cpu = 0; cpu < nr_cpu_ids; cpu++ ) + { + free_cpumask_var(cpu_topology[cpu].thread_sibling); + free_cpumask_var(cpu_topology[cpu].core_sibling); + free_cpumask_var(cpu_topology[cpu].cluster_sibling); + } + + XFREE(cpu_topology); +} + +void __init init_cpu_topology(void) +{ + unsigned int cpu; + + cpu_topology = xzalloc_array(struct cpu_topology, nr_cpu_ids); + if ( !cpu_topology ) + { + printk(XENLOG_ERR "Failed to allocate memory for cpu_topology table\n"); + return; + } + + for ( cpu = 0; cpu < nr_cpu_ids; cpu++ ) + { + if ( !zalloc_cpumask_var(&cpu_topology[cpu].thread_sibling) || + !zalloc_cpumask_var(&cpu_topology[cpu].core_sibling) || + !zalloc_cpumask_var(&cpu_topology[cpu].cluster_sibling) ) + { + free_topology_table(); + printk(XENLOG_ERR "Failed to allocate memory for cpu_topology table\n"); + return; + } + } + + if ( acpi_disabled ) + dt_init_cpu_topology(); + else + acpi_init_cpu_topology(); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/device-tree/Makefile +++ b/xen/common/device-tree/Makefile @@ -XXX,XX +XXX,XX @@ 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.init.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 XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/common/device-tree/cpu-topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Derived from Linux kernel 7.0's $drivers/base/arch_topology.c + * Parse cpu topology information. + */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/device_tree.h> +#include <xen/errno.h> +#include <xen/init.h> +#include <xen/numa.h> + +struct cpu_map { + unsigned int thread_id; + unsigned int core_id; + unsigned int cluster_id; + unsigned int package_id; +}; + +static const unsigned int __initdata invalid_topo_id = ~0U; +static struct cpu_map __initdata cpu_map[NR_CPUS] = { + [0 ... NR_CPUS - 1] = {invalid_topo_id, invalid_topo_id, invalid_topo_id, 0} +}; +static struct dt_device_node *__initdata dt_cpu_table[NR_CPUS]; + +static void __init setup_siblings_masks(unsigned int target_cpu) +{ + const struct cpu_topology *target_topo = &cpu_topology[target_cpu]; + const struct cpu_map *target_map = &cpu_map[target_cpu]; + unsigned int cpu; + + /* Update core and thread sibling masks */ + for_each_possible_cpu(cpu) + { + const struct cpu_topology *cpu_topo = &cpu_topology[cpu]; + const struct cpu_map *map = &cpu_map[cpu]; + + if ( target_map->package_id != map->package_id ) + continue; + + cpumask_set_cpu(target_cpu, cpu_topo->core_sibling); + cpumask_set_cpu(cpu, target_topo->core_sibling); + + if ( target_map->cluster_id != map->cluster_id ) + continue; + + if ( target_map->cluster_id != invalid_topo_id ) + { + cpumask_set_cpu(target_cpu, cpu_topo->cluster_sibling); + cpumask_set_cpu(cpu, target_topo->cluster_sibling); + } + + if ( target_map->core_id != map->core_id ) + continue; + + cpumask_set_cpu(target_cpu, cpu_topo->thread_sibling); + cpumask_set_cpu(cpu, target_topo->thread_sibling); + } +} + +static struct dt_device_node *__init + dt_find_child_node_by_name(const struct dt_device_node *dt, + const char *name) +{ + struct dt_device_node *np; + + dt_for_each_child_node(dt, np) + if ( np->name && (dt_node_cmp(np->name, name) == 0) ) + break; + + return np; +} + +void __init map_cpu_to_dt_node(unsigned int cpu, + struct dt_device_node *cpu_node) +{ + if ( cpu < ARRAY_SIZE(dt_cpu_table) ) + dt_cpu_table[cpu] = cpu_node; + else + printk(XENLOG_WARNING + "cpu id %u exceeds the max cores %lu\n", + cpu, ARRAY_SIZE(dt_cpu_table)); +} + +static unsigned int __init cpu_node_to_id(const struct dt_device_node *cpu_node) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + if ( cpu_node == dt_cpu_table[cpu] ) + return cpu; + + return invalid_topo_id; +} + +/* + * This function returns the logical cpu number of the DT node. + */ +static unsigned int __init + get_cpu_for_node(const struct dt_device_node *dt_node) +{ + const struct dt_device_node *cpu_node = dt_parse_phandle(dt_node, "cpu", 0); + + if ( !cpu_node ) + return invalid_topo_id; + + return cpu_node_to_id(cpu_node); +} + +static int __init parse_core(const struct dt_device_node *core, + unsigned int package_id, + unsigned int cluster_id, + unsigned int core_id) +{ + bool leaf = true; + unsigned int i = 0; + unsigned int cpu; + + do { + const struct dt_device_node *t; + char name[20]; + + 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: %s: Can't get CPU for thread\n", dt_node_name(t)); + return -EINVAL; + } + i++; + } while ( true ); + + cpu = get_cpu_for_node(core); + + if ( cpu != invalid_topo_id ) + { + if ( !leaf ) + { + printk(XENLOG_ERR "ERROR: %s: Core has both threads and CPU\n", + dt_node_name(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 = 0; + } + else if ( leaf ) + { + printk(XENLOG_ERR + "ERROR: %s: Can't get CPU for leaf core\n", dt_node_name(core)); + return -EINVAL; + } + + return 0; +} + +static int __init parse_cluster(const struct dt_device_node *cluster, + unsigned int package_id, + unsigned int cluster_id, + unsigned int depth) +{ + bool leaf = true; + bool has_cores = false; + unsigned int core_id = 0; + unsigned int i = 0; + + /* + * 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. + */ + do { + const struct dt_device_node *c; + char name[20]; + int ret; + + 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 + 1); + if ( depth > 0 ) + 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 = 0; + do { + const struct dt_device_node *c; + char name[20]; + int ret; + + snprintf(name, sizeof(name), "core%u", i); + c = dt_find_child_node_by_name(cluster, name); + + if ( !c ) + break; + + has_cores = true; + + if ( depth == 0 ) + { + printk(XENLOG_ERR + "ERROR: %s: cpu-map children should be clusters\n", + dt_node_name(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: %s: Non-leaf cluster with core %s\n", + dt_node_name(cluster), name); + return -EINVAL; + } + + i++; + } while ( true ); + + if ( leaf && !has_cores ) + printk(XENLOG_WARNING "WARNING: %s: empty cluster\n", + dt_node_name(cluster)); + + return 0; +} + +static int __init parse_socket(const struct dt_device_node *socket) +{ + bool has_socket = false; + unsigned int package_id = 0; + int ret; + + do { + const struct dt_device_node *c; + char name[20]; + + 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, 0); + if ( ret != 0 ) + return ret; + + package_id++; + } while ( true ); + + if ( !has_socket ) + ret = parse_cluster(socket, 0, invalid_topo_id, 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) +{ + unsigned int cpu; + unsigned int clid = 0; + unsigned int pkgid = 0; + + 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 = 0; + } + map->cluster_id = clid++; + map->core_id = 0; + map->thread_id = 0; + } +} + +int __init parse_dt_topology(void) +{ + const struct dt_device_node *cpus; + const 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; + + BUG_ON(!acpi_disabled); + BUG_ON(!cpu_topology); + + if ( parse_dt_topology() ) + fixup_topology(); + + for_each_possible_cpu(cpu) + setup_siblings_masks(cpu); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/drivers/acpi/Kconfig b/xen/drivers/acpi/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/Kconfig +++ b/xen/drivers/acpi/Kconfig @@ -XXX,XX +XXX,XX @@ config ACPI_LEGACY_TABLES_LOOKUP config ACPI_NUMA bool select NUMA + +config ACPI_CPU_TOPOLOGY + bool diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/Makefile +++ b/xen/drivers/acpi/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_PM_OP) += pm-op.o obj-$(CONFIG_X86) += hwregs.o obj-$(CONFIG_X86) += reboot.o +obj-$(CONFIG_ACPI_CPU_TOPOLOGY) += topology.init.o diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/drivers/acpi/topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/init.h> + +/* + * TODO: Populate the topology information by scanning the ACPI + * PPTT (Processor Properties Topology Table). + */ +void __init acpi_init_cpu_topology(void) +{ + unsigned int cpu; + + /* + * Generate temporary cpu topology information for now. + * It assumes that the cpu doesn't have SMT and all CPUs + * belong to the same socket. + */ + for_each_possible_cpu(cpu) + { + struct cpu_topology *topo = &cpu_topology[cpu]; + + cpumask_set_cpu(cpu, topo->thread_sibling); + cpumask_copy(topo->core_sibling, &cpu_possible_map); + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -XXX,XX +XXX,XX @@ void acpi_table_print (struct acpi_table_header *header, unsigned long phys_addr void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); void acpi_table_print_srat_entry (struct acpi_subtable_header *srat); +void acpi_init_cpu_topology(void); + /* the following four functions are architecture-dependent */ void acpi_numa_slit_init (struct acpi_table_slit *slit); void acpi_numa_processor_affinity_init(const struct acpi_srat_cpu_affinity *); diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef XEN_CPU_TOPOLOGY_H +#define XEN_CPU_TOPOLOGY_H + +#include <xen/cpumask.h> + +#ifdef CONFIG_GENERIC_CPU_TOPOLOGY + +struct cpu_topology { + cpumask_var_t thread_sibling; + cpumask_var_t core_sibling; + cpumask_var_t cluster_sibling; +}; + +extern struct cpu_topology *cpu_topology; +void init_cpu_topology(void); + +#else /* CONFIG_GENERIC_CPU_TOPOLOGY */ + +#define cpu_topology ((struct cpu_topology *)NULL) +static inline void init_cpu_topology(void) {} + +#endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ + +#endif /* XEN_CPU_TOPOLOGY_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/dt-cpu-topology.h b/xen/include/xen/dt-cpu-topology.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/include/xen/dt-cpu-topology.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef XEN_DT_CPU_TOPOLOGY_H +#define XEN_DT_CPU_TOPOLOGY_H + +struct dt_device_node; + +#ifdef CONFIG_DT_CPU_TOPOLOGY + +void map_cpu_to_dt_node(unsigned int cpu, struct dt_device_node *cpu_node); +void dt_init_cpu_topology(void); + +#else /* CONFIG_DT_CPU_TOPOLOGY */ + +static inline void map_cpu_to_dt_node(unsigned int cpu, + struct dt_device_node *cpu_node) {} + +#endif /* CONFIG_DT_CPU_TOPOLOGY */ + +#endif /* XEN_DT_CPU_TOPOLOGY_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.43.0
Make CPU topology information available to the Xen scheduler. Additionally, ensure that this topology information is displayed when executing the 'xl info -n' command. Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp> --- Changes in v4 - Explicitly #include <asm/processor.h> in cpu-topology.h to guarantee that arch-specific definitions of cpu_to_core() and cpu_to_socket() take precedence over the generic fallbacks. - Introduce inline initialization functions for cpu_sibling_mask and cpu_core_mask in cpu-topology.h, providing separate variants for both when CONFIG_GENERIC_CPU_TOPOLOGY is enabled and disabled. Changes in v3 - Remove the temporary definitions of cpu_to_core() and cpu_to_socket() from RISC-V and PPC processor.h. - Minimize the use of #ifdef blocks, leveraging compiler Dead Code Elimination (DCE) where possible. xen/arch/arm/include/asm/processor.h | 4 -- xen/arch/arm/smpboot.c | 8 +--- xen/arch/ppc/include/asm/processor.h | 4 -- xen/arch/riscv/include/asm/processor.h | 4 -- xen/common/device-tree/cpu-topology.c | 51 ++++++++++++++++++++++++++ xen/common/sched/credit2.c | 3 ++ xen/common/sysctl.c | 1 + xen/drivers/acpi/topology.c | 3 ++ xen/include/xen/cpu-topology.h | 45 ++++++++++++++++++++++- 9 files changed, 103 insertions(+), 20 deletions(-) diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/processor.h +++ b/xen/arch/arm/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ void show_stack(const struct cpu_user_regs *regs); #define cpu_relax() barrier() /* Could yield? */ -/* All a bit UP for the moment */ -#define cpu_to_core(_cpu) (0) -#define cpu_to_socket(_cpu) (0) - struct vcpu; void vcpu_regs_hyp_to_user(const struct vcpu *vcpu, struct vcpu_guest_core_regs *regs); diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -XXX,XX +XXX,XX @@ static int setup_cpu_sibling_map(int cpu) !zalloc_cpumask_var(&per_cpu(cpu_core_mask, cpu)) ) return -ENOMEM; - /* - * Currently we assume there is no multithread and NUMA, so - * a CPU is a sibling with itself, and the all possible CPUs - * are supposed to belong to the same socket (NUMA node). - */ - cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); - cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); + init_cpu_sibling_map(cpu); return 0; } diff --git a/xen/arch/ppc/include/asm/processor.h b/xen/arch/ppc/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/ppc/include/asm/processor.h +++ b/xen/arch/ppc/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ /* Macro to adjust thread priority for hardware multithreading */ #define HMT_very_low() asm volatile ( "or %r31, %r31, %r31" ) -/* TODO: This isn't correct */ -#define cpu_to_core(cpu) (0) -#define cpu_to_socket(cpu) (0) - /* * User-accessible registers: most of these need to be saved/restored * for every nested Xen invocation. diff --git a/xen/arch/riscv/include/asm/processor.h b/xen/arch/riscv/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/riscv/include/asm/processor.h +++ b/xen/arch/riscv/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ struct cpu_user_regs unsigned long pregs; }; -/* TODO: need to implement */ -#define cpu_to_core(cpu) 0 -#define cpu_to_socket(cpu) 0 - static inline void cpu_relax(void) { #ifdef __riscv_zihintpause diff --git a/xen/common/device-tree/cpu-topology.c b/xen/common/device-tree/cpu-topology.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/device-tree/cpu-topology.c +++ b/xen/common/device-tree/cpu-topology.c @@ -XXX,XX +XXX,XX @@ int __init parse_dt_topology(void) return parse_socket(map); } +static void __init setup_cpu_topology_ids(void) +{ + unsigned int cpu; + unsigned int next_core_id = 0; + unsigned int next_cluster_id = 0; + unsigned int next_socket_id = 0; + + for_each_possible_cpu(cpu) + { + unsigned int first_cpu; + struct cpu_topology *topo = &cpu_topology[cpu]; + + first_cpu = cpumask_first(topo->thread_sibling); + if ( first_cpu == cpu ) + { + topo->phys_core_id = next_core_id; + next_core_id++; + } + else + topo->phys_core_id = cpu_topology[first_cpu].phys_core_id; + + /* Reuse the calculated core id if clustering is not supported */ + if ( cpumask_empty(topo->cluster_sibling) ) + topo->phys_cluster_id = topo->phys_core_id; + else + { + first_cpu = cpumask_first(topo->cluster_sibling); + if ( first_cpu == cpu ) + { + topo->phys_cluster_id = next_cluster_id; + next_cluster_id++; + } + else + topo->phys_cluster_id = cpu_topology[first_cpu].phys_cluster_id; + } + + first_cpu = cpumask_first(topo->core_sibling); + if ( first_cpu == cpu ) + { + topo->phys_socket_id = next_socket_id; + next_socket_id++; + } + else + topo->phys_socket_id = cpu_topology[first_cpu].phys_socket_id; + + topo->num_siblings = cpumask_weight(topo->thread_sibling); + } +} + void __init dt_init_cpu_topology(void) { unsigned int cpu; @@ -XXX,XX +XXX,XX @@ void __init dt_init_cpu_topology(void) for_each_possible_cpu(cpu) setup_siblings_masks(cpu); + + setup_cpu_topology_ids(); } /* diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sched/credit2.c +++ b/xen/common/sched/credit2.c @@ -XXX,XX +XXX,XX @@ * Based on an earlier verson by Emmanuel Ackaouy. */ +#include <xen/cpu-topology.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/lib.h> @@ -XXX,XX +XXX,XX @@ static unsigned int cpu_nr_siblings(unsigned int cpu) { #ifdef CONFIG_X86 return cpu_data[cpu].x86_num_siblings; +#elif defined(CONFIG_CPU_TOPOLOGY) + return cpu_topology ? cpu_topology[cpu].num_siblings : 1; #else return 1; #endif diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -XXX,XX +XXX,XX @@ #include <xen/pmstat.h> #include <xen/livepatch.h> #include <xen/coverage.h> +#include <xen/cpu-topology.h> long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) { diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/topology.c +++ b/xen/drivers/acpi/topology.c @@ -XXX,XX +XXX,XX @@ void __init acpi_init_cpu_topology(void) { struct cpu_topology *topo = &cpu_topology[cpu]; + topo->phys_core_id = cpu; + topo->num_siblings = 1; + cpumask_set_cpu(cpu, topo->thread_sibling); cpumask_copy(topo->core_sibling, &cpu_possible_map); } diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/cpu-topology.h +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ #define XEN_CPU_TOPOLOGY_H #include <xen/cpumask.h> +#include <xen/percpu.h> +#include <asm/processor.h> +#include <asm/smp.h> #ifdef CONFIG_GENERIC_CPU_TOPOLOGY @@ -XXX,XX +XXX,XX @@ struct cpu_topology { cpumask_var_t thread_sibling; cpumask_var_t core_sibling; cpumask_var_t cluster_sibling; + unsigned int phys_core_id; + unsigned int phys_cluster_id; + unsigned int phys_socket_id; + unsigned int num_siblings; }; extern struct cpu_topology *cpu_topology; void init_cpu_topology(void); +static inline void init_cpu_sibling_map(unsigned int cpu) +{ + if ( cpu_topology ) + { + cpumask_copy(per_cpu(cpu_sibling_mask, cpu), + cpu_topology[cpu].thread_sibling); + cpumask_copy(per_cpu(cpu_core_mask, cpu), + cpu_topology[cpu].core_sibling); + } + else + { + cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); + cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); + } +} + +#define cpu_to_core(cpu) (cpu_topology ? cpu_topology[cpu].phys_core_id : 0) +#define cpu_to_socket(cpu) (cpu_topology ? cpu_topology[cpu].phys_socket_id : 0) + #else /* CONFIG_GENERIC_CPU_TOPOLOGY */ -#define cpu_topology ((struct cpu_topology *)NULL) static inline void init_cpu_topology(void) {} +static inline void init_cpu_sibling_map(unsigned int cpu) +{ + /* + * If CONFIG_GENERIC_CPU_TOPOLOGY is disabled, it is assumed that + * all CPUs reside in the same socket and that SMT is not used. + */ + cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); + cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); +} + +#ifndef cpu_to_core +#define cpu_to_core(cpu) (0) +#endif + +#ifndef cpu_to_socket +#define cpu_to_socket(cpu) (0) +#endif + #endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ #endif /* XEN_CPU_TOPOLOGY_H */ -- 2.43.0
Make cpu_nr_siblings() an architecture-specific function. This patch provides the implementation for x86 and a common version for Device Tree-based architectures. Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp> Acked-by: Jan Beulich <jbeulich@suse.com> --- xen/arch/x86/include/asm/processor.h | 1 + xen/common/sched/credit2.c | 22 +++------------------- xen/include/xen/cpu-topology.h | 5 +++++ 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/x86/include/asm/processor.h +++ b/xen/arch/x86/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ extern void intel_init_arat(void); #define cpu_to_core(_cpu) (cpu_data[_cpu].cpu_core_id) #define cpu_to_socket(_cpu) (cpu_data[_cpu].phys_proc_id) +#define cpu_nr_siblings(_cpu) (cpu_data[_cpu].x86_num_siblings) unsigned int apicid_to_socket(unsigned int apicid); diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sched/credit2.c +++ b/xen/common/sched/credit2.c @@ -XXX,XX +XXX,XX @@ /* #define d2printk printk */ #define d2printk(x...) -/* - * TODO: Abstract this properly, and figure out what Credit2 wants to do with - * the fact that x86_num_siblings doesn't even have the same meaning - * between x86 vendors. - */ -static unsigned int cpu_nr_siblings(unsigned int cpu) -{ -#ifdef CONFIG_X86 - return cpu_data[cpu].x86_num_siblings; -#elif defined(CONFIG_CPU_TOPOLOGY) - return cpu_topology ? cpu_topology[cpu].num_siblings : 1; -#else - return 1; -#endif -} - /* * Credit2 tracing events ("only" 512 available!). Check * include/public/trace.h for more details. @@ -XXX,XX +XXX,XX @@ cpu_runqueue_match(const struct csched2_runqueue_data *rqd, unsigned int cpu) /* * Additional checks, to avoid separating siblings in different runqueues. - * This deals with both Intel's HTs and AMD's CUs. An arch that does not have - * any similar concept will just have cpu_nr_siblings() always return 1, and - * setup the cpu_sibling_mask-s acordingly (as currently does ARM), and things + * This deals with Intel's HTs, AMD's CUs and ARM's SMT. An arch that + * does not have similar concept will just have cpu_nr_siblings() always + * return 1, and setup the cpu_sibling_mask-s accordingly, and things * will just work as well. */ static bool diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/cpu-topology.h +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ static inline void init_cpu_sibling_map(unsigned int cpu) #define cpu_to_core(cpu) (cpu_topology ? cpu_topology[cpu].phys_core_id : 0) #define cpu_to_socket(cpu) (cpu_topology ? cpu_topology[cpu].phys_socket_id : 0) +#define cpu_nr_siblings(cpu) (cpu_topology ? cpu_topology[cpu].num_siblings : 1) #else /* CONFIG_GENERIC_CPU_TOPOLOGY */ @@ -XXX,XX +XXX,XX @@ static inline void init_cpu_sibling_map(unsigned int cpu) #define cpu_to_socket(cpu) (0) #endif +#ifndef cpu_nr_siblings +#define cpu_nr_siblings(cpu) (1) +#endif + #endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ #endif /* XEN_CPU_TOPOLOGY_H */ -- 2.43.0
Hello, This patch series introduces CPU topology support based on Device-Tree and ACPI PPTT for ARM Xen. In v5, I added code to parse the ACPI PPTT and reflect it in the CPU topology information. Future Work: - Support for the "credit2_runqueue=cluster" option. - CPU topology support for RISC-V and PPC (once SMP support is available). Changes in v5: - Extracted CPU topology information from the ACPI PPTT. - Corrected the erroneous use of CONFIG_CPU_TOPOLOGY to CONFIG_GENERIC_CPU_TOPOLOGY. Changes in v4: - Only display the CPU topology configuration prompt in common/Kconfig if the architecture defines HAS_GENERIC_CPU_TOPOLOGY. - Move the definition of the global 'cpu_topology' pointer to common/cpu-topology.c. - Update the Makefile to explicitly build objects as .init.o when all functions and data within a file are annotated with __init/__initdata, ensuring their memory is reclaimed after system initialization. - Add an error log in the CPU-to-node mapping function for out-of-bounds cases. - Use ARRAY_SIZE() instead of raw macros when guarding array accesses. - Rename variables and functions to avoid ambiguous or misleading terms: - Avoid 'cpuid' to prevent confusion with x86 CPUID features/instructions. - Avoid 'node' where it could be confused with a NUMA node, explicitly renaming them to clarify they refer to a Device Tree node. - Move local variable declarations into the narrowest possible scope. - Replace the unsupported "%pOF" printk format specifier with "%s" and explicit node name retrieval. - Remove #include <dt-cpu-topology.h> from cpu-topology.h, and ensure the header directly includes only what its definitions require. - Remove #include <xen/device_tree.h> from dt-cpu-topology.h, replacing it with a forward declaration of 'struct dt_device_node'. - Use 'const' qualifiers for pointer declarations where the pointed-to structure is not modified. - Explicitly #include <asm/processor.h> in cpu-topology.h to guarantee that arch-specific definitions of cpu_to_core() and cpu_to_socket() take precedence over the generic fallbacks. - Introduce inline initialization functions for cpu_sibling_mask and cpu_core_mask in cpu-topology.h, providing separate variants for both when CONFIG_GENERIC_CPU_TOPOLOGY is enabled and disabled. Changes in v3: - Use (nr_cpu_ids - 1) as the maximum CPU ID here. The fix for the sparse map mismatch issue on ARM Xen has been split out into a separate patch. - Switch topology sibling masks to cpumask_var_t for dynamic allocation. - Allow the system to keep running with a degraded fallback even if the topology table allocation fails. - Remove the temporary definitions of cpu_to_core() and cpu_to_socket() from RISC-V and PPC processor.h. - Minimize the use of #ifdef blocks, leveraging compiler Dead Code Elimination (DCE) where possible. - Clean up the code to follow the Xen coding style. Please let me know if I missed any style nits! - Verify successful builds across x86, RISC-V, and PPC environments. Changes in v2: - Generate topology information even when ACPI is enabled. Note that this is a temporary implementation and doesn't yet parse the PPTT (Processor Properties Topology Table). - Added support for cpu-map node in Device Tree that doesn't contain explicit cluster node definitions. Changes in v1 from the previous series "Introduce Device Tree based NUMA support for ARM Xen": 1. Optimized Memory Allocation: The series now allocates only the minimum required memory area to manage the essential data for the CPUs. 2. Flexible Device Tree Parsing: The parsing logic no longer depends on the definition order of the 'cpu' nodes and 'cpu-map' nodes in the Device Tree. They can now be read correctly even if their orders do not match. 3. CPU Hotplug Readiness: To support future CPU hotplug, the system assumes that inactive CPUs are also described in the Device Tree. Xen will pre-load and generate the topology information for these inactive CPUs during the boot phase so it stays available in memory. Thank you, Hirokazu Takahashi Hirokazu Takahashi (4): xen/device-tree: Parse 'cpu-map' node for CPU topology exploration xen/sched: Link CPU topology to scheduler xen/sched: Make cpu_nr_siblings() architecture-specific arm/acpi: Parse PPTT to initialize CPU topology xen/arch/arm/Kconfig | 1 + xen/arch/arm/acpi/boot.c | 2 + xen/arch/arm/include/asm/acpi.h | 2 + xen/arch/arm/include/asm/processor.h | 4 - xen/arch/arm/smpboot.c | 15 +- xen/arch/ppc/include/asm/processor.h | 4 - xen/arch/riscv/include/asm/processor.h | 4 - xen/arch/x86/include/asm/processor.h | 1 + xen/common/Kconfig | 15 + xen/common/Makefile | 1 + xen/common/cpu-topology.c | 62 ++++ xen/common/device-tree/Makefile | 1 + xen/common/device-tree/cpu-topology.c | 406 +++++++++++++++++++++++++ xen/common/sched/credit2.c | 21 +- xen/common/sysctl.c | 1 + xen/drivers/acpi/Kconfig | 3 + xen/drivers/acpi/Makefile | 1 + xen/drivers/acpi/topology.c | 255 ++++++++++++++++ xen/include/acpi/actbl3.h | 30 ++ xen/include/xen/acpi.h | 18 ++ xen/include/xen/cpu-topology.h | 83 +++++ xen/include/xen/dt-cpu-topology.h | 29 ++ 22 files changed, 923 insertions(+), 36 deletions(-) create mode 100644 xen/common/cpu-topology.c create mode 100644 xen/common/device-tree/cpu-topology.c create mode 100644 xen/drivers/acpi/topology.c create mode 100644 xen/include/xen/cpu-topology.h create mode 100644 xen/include/xen/dt-cpu-topology.h -- 2.43.0
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@valinux.co.jp> --- xen/arch/arm/Kconfig | 1 + xen/arch/arm/smpboot.c | 7 + xen/common/Kconfig | 15 ++ xen/common/Makefile | 1 + xen/common/cpu-topology.c | 62 +++++ xen/common/device-tree/Makefile | 1 + xen/common/device-tree/cpu-topology.c | 355 ++++++++++++++++++++++++++ xen/drivers/acpi/Kconfig | 3 + xen/drivers/acpi/Makefile | 1 + xen/drivers/acpi/topology.c | 38 +++ xen/include/xen/acpi.h | 10 + xen/include/xen/cpu-topology.h | 35 +++ xen/include/xen/dt-cpu-topology.h | 29 +++ 13 files changed, 558 insertions(+) create mode 100644 xen/common/cpu-topology.c create mode 100644 xen/common/device-tree/cpu-topology.c create mode 100644 xen/drivers/acpi/topology.c create mode 100644 xen/include/xen/cpu-topology.h create mode 100644 xen/include/xen/dt-cpu-topology.h diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config ARM select HAS_STACK_PROTECTOR select HAS_STATIC_MEMORY select HAS_UBSAN + select HAS_GENERIC_CPU_TOPOLOGY config ARCH_DEFCONFIG string diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -XXX,XX +XXX,XX @@ #include <xen/acpi.h> #include <xen/cpu.h> +#include <xen/cpu-topology.h> #include <xen/cpumask.h> #include <xen/delay.h> #include <xen/device_tree.h> #include <xen/domain_page.h> +#include <xen/dt-cpu-topology.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/mm.h> @@ -XXX,XX +XXX,XX @@ static void __init dt_smp_init_cpus(void) } else tmp_map[i] = hwid; + + /* Pass the info to dt_init_cpu_topology() */ + map_cpu_to_dt_node(i, cpu); } if ( !bootcpu_valid ) @@ -XXX,XX +XXX,XX @@ void __init smp_init_cpus(void) else acpi_smp_init_cpus(); + init_cpu_topology(); + if ( opt_hmp_unsafe ) warning_add("WARNING: HMP COMPUTING HAS BEEN ENABLED.\n" "It has implications on the security and stability of the system,\n" diff --git a/xen/common/Kconfig b/xen/common/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Kconfig +++ b/xen/common/Kconfig @@ -XXX,XX +XXX,XX @@ config VM_EVENT config NEEDS_LIBELF bool +config HAS_GENERIC_CPU_TOPOLOGY + bool + +config DT_CPU_TOPOLOGY + bool + +config GENERIC_CPU_TOPOLOGY + bool "CPU topology support (UNSUPPORTED)" if UNSUPPORTED + depends on HAS_GENERIC_CPU_TOPOLOGY + select DT_CPU_TOPOLOGY if DEVICE_TREE_PARSE + select ACPI_CPU_TOPOLOGY if ACPI + help + Retrieve CPU topology information from the device tree or the + ACPI PPTT to optimize virtual CPU scheduling. + config NUMA bool diff --git a/xen/common/Makefile b/xen/common/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_GENERIC_BUG_FRAME) += bug.o obj-$(CONFIG_HYPFS_CONFIG) += config_data.o obj-$(CONFIG_CORE_PARKING) += core_parking.o obj-y += cpu.o +obj-$(CONFIG_GENERIC_CPU_TOPOLOGY) += cpu-topology.o obj-$(CONFIG_DEBUG_TRACE) += debugtrace.o obj-$(CONFIG_HAS_DEVICE_TREE_DISCOVERY) += device.o obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o diff --git a/xen/common/cpu-topology.c b/xen/common/cpu-topology.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/common/cpu-topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/dt-cpu-topology.h> +#include <xen/init.h> + +struct cpu_topology *__ro_after_init cpu_topology; + +static void __init free_topology_table(void) +{ + unsigned int cpu; + + for ( cpu = 0; cpu < nr_cpu_ids; cpu++ ) + { + free_cpumask_var(cpu_topology[cpu].thread_sibling); + free_cpumask_var(cpu_topology[cpu].core_sibling); + free_cpumask_var(cpu_topology[cpu].cluster_sibling); + } + + XFREE(cpu_topology); +} + +void __init init_cpu_topology(void) +{ + unsigned int cpu; + + cpu_topology = xzalloc_array(struct cpu_topology, nr_cpu_ids); + if ( !cpu_topology ) + { + printk(XENLOG_ERR "Failed to allocate memory for cpu_topology table\n"); + return; + } + + for ( cpu = 0; cpu < nr_cpu_ids; cpu++ ) + { + if ( !zalloc_cpumask_var(&cpu_topology[cpu].thread_sibling) || + !zalloc_cpumask_var(&cpu_topology[cpu].core_sibling) || + !zalloc_cpumask_var(&cpu_topology[cpu].cluster_sibling) ) + { + free_topology_table(); + printk(XENLOG_ERR "Failed to allocate memory for cpu_topology table\n"); + return; + } + } + + if ( acpi_disabled ) + dt_init_cpu_topology(); + else + acpi_init_cpu_topology(); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/common/device-tree/Makefile +++ b/xen/common/device-tree/Makefile @@ -XXX,XX +XXX,XX @@ 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.init.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 XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/common/device-tree/cpu-topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Derived from Linux kernel 7.0's $drivers/base/arch_topology.c + * Parse cpu topology information. + */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/device_tree.h> +#include <xen/errno.h> +#include <xen/init.h> +#include <xen/numa.h> + +struct cpu_map { + unsigned int thread_id; + unsigned int core_id; + unsigned int cluster_id; + unsigned int package_id; +}; + +static const unsigned int __initdata invalid_topo_id = ~0U; +static struct cpu_map __initdata cpu_map[NR_CPUS] = { + [0 ... NR_CPUS - 1] = {invalid_topo_id, invalid_topo_id, invalid_topo_id, 0} +}; +static struct dt_device_node *__initdata dt_cpu_table[NR_CPUS]; + +static void __init setup_siblings_masks(unsigned int target_cpu) +{ + const struct cpu_topology *target_topo = &cpu_topology[target_cpu]; + const struct cpu_map *target_map = &cpu_map[target_cpu]; + unsigned int cpu; + + /* Update core and thread sibling masks */ + for_each_possible_cpu(cpu) + { + const struct cpu_topology *cpu_topo = &cpu_topology[cpu]; + const struct cpu_map *map = &cpu_map[cpu]; + + if ( target_map->package_id != map->package_id ) + continue; + + cpumask_set_cpu(target_cpu, cpu_topo->core_sibling); + cpumask_set_cpu(cpu, target_topo->core_sibling); + + if ( target_map->cluster_id != map->cluster_id ) + continue; + + if ( target_map->cluster_id != invalid_topo_id ) + { + cpumask_set_cpu(target_cpu, cpu_topo->cluster_sibling); + cpumask_set_cpu(cpu, target_topo->cluster_sibling); + } + + if ( target_map->core_id != map->core_id ) + continue; + + cpumask_set_cpu(target_cpu, cpu_topo->thread_sibling); + cpumask_set_cpu(cpu, target_topo->thread_sibling); + } +} + +static struct dt_device_node *__init + dt_find_child_node_by_name(const struct dt_device_node *dt, + const char *name) +{ + struct dt_device_node *np; + + dt_for_each_child_node(dt, np) + if ( np->name && (dt_node_cmp(np->name, name) == 0) ) + break; + + return np; +} + +void __init map_cpu_to_dt_node(unsigned int cpu, + struct dt_device_node *cpu_node) +{ + if ( cpu < ARRAY_SIZE(dt_cpu_table) ) + dt_cpu_table[cpu] = cpu_node; + else + printk(XENLOG_WARNING + "cpu id %u exceeds the max cores %lu\n", + cpu, ARRAY_SIZE(dt_cpu_table)); +} + +static unsigned int __init cpu_node_to_id(const struct dt_device_node *cpu_node) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + if ( cpu_node == dt_cpu_table[cpu] ) + return cpu; + + return invalid_topo_id; +} + +/* + * This function returns the logical cpu number of the DT node. + */ +static unsigned int __init + get_cpu_for_node(const struct dt_device_node *dt_node) +{ + const struct dt_device_node *cpu_node = dt_parse_phandle(dt_node, "cpu", 0); + + if ( !cpu_node ) + return invalid_topo_id; + + return cpu_node_to_id(cpu_node); +} + +static int __init parse_core(const struct dt_device_node *core, + unsigned int package_id, + unsigned int cluster_id, + unsigned int core_id) +{ + bool leaf = true; + unsigned int i = 0; + unsigned int cpu; + + do { + const struct dt_device_node *t; + char name[20]; + + 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: %s: Can't get CPU for thread\n", dt_node_name(t)); + return -EINVAL; + } + i++; + } while ( true ); + + cpu = get_cpu_for_node(core); + + if ( cpu != invalid_topo_id ) + { + if ( !leaf ) + { + printk(XENLOG_ERR "ERROR: %s: Core has both threads and CPU\n", + dt_node_name(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 = 0; + } + else if ( leaf ) + { + printk(XENLOG_ERR + "ERROR: %s: Can't get CPU for leaf core\n", dt_node_name(core)); + return -EINVAL; + } + + return 0; +} + +static int __init parse_cluster(const struct dt_device_node *cluster, + unsigned int package_id, + unsigned int cluster_id, + unsigned int depth) +{ + bool leaf = true; + bool has_cores = false; + unsigned int core_id = 0; + unsigned int i = 0; + + /* + * 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. + */ + do { + const struct dt_device_node *c; + char name[20]; + int ret; + + 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 + 1); + if ( depth > 0 ) + 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 = 0; + do { + const struct dt_device_node *c; + char name[20]; + int ret; + + snprintf(name, sizeof(name), "core%u", i); + c = dt_find_child_node_by_name(cluster, name); + + if ( !c ) + break; + + has_cores = true; + + if ( depth == 0 ) + { + printk(XENLOG_ERR + "ERROR: %s: cpu-map children should be clusters\n", + dt_node_name(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: %s: Non-leaf cluster with core %s\n", + dt_node_name(cluster), name); + return -EINVAL; + } + + i++; + } while ( true ); + + if ( leaf && !has_cores ) + printk(XENLOG_WARNING "WARNING: %s: empty cluster\n", + dt_node_name(cluster)); + + return 0; +} + +static int __init parse_socket(const struct dt_device_node *socket) +{ + bool has_socket = false; + unsigned int package_id = 0; + int ret; + + do { + const struct dt_device_node *c; + char name[20]; + + 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, 0); + if ( ret != 0 ) + return ret; + + package_id++; + } while ( true ); + + if ( !has_socket ) + ret = parse_cluster(socket, 0, invalid_topo_id, 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) +{ + unsigned int cpu; + unsigned int clid = 0; + unsigned int pkgid = 0; + + 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 = 0; + } + map->cluster_id = clid++; + map->core_id = 0; + map->thread_id = 0; + } +} + +int __init parse_dt_topology(void) +{ + const struct dt_device_node *cpus; + const 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; + + BUG_ON(!acpi_disabled); + BUG_ON(!cpu_topology); + + if ( parse_dt_topology() ) + fixup_topology(); + + for_each_possible_cpu(cpu) + setup_siblings_masks(cpu); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/drivers/acpi/Kconfig b/xen/drivers/acpi/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/Kconfig +++ b/xen/drivers/acpi/Kconfig @@ -XXX,XX +XXX,XX @@ config ACPI_LEGACY_TABLES_LOOKUP config ACPI_NUMA bool select NUMA + +config ACPI_CPU_TOPOLOGY + bool diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/Makefile +++ b/xen/drivers/acpi/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_PM_OP) += pm-op.o obj-$(CONFIG_X86) += hwregs.o obj-$(CONFIG_X86) += reboot.o +obj-$(CONFIG_ACPI_CPU_TOPOLOGY) += topology.init.o diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/drivers/acpi/topology.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <xen/acpi.h> +#include <xen/cpu-topology.h> +#include <xen/cpumask.h> +#include <xen/init.h> + +/* + * TODO: Populate the topology information by scanning the ACPI + * PPTT (Processor Properties Topology Table). + */ +void __init acpi_init_cpu_topology(void) +{ + unsigned int cpu; + + /* + * Generate temporary cpu topology information for now. + * It assumes that the cpu doesn't have SMT and all CPUs + * belong to the same socket. + */ + for_each_possible_cpu(cpu) + { + struct cpu_topology *topo = &cpu_topology[cpu]; + + cpumask_set_cpu(cpu, topo->thread_sibling); + cpumask_copy(topo->core_sibling, &cpu_possible_map); + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -XXX,XX +XXX,XX @@ static inline int acpi_boot_table_init(void) #endif /*!CONFIG_ACPI*/ +#ifdef CONFIG_ACPI_CPU_TOPOLOGY + +void acpi_init_cpu_topology(void); + +#else /* CONFIG_ACPI_CPU_TOPOLOGY */ + +static inline void acpi_init_cpu_topology(void) {} + +#endif /* CONFIG_ACPI_CPU_TOPOLOGY */ + int get_cpu_id(u32 acpi_id); unsigned int acpi_register_gsi (u32 gsi, int edge_level, int active_high_low); diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef XEN_CPU_TOPOLOGY_H +#define XEN_CPU_TOPOLOGY_H + +#include <xen/cpumask.h> + +#ifdef CONFIG_GENERIC_CPU_TOPOLOGY + +struct cpu_topology { + cpumask_var_t thread_sibling; + cpumask_var_t core_sibling; + cpumask_var_t cluster_sibling; +}; + +extern struct cpu_topology *cpu_topology; +void init_cpu_topology(void); + +#else /* CONFIG_GENERIC_CPU_TOPOLOGY */ + +#define cpu_topology ((struct cpu_topology *)NULL) +static inline void init_cpu_topology(void) {} + +#endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ + +#endif /* XEN_CPU_TOPOLOGY_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/dt-cpu-topology.h b/xen/include/xen/dt-cpu-topology.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/include/xen/dt-cpu-topology.h @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef XEN_DT_CPU_TOPOLOGY_H +#define XEN_DT_CPU_TOPOLOGY_H + +struct dt_device_node; + +#ifdef CONFIG_DT_CPU_TOPOLOGY + +void map_cpu_to_dt_node(unsigned int cpu, struct dt_device_node *cpu_node); +void dt_init_cpu_topology(void); + +#else /* CONFIG_DT_CPU_TOPOLOGY */ + +static inline void map_cpu_to_dt_node(unsigned int cpu, + struct dt_device_node *cpu_node) {} + +#endif /* CONFIG_DT_CPU_TOPOLOGY */ + +#endif /* XEN_DT_CPU_TOPOLOGY_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.43.0
Make CPU topology information available to the Xen scheduler. Additionally, ensure that this topology information is displayed when executing the 'xl info -n' command. Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp> --- Changes in v5: Corrected the erroneous use of CONFIG_CPU_TOPOLOGY to CONFIG_GENERIC_CPU_TOPOLOGY. xen/arch/arm/include/asm/processor.h | 4 -- xen/arch/arm/smpboot.c | 8 +--- xen/arch/ppc/include/asm/processor.h | 4 -- xen/arch/riscv/include/asm/processor.h | 4 -- xen/common/device-tree/cpu-topology.c | 51 ++++++++++++++++++++++++++ xen/common/sched/credit2.c | 3 ++ xen/common/sysctl.c | 1 + xen/drivers/acpi/topology.c | 3 ++ xen/include/xen/cpu-topology.h | 45 ++++++++++++++++++++++- 9 files changed, 103 insertions(+), 20 deletions(-) diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/processor.h +++ b/xen/arch/arm/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ void show_stack(const struct cpu_user_regs *regs); #define cpu_relax() barrier() /* Could yield? */ -/* All a bit UP for the moment */ -#define cpu_to_core(_cpu) (0) -#define cpu_to_socket(_cpu) (0) - struct vcpu; void vcpu_regs_hyp_to_user(const struct vcpu *vcpu, struct vcpu_guest_core_regs *regs); diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -XXX,XX +XXX,XX @@ static int setup_cpu_sibling_map(int cpu) !zalloc_cpumask_var(&per_cpu(cpu_core_mask, cpu)) ) return -ENOMEM; - /* - * Currently we assume there is no multithread and NUMA, so - * a CPU is a sibling with itself, and the all possible CPUs - * are supposed to belong to the same socket (NUMA node). - */ - cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); - cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); + init_cpu_sibling_map(cpu); return 0; } diff --git a/xen/arch/ppc/include/asm/processor.h b/xen/arch/ppc/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/ppc/include/asm/processor.h +++ b/xen/arch/ppc/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ /* Macro to adjust thread priority for hardware multithreading */ #define HMT_very_low() asm volatile ( "or %r31, %r31, %r31" ) -/* TODO: This isn't correct */ -#define cpu_to_core(cpu) (0) -#define cpu_to_socket(cpu) (0) - /* * User-accessible registers: most of these need to be saved/restored * for every nested Xen invocation. diff --git a/xen/arch/riscv/include/asm/processor.h b/xen/arch/riscv/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/riscv/include/asm/processor.h +++ b/xen/arch/riscv/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ struct cpu_user_regs unsigned long pregs; }; -/* TODO: need to implement */ -#define cpu_to_core(cpu) 0 -#define cpu_to_socket(cpu) 0 - static inline void cpu_relax(void) { #ifdef __riscv_zihintpause diff --git a/xen/common/device-tree/cpu-topology.c b/xen/common/device-tree/cpu-topology.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/device-tree/cpu-topology.c +++ b/xen/common/device-tree/cpu-topology.c @@ -XXX,XX +XXX,XX @@ int __init parse_dt_topology(void) return parse_socket(map); } +static void __init setup_cpu_topology_ids(void) +{ + unsigned int cpu; + unsigned int next_core_id = 0; + unsigned int next_cluster_id = 0; + unsigned int next_socket_id = 0; + + for_each_possible_cpu(cpu) + { + unsigned int first_cpu; + struct cpu_topology *topo = &cpu_topology[cpu]; + + first_cpu = cpumask_first(topo->thread_sibling); + if ( first_cpu == cpu ) + { + topo->phys_core_id = next_core_id; + next_core_id++; + } + else + topo->phys_core_id = cpu_topology[first_cpu].phys_core_id; + + /* Reuse the calculated core id if clustering is not supported */ + if ( cpumask_empty(topo->cluster_sibling) ) + topo->phys_cluster_id = topo->phys_core_id; + else + { + first_cpu = cpumask_first(topo->cluster_sibling); + if ( first_cpu == cpu ) + { + topo->phys_cluster_id = next_cluster_id; + next_cluster_id++; + } + else + topo->phys_cluster_id = cpu_topology[first_cpu].phys_cluster_id; + } + + first_cpu = cpumask_first(topo->core_sibling); + if ( first_cpu == cpu ) + { + topo->phys_socket_id = next_socket_id; + next_socket_id++; + } + else + topo->phys_socket_id = cpu_topology[first_cpu].phys_socket_id; + + topo->num_siblings = cpumask_weight(topo->thread_sibling); + } +} + void __init dt_init_cpu_topology(void) { unsigned int cpu; @@ -XXX,XX +XXX,XX @@ void __init dt_init_cpu_topology(void) for_each_possible_cpu(cpu) setup_siblings_masks(cpu); + + setup_cpu_topology_ids(); } /* diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sched/credit2.c +++ b/xen/common/sched/credit2.c @@ -XXX,XX +XXX,XX @@ * Based on an earlier verson by Emmanuel Ackaouy. */ +#include <xen/cpu-topology.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/lib.h> @@ -XXX,XX +XXX,XX @@ static unsigned int cpu_nr_siblings(unsigned int cpu) { #ifdef CONFIG_X86 return cpu_data[cpu].x86_num_siblings; +#elif defined(CONFIG_GENERIC_CPU_TOPOLOGY) + return cpu_topology ? cpu_topology[cpu].num_siblings : 1; #else return 1; #endif diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -XXX,XX +XXX,XX @@ #include <xen/pmstat.h> #include <xen/livepatch.h> #include <xen/coverage.h> +#include <xen/cpu-topology.h> long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) { diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/topology.c +++ b/xen/drivers/acpi/topology.c @@ -XXX,XX +XXX,XX @@ void __init acpi_init_cpu_topology(void) { struct cpu_topology *topo = &cpu_topology[cpu]; + topo->phys_core_id = cpu; + topo->num_siblings = 1; + cpumask_set_cpu(cpu, topo->thread_sibling); cpumask_copy(topo->core_sibling, &cpu_possible_map); } diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/cpu-topology.h +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ #define XEN_CPU_TOPOLOGY_H #include <xen/cpumask.h> +#include <xen/percpu.h> +#include <asm/processor.h> +#include <asm/smp.h> #ifdef CONFIG_GENERIC_CPU_TOPOLOGY @@ -XXX,XX +XXX,XX @@ struct cpu_topology { cpumask_var_t thread_sibling; cpumask_var_t core_sibling; cpumask_var_t cluster_sibling; + unsigned int phys_core_id; + unsigned int phys_cluster_id; + unsigned int phys_socket_id; + unsigned int num_siblings; }; extern struct cpu_topology *cpu_topology; void init_cpu_topology(void); +static inline void init_cpu_sibling_map(unsigned int cpu) +{ + if ( cpu_topology ) + { + cpumask_copy(per_cpu(cpu_sibling_mask, cpu), + cpu_topology[cpu].thread_sibling); + cpumask_copy(per_cpu(cpu_core_mask, cpu), + cpu_topology[cpu].core_sibling); + } + else + { + cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); + cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); + } +} + +#define cpu_to_core(cpu) (cpu_topology ? cpu_topology[cpu].phys_core_id : 0) +#define cpu_to_socket(cpu) (cpu_topology ? cpu_topology[cpu].phys_socket_id : 0) + #else /* CONFIG_GENERIC_CPU_TOPOLOGY */ -#define cpu_topology ((struct cpu_topology *)NULL) static inline void init_cpu_topology(void) {} +static inline void init_cpu_sibling_map(unsigned int cpu) +{ + /* + * If CONFIG_GENERIC_CPU_TOPOLOGY is disabled, it is assumed that + * all CPUs reside in the same socket and that SMT is not used. + */ + cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu)); + cpumask_copy(per_cpu(cpu_core_mask, cpu), &cpu_possible_map); +} + +#ifndef cpu_to_core +#define cpu_to_core(cpu) (0) +#endif + +#ifndef cpu_to_socket +#define cpu_to_socket(cpu) (0) +#endif + #endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ #endif /* XEN_CPU_TOPOLOGY_H */ -- 2.43.0
Make cpu_nr_siblings() an architecture-specific function. This patch provides the implementation for x86 and a common version for Device Tree-based architectures. Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp> Acked-by: Jan Beulich <jbeulich@suse.com> --- xen/arch/x86/include/asm/processor.h | 1 + xen/common/sched/credit2.c | 22 +++------------------- xen/include/xen/cpu-topology.h | 5 +++++ 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/x86/include/asm/processor.h +++ b/xen/arch/x86/include/asm/processor.h @@ -XXX,XX +XXX,XX @@ extern void intel_init_arat(void); #define cpu_to_core(_cpu) (cpu_data[_cpu].cpu_core_id) #define cpu_to_socket(_cpu) (cpu_data[_cpu].phys_proc_id) +#define cpu_nr_siblings(_cpu) (cpu_data[_cpu].x86_num_siblings) unsigned int apicid_to_socket(unsigned int apicid); diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c index XXXXXXX..XXXXXXX 100644 --- a/xen/common/sched/credit2.c +++ b/xen/common/sched/credit2.c @@ -XXX,XX +XXX,XX @@ /* #define d2printk printk */ #define d2printk(x...) -/* - * TODO: Abstract this properly, and figure out what Credit2 wants to do with - * the fact that x86_num_siblings doesn't even have the same meaning - * between x86 vendors. - */ -static unsigned int cpu_nr_siblings(unsigned int cpu) -{ -#ifdef CONFIG_X86 - return cpu_data[cpu].x86_num_siblings; -#elif defined(CONFIG_GENERIC_CPU_TOPOLOGY) - return cpu_topology ? cpu_topology[cpu].num_siblings : 1; -#else - return 1; -#endif -} - /* * Credit2 tracing events ("only" 512 available!). Check * include/public/trace.h for more details. @@ -XXX,XX +XXX,XX @@ cpu_runqueue_match(const struct csched2_runqueue_data *rqd, unsigned int cpu) /* * Additional checks, to avoid separating siblings in different runqueues. - * This deals with both Intel's HTs and AMD's CUs. An arch that does not have - * any similar concept will just have cpu_nr_siblings() always return 1, and - * setup the cpu_sibling_mask-s acordingly (as currently does ARM), and things + * This deals with Intel's HTs, AMD's CUs and ARM's SMT. An arch that + * does not have similar concept will just have cpu_nr_siblings() always + * return 1, and setup the cpu_sibling_mask-s accordingly, and things * will just work as well. */ static bool diff --git a/xen/include/xen/cpu-topology.h b/xen/include/xen/cpu-topology.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/cpu-topology.h +++ b/xen/include/xen/cpu-topology.h @@ -XXX,XX +XXX,XX @@ static inline void init_cpu_sibling_map(unsigned int cpu) #define cpu_to_core(cpu) (cpu_topology ? cpu_topology[cpu].phys_core_id : 0) #define cpu_to_socket(cpu) (cpu_topology ? cpu_topology[cpu].phys_socket_id : 0) +#define cpu_nr_siblings(cpu) (cpu_topology ? cpu_topology[cpu].num_siblings : 1) #else /* CONFIG_GENERIC_CPU_TOPOLOGY */ @@ -XXX,XX +XXX,XX @@ static inline void init_cpu_sibling_map(unsigned int cpu) #define cpu_to_socket(cpu) (0) #endif +#ifndef cpu_nr_siblings +#define cpu_nr_siblings(cpu) (1) +#endif + #endif /* CONFIG_GENERIC_CPU_TOPOLOGY */ #endif /* XEN_CPU_TOPOLOGY_H */ -- 2.43.0
Parse the ACPI PPTT (Processor Properties Topology Table) to initialize the CPU topology. For ACPI 6.3 and later, the ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD flag is checked to determine the presence of SMT. For ACPI 6.2 and earlier, CPUs are assumed not to support SMT. Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp> --- Changes in v5: Extracted CPU topology information from the ACPI PPTT. xen/arch/arm/acpi/boot.c | 2 + xen/arch/arm/include/asm/acpi.h | 2 + xen/drivers/acpi/topology.c | 230 ++++++++++++++++++++++++++++++-- xen/include/acpi/actbl3.h | 30 +++++ xen/include/xen/acpi.h | 8 ++ 5 files changed, 264 insertions(+), 8 deletions(-) diff --git a/xen/arch/arm/acpi/boot.c b/xen/arch/arm/acpi/boot.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/acpi/boot.c +++ b/xen/arch/arm/acpi/boot.c @@ -XXX,XX +XXX,XX @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) return; } bootcpu_valid = true; + acpi_map_cpu_acpiid(0, processor->uid); return; } @@ -XXX,XX +XXX,XX @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) /* map the logical cpu id to cpu MPIDR */ cpu_logical_map(enabled_cpus) = mpidr; + acpi_map_cpu_acpiid(enabled_cpus, processor->uid); enabled_cpus++; } diff --git a/xen/arch/arm/include/asm/acpi.h b/xen/arch/arm/include/asm/acpi.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/acpi.h +++ b/xen/arch/arm/include/asm/acpi.h @@ -XXX,XX +XXX,XX @@ paddr_t acpi_get_table_offset(struct membank tbl_add[], EFI_MEM_RES index); (!(entry) || (unsigned long)(entry) + sizeof(*(entry)) > (end) || \ (entry)->header.length != ACPI_MADT_GICC_LENGTH) +#define INVALID_ACPIID (-1U) + #ifdef CONFIG_ACPI extern bool acpi_disabled; /* Basic configuration for ACPI */ diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c index XXXXXXX..XXXXXXX 100644 --- a/xen/drivers/acpi/topology.c +++ b/xen/drivers/acpi/topology.c @@ -XXX,XX +XXX,XX @@ #include <xen/cpumask.h> #include <xen/init.h> -/* - * TODO: Populate the topology information by scanning the ACPI - * PPTT (Processor Properties Topology Table). - */ -void __init acpi_init_cpu_topology(void) +uint32_t map_cpu_acpiid[NR_CPUS] __initdata = + { [0 ... NR_CPUS - 1] = INVALID_ACPIID }; +uint32_t socket_map[NR_CPUS] __initdata; +uint32_t cluster_map[NR_CPUS] __initdata; +uint32_t core_map[NR_CPUS] __initdata; +uint32_t thread_map[NR_CPUS] __initdata; +unsigned int __initdata num_sockets; +unsigned int __initdata num_clusters; +unsigned int __initdata num_cores; + +static unsigned int __init get_logical_id(uint32_t phys_offset, + uint32_t *map, + unsigned int *count) +{ + unsigned int id; + + for ( id = 0; id < *count; id++ ) + if ( map[id] == phys_offset ) + return id; + + map[*count] = phys_offset; + id = *count; + (*count)++; + + return id; +} + +static struct acpi_pptt_processor *__init find_pptt_node( + const struct acpi_table_header *table_hdr, unsigned int acpi_id) +{ + const struct acpi_subtable_header *entry; + unsigned long table_end; + const char *ptr; + + if ( !table_hdr ) + return NULL; + + table_end = (unsigned long)table_hdr + table_hdr->length; + + ptr = (const char *)table_hdr + sizeof(struct acpi_table_pptt); + + while ( (unsigned long)ptr + sizeof(struct acpi_subtable_header) + <= table_end ) + { + entry = (const struct acpi_subtable_header *)ptr; + + if ( entry->length == 0 ) + { + printk(XENLOG_ERR + "ACPI: PPTT has an invalid zero-length subtable.\n"); + break; + } + + if ( (unsigned long)ptr + entry->length > table_end ) + { + printk(XENLOG_ERR + "ACPI: PPTT subtable extends beyond table end.\n"); + break; + } + + if ( entry->type == ACPI_PPTT_TYPE_PROCESSOR ) + if ( entry->length >= sizeof(struct acpi_pptt_processor) ) + { + struct acpi_pptt_processor *proc = + (struct acpi_pptt_processor *)entry; + + if ( (proc->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) && + proc->acpi_processor_id == acpi_id ) + return proc; + } + + ptr += entry->length; + } + + return NULL; +} + +static void __init setup_fake_topology(void) { unsigned int cpu; /* - * Generate temporary cpu topology information for now. - * It assumes that the cpu doesn't have SMT and all CPUs - * belong to the same socket. + * Generate temporary cpu topology information. It assumes that + * the cpu doesn't have SMT and all CPUs belong to the same socket. */ for_each_possible_cpu(cpu) { @@ -XXX,XX +XXX,XX @@ void __init acpi_init_cpu_topology(void) } } +/* + * Populate the topology information by scanning the ACPI PPTT + * (Processor Properties Topology Table). + */ +void __init acpi_init_cpu_topology(void) +{ + acpi_status status; + struct acpi_table_header *header; + const struct acpi_table_pptt *pptt; + unsigned int cpu; + + status = acpi_get_table(ACPI_SIG_PPTT, 0, &header); + if ( ACPI_FAILURE(status) ) + { + printk(XENLOG_WARNING + "ACPI: PPTT table not found. Topology fallback will be used.\n"); + setup_fake_topology(); + return; + } + + pptt = (struct acpi_table_pptt *)header; + + for_each_possible_cpu(cpu) + { + unsigned int acpi_id = map_cpu_acpiid[cpu]; + struct cpu_topology *topo = &cpu_topology[cpu]; + const struct acpi_pptt_processor *proc; + unsigned int level = 0; + uint32_t thread_offset = 0; + uint32_t core_offset = 0; + uint32_t cluster_offset = 0; + uint32_t socket_offset = 0; + bool threading = true; + + proc = find_pptt_node(&pptt->header, acpi_id); + if ( !proc ) + { + printk(XENLOG_WARNING + "ACPI: No PPTT leaf node for CPU %u (ACPI ID 0x%u)\n", + cpu, acpi_id); + continue; + } + + while ( proc ) + { + if ( proc->flags & ACPI_PPTT_PHYSICAL_PACKAGE ) + { + socket_offset = (char *)proc - (char *)pptt; + break; + } + else if ( level == 0 ) + /* + * ACPI_PPTT_PROCESSOR_IS_THREAD is supported in PPTT + * revision 2 and later. + */ + if ( proc->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD ) + thread_offset = (char *)proc - (char *)pptt; + else + { + /* Assume no threading support when PPTT revision is 1. */ + threading = false; + core_offset = (char *)proc - (char *)pptt; + } + else if ( level == 1 ) + if ( threading ) + core_offset = (char *)proc - (char *)pptt; + else + cluster_offset = (char *)proc - (char *)pptt; + else if ( level == 2 ) + if ( threading ) + cluster_offset = (char *)proc - (char *)pptt; + + if ( proc->parent ) + { + proc = (const struct acpi_pptt_processor *) + ((char *)pptt + proc->parent); + level++; + } + else + break; + } + + topo->phys_socket_id = + get_logical_id(socket_offset, socket_map, &num_sockets); + topo->phys_cluster_id = + get_logical_id(cluster_offset, cluster_map, &num_clusters); + topo->phys_core_id = + get_logical_id(core_offset, core_map, &num_cores); + + /* Fall back to socket ID if PPTT lacks cluster information. */ + if ( topo->phys_cluster_id == 0 ) + topo->phys_cluster_id = topo->phys_socket_id; + } + + for_each_possible_cpu(cpu) + { + struct cpu_topology *topo = &cpu_topology[cpu]; + unsigned int tcpu; + + for_each_possible_cpu(tcpu) + { + struct cpu_topology *ttopo = &cpu_topology[tcpu]; + + if ( cpu > tcpu ) + continue; + + if ( topo->phys_core_id == ttopo->phys_core_id ) + { + cpumask_set_cpu(tcpu, topo->thread_sibling); + cpumask_set_cpu(cpu, ttopo->thread_sibling); + } + + if ( topo->phys_cluster_id == ttopo->phys_cluster_id ) + { + cpumask_set_cpu(tcpu, topo->cluster_sibling); + cpumask_set_cpu(cpu, ttopo->cluster_sibling); + } + + if ( topo->phys_socket_id == ttopo->phys_socket_id ) + { + cpumask_set_cpu(tcpu, topo->core_sibling); + cpumask_set_cpu(cpu, ttopo->core_sibling); + } + } + + topo->num_siblings = cpumask_weight(topo->thread_sibling); + } + + for_each_possible_cpu(cpu) + { + const struct cpu_topology *topo = &cpu_topology[cpu]; + + printk(XENLOG_DEBUG + "ACPI: acpi_id[%u] CPU-%u Socket-%u Cluster-%u Core-%u\n", + map_cpu_acpiid[cpu], + cpu, + topo->phys_socket_id, + topo->phys_cluster_id, + topo->phys_core_id); + } +} + /* * Local variables: * mode: C diff --git a/xen/include/acpi/actbl3.h b/xen/include/acpi/actbl3.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/acpi/actbl3.h +++ b/xen/include/acpi/actbl3.h @@ -XXX,XX +XXX,XX @@ #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ +#define ACPI_SIG_PPTT "PPTT" /* Processor Properties Topology Table */ /* Reserved table signatures */ @@ -XXX,XX +XXX,XX @@ struct acpi_table_stao { u8 ignore_uart; }; +/******************************************************************************* + * + * PPTT - Processor Properties Topology Table - ACPI 6.3 + * Version 1 + * + ******************************************************************************/ +struct acpi_table_pptt { + struct acpi_table_header header; +}; + +#define ACPI_PPTT_TYPE_PROCESSOR 0 +#define ACPI_PPTT_TYPE_CACHE 1 +#define ACPI_PPTT_TYPE_ID 2 + +struct acpi_pptt_processor { + struct acpi_subtable_header header; + u16 reserved; + u32 flags; + u32 parent; + u32 acpi_processor_id; + u32 number_of_priv_resources; +}; + +#define ACPI_PPTT_PHYSICAL_PACKAGE (1) +#define ACPI_PPTT_ACPI_PROCESSOR_ID_VALID (1 << 1) +#define ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD (1 << 2) /* ACPI 6.3 */ +#define ACPI_PPTT_ACPI_LEAF_NODE (1 << 3) /* ACPI 6.3 */ +#define ACPI_PPTT_ACPI_IDENTICAL (1 << 4) /* ACPI 6.3 */ + /* Reset to default packing */ #pragma pack() diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index XXXXXXX..XXXXXXX 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -XXX,XX +XXX,XX @@ static inline int acpi_boot_table_init(void) void acpi_init_cpu_topology(void); +extern uint32_t map_cpu_acpiid[NR_CPUS]; + +static inline void acpi_map_cpu_acpiid(unsigned int cpu, uint32_t acpi_id) +{ + map_cpu_acpiid[cpu] = acpi_id; +} + #else /* CONFIG_ACPI_CPU_TOPOLOGY */ +static inline void acpi_map_cpu_acpiid(unsigned int cpu, uint32_t acpi_id) {} static inline void acpi_init_cpu_topology(void) {} #endif /* CONFIG_ACPI_CPU_TOPOLOGY */ -- 2.43.0