[PATCH v4 2/3] xen/sched: Link CPU topology to scheduler

Hirokazu Takahashi posted 3 patches 1 month ago
There is a newer version of this series
[PATCH v4 2/3] xen/sched: Link CPU topology to scheduler
Posted by Hirokazu Takahashi 1 month ago
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
Re: [PATCH v4 2/3] xen/sched: Link CPU topology to scheduler
Posted by Jan Beulich 1 month ago
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
Re: [PATCH v4 2/3] xen/sched: Link CPU topology to scheduler
Posted by Oleksii Kurochko 1 month ago

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
RE: [PATCH v4 2/3] xen/sched: Link CPU topology to scheduler
Posted by Hirokazu Takahashi 1 month ago
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.