Stefano Stabellini
2012-Nov-15  17:48 UTC
[PATCH v3] xen/arm: get the number of cpus from device tree
The system might have fewer cpus than the GIC supports.
Changes in v3:
- get cpu nodes matching the device_type property on the DT;
- get the cpu ID from the reg property on the DT;
- hook the DT cpu parsing in early_scan_node.
Changes in v2:
- return always at least 1 cpu;
- skip nodes with names that don''t start with "cpu".
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
---
 xen/arch/arm/gic.c        |    4 +---
 xen/arch/arm/gic.h        |    2 +-
 xen/arch/arm/setup.c      |    5 ++++-
 xen/arch/arm/smpboot.c    |   27 ++++++++++++++++++++-------
 xen/common/device_tree.c  |   43 +++++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/smp.h |    2 ++
 6 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 5f06e08..0c6fab9 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -304,7 +304,7 @@ static void __cpuinit gic_hyp_disable(void)
 }
 
 /* Set up the GIC */
-int __init gic_init(void)
+void __init gic_init(void)
 {
     /* XXX FIXME get this from devicetree */
     gic.dbase = GIC_BASE_ADDRESS + GIC_DR_OFFSET;
@@ -328,8 +328,6 @@ int __init gic_init(void)
     gic.lr_mask = 0ULL;
 
     spin_unlock(&gic.lock);
-
-    return gic.cpus;
 }
 
 /* Set up the per-CPU parts of the GIC for a secondary CPU */
diff --git a/xen/arch/arm/gic.h b/xen/arch/arm/gic.h
index b2e1d7f..1bf1b02 100644
--- a/xen/arch/arm/gic.h
+++ b/xen/arch/arm/gic.h
@@ -147,7 +147,7 @@ extern int gic_route_irq_to_guest(struct domain *d, unsigned
int irq,
 /* Accept an interrupt from the GIC and dispatch its handler */
 extern void gic_interrupt(struct cpu_user_regs *regs, int is_fiq);
 /* Bring up the interrupt controller, and report # cpus attached */
-extern int gic_init(void);
+extern void gic_init(void);
 /* Bring up a secondary CPU''s per-CPU GIC interface */
 extern void gic_init_secondary_cpu(void);
 /* Take down a CPU''s per-CPU GIC interface */
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index a579a56..d5d845a 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -189,10 +189,13 @@ void __init start_xen(unsigned long boot_phys_offset,
     if ( cacheline_bytes < MIN_CACHELINE_BYTES )
         panic("CPU has preposterously small cache lines");
 
+    smp_clear_cpu_maps();
+
     fdt = (void *)BOOT_MISC_VIRT_START
         + (atag_paddr & ((1 << SECOND_SHIFT) - 1));
     fdt_size = device_tree_early_init(fdt);
 
+    cpus = smp_get_max_cpus();
     cmdline_parse(device_tree_bootargs(fdt));
 
     setup_pagetables(boot_phys_offset, get_xen_paddr());
@@ -203,7 +206,7 @@ void __init start_xen(unsigned long boot_phys_offset,
     console_init_preirq();
 #endif
 
-    cpus = gic_init();
+    gic_init();
     make_cpus_ready(cpus, boot_phys_offset);
 
     percpu_init_areas();
diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
index f4fd512..93b7ded 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -71,18 +71,31 @@ static void setup_cpu_sibling_map(int cpu)
     cpumask_set_cpu(cpu, per_cpu(cpu_core_mask, cpu));
 }
 
-
 void __init
-smp_prepare_cpus (unsigned int max_cpus)
+smp_clear_cpu_maps (void)
 {
-    int i;
-
+    cpumask_clear(&cpu_possible_map);
     cpumask_clear(&cpu_online_map);
     cpumask_set_cpu(0, &cpu_online_map);
+    cpumask_set_cpu(0, &cpu_possible_map);
+}
 
-    cpumask_clear(&cpu_possible_map);
-    for ( i = 0; i < max_cpus; i++ )
-        cpumask_set_cpu(i, &cpu_possible_map);
+int __init
+smp_get_max_cpus (void)
+{
+    int i, max_cpus = 0;
+
+    for ( i = 0; i < nr_cpu_ids; i++ )
+        if ( cpu_possible(i) )
+            max_cpus++;
+
+    return max_cpus;
+}
+
+
+void __init
+smp_prepare_cpus (unsigned int max_cpus)
+{
     cpumask_copy(&cpu_present_map, &cpu_possible_map);
 
     setup_cpu_sibling_map(0);
diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 3d1f0f4..da0af77 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -18,6 +18,7 @@
 #include <xen/mm.h>
 #include <xen/stdarg.h>
 #include <xen/string.h>
+#include <xen/cpumask.h>
 #include <asm/early_printk.h>
 
 struct dt_early_info __initdata early_info;
@@ -41,6 +42,18 @@ bool_t device_tree_node_matches(const void *fdt, int node,
const char *match)
         && (name[match_len] == ''@'' || name[match_len]
== ''\0'');
 }
 
+bool_t device_tree_type_matches(const void *fdt, int node, const char *match)
+{
+    int len;
+    const void *prop;
+
+    prop = fdt_getprop(fdt, node, "device_type", &len);
+    if ( prop == NULL )
+        return 0;
+
+    return !strncmp(prop, match, len);
+}
+
 static void __init get_val(const u32 **cell, u32 cells, u64 *val)
 {
     *val = 0;
@@ -229,6 +242,34 @@ static void __init process_memory_node(const void *fdt, int
node,
     }
 }
 
+static void __init process_cpu_node(const void *fdt, int node,
+                                    const char *name,
+                                    u32 address_cells, u32 size_cells)
+{
+    const struct fdt_property *prop;
+    const u32 *cell;
+    paddr_t start, size;
+
+    if ( address_cells != 1 || size_cells != 0 )
+    {
+        early_printk("fdt: node `%s'': invalid #address-cells or
#size-cells",
+                     name);
+        return;
+    }
+
+    prop = fdt_get_property(fdt, node, "reg", NULL);
+    if ( !prop )
+    {
+        early_printk("fdt: node `%s'': missing `reg''
property\n", name);
+        return;
+    }
+
+    cell = (const u32 *)prop->data;
+    device_tree_get_reg(&cell, address_cells, size_cells, &start,
&size);
+
+    cpumask_set_cpu(start, &cpu_possible_map);
+}
+
 static int __init early_scan_node(const void *fdt,
                                   int node, const char *name, int depth,
                                   u32 address_cells, u32 size_cells,
@@ -236,6 +277,8 @@ static int __init early_scan_node(const void *fdt,
 {
     if ( device_tree_node_matches(fdt, node, "memory") )
         process_memory_node(fdt, node, name, address_cells, size_cells);
+    else if ( device_tree_type_matches(fdt, node, "cpu") )
+        process_cpu_node(fdt, node, name, address_cells, size_cells);
 
     return 0;
 }
diff --git a/xen/include/asm-arm/smp.h b/xen/include/asm-arm/smp.h
index a98032d..d4ed1cb 100644
--- a/xen/include/asm-arm/smp.h
+++ b/xen/include/asm-arm/smp.h
@@ -22,6 +22,8 @@ extern void stop_cpu(void);
 extern void
 make_cpus_ready(unsigned int max_cpus, unsigned long boot_phys_offset);
 
+extern void smp_clear_cpu_maps (void);
+extern int smp_get_max_cpus (void);
 #endif
 /*
  * Local variables:
-- 
1.7.2.5
Ian Campbell
2012-Nov-19  12:55 UTC
Re: [PATCH v3] xen/arm: get the number of cpus from device tree
On Thu, 2012-11-15 at 17:48 +0000, Stefano Stabellini wrote:> The system might have fewer cpus than the GIC supports. > > Changes in v3: > - get cpu nodes matching the device_type property on the DT; > - get the cpu ID from the reg property on the DT; > - hook the DT cpu parsing in early_scan_node. > > Changes in v2: > - return always at least 1 cpu; > - skip nodes with names that don''t start with "cpu". > > Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>Acked + applied, thanks. Small reject in xen/arch/arm/setup.c due to changes in the flush D-cache patch (the panic on preposterous cahceline size at the start of start_xen is gone). Please check I did the right thing!> --- > xen/arch/arm/gic.c | 4 +--- > xen/arch/arm/gic.h | 2 +- > xen/arch/arm/setup.c | 5 ++++- > xen/arch/arm/smpboot.c | 27 ++++++++++++++++++++------- > xen/common/device_tree.c | 43 +++++++++++++++++++++++++++++++++++++++++++ > xen/include/asm-arm/smp.h | 2 ++ > 6 files changed, 71 insertions(+), 12 deletions(-) > > diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c > index 5f06e08..0c6fab9 100644 > --- a/xen/arch/arm/gic.c > +++ b/xen/arch/arm/gic.c > @@ -304,7 +304,7 @@ static void __cpuinit gic_hyp_disable(void) > } > > /* Set up the GIC */ > -int __init gic_init(void) > +void __init gic_init(void) > { > /* XXX FIXME get this from devicetree */ > gic.dbase = GIC_BASE_ADDRESS + GIC_DR_OFFSET; > @@ -328,8 +328,6 @@ int __init gic_init(void) > gic.lr_mask = 0ULL; > > spin_unlock(&gic.lock); > - > - return gic.cpus; > } > > /* Set up the per-CPU parts of the GIC for a secondary CPU */ > diff --git a/xen/arch/arm/gic.h b/xen/arch/arm/gic.h > index b2e1d7f..1bf1b02 100644 > --- a/xen/arch/arm/gic.h > +++ b/xen/arch/arm/gic.h > @@ -147,7 +147,7 @@ extern int gic_route_irq_to_guest(struct domain *d, unsigned int irq, > /* Accept an interrupt from the GIC and dispatch its handler */ > extern void gic_interrupt(struct cpu_user_regs *regs, int is_fiq); > /* Bring up the interrupt controller, and report # cpus attached */ > -extern int gic_init(void); > +extern void gic_init(void); > /* Bring up a secondary CPU''s per-CPU GIC interface */ > extern void gic_init_secondary_cpu(void); > /* Take down a CPU''s per-CPU GIC interface */ > diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c > index a579a56..d5d845a 100644 > --- a/xen/arch/arm/setup.c > +++ b/xen/arch/arm/setup.c > @@ -189,10 +189,13 @@ void __init start_xen(unsigned long boot_phys_offset, > if ( cacheline_bytes < MIN_CACHELINE_BYTES ) > panic("CPU has preposterously small cache lines"); > > + smp_clear_cpu_maps(); > + > fdt = (void *)BOOT_MISC_VIRT_START > + (atag_paddr & ((1 << SECOND_SHIFT) - 1)); > fdt_size = device_tree_early_init(fdt); > > + cpus = smp_get_max_cpus(); > cmdline_parse(device_tree_bootargs(fdt)); > > setup_pagetables(boot_phys_offset, get_xen_paddr()); > @@ -203,7 +206,7 @@ void __init start_xen(unsigned long boot_phys_offset, > console_init_preirq(); > #endif > > - cpus = gic_init(); > + gic_init(); > make_cpus_ready(cpus, boot_phys_offset); > > percpu_init_areas(); > diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c > index f4fd512..93b7ded 100644 > --- a/xen/arch/arm/smpboot.c > +++ b/xen/arch/arm/smpboot.c > @@ -71,18 +71,31 @@ static void setup_cpu_sibling_map(int cpu) > cpumask_set_cpu(cpu, per_cpu(cpu_core_mask, cpu)); > } > > - > void __init > -smp_prepare_cpus (unsigned int max_cpus) > +smp_clear_cpu_maps (void) > { > - int i; > - > + cpumask_clear(&cpu_possible_map); > cpumask_clear(&cpu_online_map); > cpumask_set_cpu(0, &cpu_online_map); > + cpumask_set_cpu(0, &cpu_possible_map); > +} > > - cpumask_clear(&cpu_possible_map); > - for ( i = 0; i < max_cpus; i++ ) > - cpumask_set_cpu(i, &cpu_possible_map); > +int __init > +smp_get_max_cpus (void) > +{ > + int i, max_cpus = 0; > + > + for ( i = 0; i < nr_cpu_ids; i++ ) > + if ( cpu_possible(i) ) > + max_cpus++; > + > + return max_cpus; > +} > + > + > +void __init > +smp_prepare_cpus (unsigned int max_cpus) > +{ > cpumask_copy(&cpu_present_map, &cpu_possible_map); > > setup_cpu_sibling_map(0); > diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c > index 3d1f0f4..da0af77 100644 > --- a/xen/common/device_tree.c > +++ b/xen/common/device_tree.c > @@ -18,6 +18,7 @@ > #include <xen/mm.h> > #include <xen/stdarg.h> > #include <xen/string.h> > +#include <xen/cpumask.h> > #include <asm/early_printk.h> > > struct dt_early_info __initdata early_info; > @@ -41,6 +42,18 @@ bool_t device_tree_node_matches(const void *fdt, int node, const char *match) > && (name[match_len] == ''@'' || name[match_len] == ''\0''); > } > > +bool_t device_tree_type_matches(const void *fdt, int node, const char *match) > +{ > + int len; > + const void *prop; > + > + prop = fdt_getprop(fdt, node, "device_type", &len); > + if ( prop == NULL ) > + return 0; > + > + return !strncmp(prop, match, len); > +} > + > static void __init get_val(const u32 **cell, u32 cells, u64 *val) > { > *val = 0; > @@ -229,6 +242,34 @@ static void __init process_memory_node(const void *fdt, int node, > } > } > > +static void __init process_cpu_node(const void *fdt, int node, > + const char *name, > + u32 address_cells, u32 size_cells) > +{ > + const struct fdt_property *prop; > + const u32 *cell; > + paddr_t start, size; > + > + if ( address_cells != 1 || size_cells != 0 ) > + { > + early_printk("fdt: node `%s'': invalid #address-cells or #size-cells", > + name); > + return; > + } > + > + prop = fdt_get_property(fdt, node, "reg", NULL); > + if ( !prop ) > + { > + early_printk("fdt: node `%s'': missing `reg'' property\n", name); > + return; > + } > + > + cell = (const u32 *)prop->data; > + device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); > + > + cpumask_set_cpu(start, &cpu_possible_map); > +} > + > static int __init early_scan_node(const void *fdt, > int node, const char *name, int depth, > u32 address_cells, u32 size_cells, > @@ -236,6 +277,8 @@ static int __init early_scan_node(const void *fdt, > { > if ( device_tree_node_matches(fdt, node, "memory") ) > process_memory_node(fdt, node, name, address_cells, size_cells); > + else if ( device_tree_type_matches(fdt, node, "cpu") ) > + process_cpu_node(fdt, node, name, address_cells, size_cells); > > return 0; > } > diff --git a/xen/include/asm-arm/smp.h b/xen/include/asm-arm/smp.h > index a98032d..d4ed1cb 100644 > --- a/xen/include/asm-arm/smp.h > +++ b/xen/include/asm-arm/smp.h > @@ -22,6 +22,8 @@ extern void stop_cpu(void); > extern void > make_cpus_ready(unsigned int max_cpus, unsigned long boot_phys_offset); > > +extern void smp_clear_cpu_maps (void); > +extern int smp_get_max_cpus (void); > #endif > /* > * Local variables: