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 a3753c317f..41fa73cfc4 100644
--- a/xen/arch/arm/include/asm/processor.h
+++ b/xen/arch/arm/include/asm/processor.h
@@ -613,10 +613,6 @@ 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 5ce2bcf6ec..3c9f2a5c53 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -92,13 +92,7 @@ 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 242346cab9..1bf6f6c66c 100644
--- a/xen/arch/ppc/include/asm/processor.h
+++ b/xen/arch/ppc/include/asm/processor.h
@@ -141,10 +141,6 @@
/* 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 6b89df4a2d..d478ffb76b 100644
--- a/xen/arch/riscv/include/asm/processor.h
+++ b/xen/arch/riscv/include/asm/processor.h
@@ -54,10 +54,6 @@ 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 b653227ef4..43322a153f 100644
--- a/xen/common/device-tree/cpu-topology.c
+++ b/xen/common/device-tree/cpu-topology.c
@@ -330,6 +330,55 @@ 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;
@@ -342,6 +391,8 @@ 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 95946634d1..ada430f262 100644
--- a/xen/common/sched/credit2.c
+++ b/xen/common/sched/credit2.c
@@ -9,6 +9,7 @@
* 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>
@@ -37,6 +38,8 @@ 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 5207664252..81a68fe24c 100644
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -28,6 +28,7 @@
#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 6bd2d96ebb..9155edc0be 100644
--- a/xen/drivers/acpi/topology.c
+++ b/xen/drivers/acpi/topology.c
@@ -22,6 +22,9 @@ 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 f64820febf..ee34425680 100644
--- a/xen/include/xen/cpu-topology.h
+++ b/xen/include/xen/cpu-topology.h
@@ -4,6 +4,9 @@
#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
@@ -11,16 +14,56 @@ 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
On 03.07.2026 11:15, Hirokazu Takahashi wrote:
> 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 a3753c317f..41fa73cfc4 100644
> --- a/xen/arch/arm/include/asm/processor.h
> +++ b/xen/arch/arm/include/asm/processor.h
> @@ -613,10 +613,6 @@ 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 5ce2bcf6ec..3c9f2a5c53 100644
> --- a/xen/arch/arm/smpboot.c
> +++ b/xen/arch/arm/smpboot.c
> @@ -92,13 +92,7 @@ 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 242346cab9..1bf6f6c66c 100644
> --- a/xen/arch/ppc/include/asm/processor.h
> +++ b/xen/arch/ppc/include/asm/processor.h
> @@ -141,10 +141,6 @@
> /* 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 6b89df4a2d..d478ffb76b 100644
> --- a/xen/arch/riscv/include/asm/processor.h
> +++ b/xen/arch/riscv/include/asm/processor.h
> @@ -54,10 +54,6 @@ 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 b653227ef4..43322a153f 100644
> --- a/xen/common/device-tree/cpu-topology.c
> +++ b/xen/common/device-tree/cpu-topology.c
> @@ -330,6 +330,55 @@ 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;
Not even an assertion to make sure first_cpu is within bounds, i.e.
topo->thread_sibling isn't (by mistake) empty? (Same again further down.)
> + /* Reuse the calculated core id if clustering is not supported */
> + if ( cpumask_empty(topo->cluster_sibling) )
> + topo->phys_cluster_id = topo->phys_core_id;
Wouldn't the cluster better be uniformly the same value (perhaps 0) for
all CPUs when there's no clustering?
> + else
> + {
> + first_cpu = cpumask_first(topo->cluster_sibling);
No need to use cpumask_empty() and cpumask_first(). The return value of the
latter allows to identify the "empty" case.
> --- a/xen/common/sched/credit2.c
> +++ b/xen/common/sched/credit2.c
> @@ -9,6 +9,7 @@
> * 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>
> @@ -37,6 +38,8 @@ 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;
Apart from the naming issue I continue to think that the generic case should
come first, and x86'es special case second. Yet then I'm not a maintainer of
this code ...
> --- a/xen/include/xen/cpu-topology.h
> +++ b/xen/include/xen/cpu-topology.h
> @@ -4,6 +4,9 @@
> #define XEN_CPU_TOPOLOGY_H
>
> #include <xen/cpumask.h>
> +#include <xen/percpu.h>
> +#include <asm/processor.h>
> +#include <asm/smp.h>
Blank line please between the xen/ group and the asm/ one.
> @@ -11,16 +14,56 @@ 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);
Isn't this the same as ...
> + }
> +}
> +
> +#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);
... this? Would be nice to avoid the duplication.
Jan
On 7/3/26 11:15 AM, Hirokazu Takahashi wrote:
>
> /*
> diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c
> index 95946634d1..ada430f262 100644
> --- a/xen/common/sched/credit2.c
> +++ b/xen/common/sched/credit2.c
> @@ -9,6 +9,7 @@
> * 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>
> @@ -37,6 +38,8 @@ 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;
Shouldn't here be CONFIG_GENERIC_CPU_TOPOLOGY?
~ Oleksii
Hello,
/*
> > diff --git a/xen/common/sched/credit2.c b/xen/common/sched/credit2.c
> > index 95946634d1..ada430f262 100644
> > --- a/xen/common/sched/credit2.c
> > +++ b/xen/common/sched/credit2.c
> > @@ -9,6 +9,7 @@
> > * 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>
> > @@ -37,6 +38,8 @@ 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;
>
> Shouldn't here be CONFIG_GENERIC_CPU_TOPOLOGY?
Oops! I will fix it soon.
Hirokazu Takahashi.
© 2016 - 2026 Red Hat, Inc.