Skip to contentSkip to conversation

powerpc/cpu: enable nr_cpus for crash kernel

PATCH In review Pingfan Liu 3 participants 4 messages May 22, 2018 – Jan 30, 2024

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH v6 1/1 powerpc/cpu: enable nr_cpus for crash kernel

For kexec -p, the boot cpu can be not the cpu0, this causes the problem to alloc paca[]. In theory, there is no requirement to assign cpu's logical id as its present seq by device tree. But we have something like cpu_first_thread_sibling(), which makes assumption on the mapping inside a core. Hence partially changing the mapping, i.e. unbind the mapping of core while keep the mapping inside a core. After this patch, the core with boot-cpu will always be mapped into core 0.

And at present, the code to discovery cpu spreads over two functions:
early_init_dt_scan_cpus() and smp_setup_cpu_maps().
This patch tries to fold smp_setup_cpu_maps() into the "previous" one

---
v5 -> v6:
simplify the loop logic (Hope it can answer Benjamin's concern) concentrate the cpu recovery code to early stage (Hope it can answer Michael's concern)
Todo: (if this method is accepted)
fold the whole smp_setup_cpu_maps()

 arch/powerpc/include/asm/smp.h     |   1 +
 arch/powerpc/kernel/prom.c         | 123 ++++++++++++++++++++++++++++---------
 arch/powerpc/kernel/setup-common.c |  58 ++---------------
 drivers/of/fdt.c                   |   2 +-
 include/linux/of_fdt.h             |   2 +
 5 files changed, 103 insertions(+), 83 deletions(-)
arch/powerpc/include/asm/smp.h +1 -0
index fac963e..80c7693 100644
@@ -30,6 +30,7 @@
 #include <asm/percpu.h>
 
 extern int boot_cpuid;
+extern int threads_in_core;
 extern int spinning_secondaries;
 
 extern void cpu_die(void);
arch/powerpc/kernel/prom.c +94 -29

189 lines not on this page · raw message

arch/powerpc/kernel/setup-common.c +5 -53

87 lines not on this page · raw message

drivers/of/fdt.c +1 -1
index 84aa9d6..16d6b02 100644
@@ -130,7 +130,7 @@ bool of_fdt_is_big_endian(const void *blob, unsigned long node)
 	return false;
 }
 
-static bool of_fdt_device_is_available(const void *blob, unsigned long node)
+bool of_fdt_device_is_available(const void *blob, unsigned long node)
 {
 	const char *status = fdt_getprop(blob, node, "status", NULL);
 
include/linux/of_fdt.h +2 -0
index b9cd9eb..28756c5 100644
@@ -30,6 +30,8 @@ extern void *of_fdt_get_property(const void *blob,
 				 int *size);
 extern bool of_fdt_is_big_endian(const void *blob,
 				 unsigned long node);
+extern bool of_fdt_device_is_available(const void *blob,
+			unsigned long node);
 extern int of_fdt_match(const void *blob, unsigned long node,
 			const char *const *compat);
 extern void *of_fdt_unflatten_tree(const unsigned long *blob,
Signature · 1 line
2.7.4
#1 lore raw

I had tested this patch for P9/P8/ guest on powerKVM and powerVM. All of them works fine.

Quoted 364 lines · Pingfan Liu

For kexec -p, the boot cpu can be not the cpu0, this causes the problem to alloc paca[]. In theory, there is no requirement to assign cpu's logical id as its present seq by device tree. But we have something like cpu_first_thread_sibling(), which makes assumption on the mapping inside a core. Hence partially changing the mapping, i.e. unbind the mapping of core while keep the mapping inside a core. After this patch, the core with boot-cpu will always be mapped into core 0.

And at present, the code to discovery cpu spreads over two functions:
early_init_dt_scan_cpus() and smp_setup_cpu_maps().
This patch tries to fold smp_setup_cpu_maps() into the "previous" one

---
v5 -> v6:
simplify the loop logic (Hope it can answer Benjamin's concern) concentrate the cpu recovery code to early stage (Hope it can answer Michael's concern)
Todo: (if this method is accepted)
fold the whole smp_setup_cpu_maps()

 arch/powerpc/include/asm/smp.h     |   1 +
 arch/powerpc/kernel/prom.c         | 123 ++++++++++++++++++++++++++++---------
 arch/powerpc/kernel/setup-common.c |  58 ++---------------
 drivers/of/fdt.c                   |   2 +-
 include/linux/of_fdt.h             |   2 +
 5 files changed, 103 insertions(+), 83 deletions(-)
arch/powerpc/include/asm/smp.h +1 -0
index fac963e..80c7693 100644
@@ -30,6 +30,7 @@
 #include <asm/percpu.h>
 
 extern int boot_cpuid;
+extern int threads_in_core;
 extern int spinning_secondaries;
 
 extern void cpu_die(void);
arch/powerpc/kernel/prom.c +94 -29
index 4922162..2ae0b4a 100644
@@ -77,7 +77,6 @@ unsigned long tce_alloc_start, tce_alloc_end;
 u64 ppc64_rma_size;
 #endif
 static phys_addr_t first_memblock_size;
-static int __initdata boot_cpu_count;
 
 static int __init early_parse_mem(char *p)
 {
@@ -305,6 +304,14 @@ static void __init check_cpu_feature_properties(unsigned long node)
        }
 }
 
+struct bootinfo {
+       int boot_thread_id;
+       unsigned int cpu_cnt;
+       int cpu_hwids[NR_CPUS];
+       bool avail[NR_CPUS];
+};
+static struct bootinfo *bt_info;
+
 static int __init early_init_dt_scan_cpus(unsigned long node,
                                          const char *uname, int depth,
                                          void *data)
@@ -312,10 +319,12 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
        const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
        const __be32 *prop;
        const __be32 *intserv;
-       int i, nthreads;
+       int i, nthreads, maxidx;
        int len;
-       int found = -1;
-       int found_thread = 0;
+       int found_thread = -1;
+       struct bootinfo *info = data;
+       bool avail;
+       int rotate_cnt, id;
 
        /* We are scanning "cpu" nodes only */
        if (type == NULL || strcmp(type, "cpu") != 0)
@@ -325,8 +334,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
        intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len);
        if (!intserv)
                intserv = of_get_flat_dt_prop(node, "reg", &len);
+       avail = of_fdt_device_is_available(initial_boot_params, node);
+#if 0
+       //todo
+       if (!avail)
+               avail = !of_fdt_property_match_string(node,
+                                       "enable-method", "spin-table");
+#endif
 
-       nthreads = len / sizeof(int);
+       threads_in_core = nthreads = len / sizeof(int);
 
        /*
         * Now see if any of these threads match our boot cpu.
@@ -338,9 +354,10 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                 * booted proc.
                 */
                if (fdt_version(initial_boot_params) >= 2) {
+                       info->cpu_hwids[info->cpu_cnt] =
+                                       be32_to_cpu(intserv[i]);
                        if (be32_to_cpu(intserv[i]) ==
                            fdt_boot_cpuid_phys(initial_boot_params)) {
-                               found = boot_cpu_count;
                                found_thread = i;
                        }
                } else {
@@ -351,22 +368,37 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                         */
                        if (of_get_flat_dt_prop(node,
                                        "linux,boot-cpu", NULL) != NULL)
-                               found = boot_cpu_count;
+                               found_thread = info->cpu_cnt;
                }
+               info->avail[info->cpu_cnt] = avail;
+
 #ifdef CONFIG_SMP
                /* logical cpu id is always 0 on UP kernels */
-               boot_cpu_count++;
+               info->cpu_cnt++;
 #endif
        }
 
        /* Not the boot CPU */
-       if (found < 0)
+       if (found_thread < 0)
                return 0;
 
-       DBG("boot cpu: logical %d physical %d\n", found,
+       /* always mapping boot-core to core 0 to cope with kexec -p */
+       maxidx = info->cpu_cnt - 1;
+       rotate_cnt = nthreads;
+       while (rotate_cnt-- > 0) {
+               avail = info->avail[maxidx];
+               id = info->cpu_hwids[maxidx];
+               for (i = maxidx; i > 0; i--) {
+                       info->avail[i] = info->avail[i - 1];
+                       info->cpu_hwids[i] = info->cpu_hwids[i - 1];
+               }
+               info->avail[i] = avail;
+               info->cpu_hwids[i] = id;
+       }
+
+       info->boot_thread_id = found_thread;
+       DBG("boot cpu: logical %d physical %d\n", found_thread,
            be32_to_cpu(intserv[found_thread]));
-       boot_cpuid = found;
-       set_hard_smp_processor_id(found, be32_to_cpu(intserv[found_thread]));
 
        /*
         * PAPR defines "logical" PVR values for cpus that
@@ -675,6 +707,55 @@ static void __init tm_init(void)
 static void tm_init(void) { }
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
+static void early_setup_cpu_mapping(void)
+{
+       unsigned int cpu, cnt;
+       int nr_cpus_aligned;
+
+       bt_info = __va(memblock_alloc(sizeof(struct bootinfo),
+                       sizeof(unsigned long)));
+       memset(bt_info, 0, sizeof(struct bootinfo));
+       bt_info->boot_thread_id = -1;
+       /* Retrieve CPU related informations from the flat tree
+        * (altivec support, boot CPU ID, ...)
+        */
+       of_scan_flat_dt(early_init_dt_scan_cpus, bt_info);
+
+       if (bt_info->boot_thread_id < 0) {
+               pr_err("Failed to identify boot CPU !\n");
+               BUG();
+       }
+
+       boot_cpuid = bt_info->boot_thread_id;
+       /* work around subcore mode */
+       nr_cpus_aligned = _ALIGN_UP(nr_cpu_ids, threads_in_core);
+       if (nr_cpus_aligned != nr_cpu_ids) {
+               pr_info("nr_cpus is forced to be aligned up from: %d to: %d\n",
+                       nr_cpu_ids, nr_cpus_aligned);
+               nr_cpu_ids = nr_cpus_aligned;
+       }
+       cnt = (nr_cpu_ids < bt_info->cpu_cnt) ? nr_cpu_ids
+                       : bt_info->cpu_cnt;
+
+       allocate_pacas();
+       for (cpu = 0; cpu < cnt; cpu++) {
+               set_cpu_present(cpu, bt_info->avail[cpu]);
+               DBG("set cpu present: %d -> hwid:%d\n",
+                       cpu, bt_info->cpu_hwids[cpu]);
+               set_hard_smp_processor_id(cpu, bt_info->cpu_hwids[cpu]);
+               set_cpu_possible(cpu, true);
+       }
+
+#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
+       /* We'll later wait for secondaries to check in; there are
+        * NCPUS-1 non-boot CPUs  :-)
+        */
+       spinning_secondaries = bt_info->cpu_cnt - 1;
+#endif
+       memblock_free(__pa(bt_info), sizeof(struct bootinfo));
+       bt_info = NULL;
+}
+
 void __init early_init_devtree(void *params)
 {
        phys_addr_t limit;
@@ -745,27 +826,11 @@ void __init early_init_devtree(void *params)
         * FIXME .. and the initrd too? */
        move_device_tree();
 
-       allocate_pacas();
-
        DBG("Scanning CPUs ...\n");
 
        dt_cpu_ftrs_scan();
 
-       /* Retrieve CPU related informations from the flat tree
-        * (altivec support, boot CPU ID, ...)
-        */
-       of_scan_flat_dt(early_init_dt_scan_cpus, NULL);
-       if (boot_cpuid < 0) {
-               printk("Failed to identify boot CPU !\n");
-               BUG();
-       }
-
-#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
-       /* We'll later wait for secondaries to check in; there are
-        * NCPUS-1 non-boot CPUs  :-)
-        */
-       spinning_secondaries = boot_cpu_count - 1;
-#endif
+       early_setup_cpu_mapping();
 
        mmu_early_init_devtree();
 
arch/powerpc/kernel/setup-common.c +5 -53
index 66f7cc6..46d034a 100644
@@ -86,7 +86,9 @@ struct machdep_calls *machine_id;
 EXPORT_SYMBOL(machine_id);
 
 int boot_cpuid = -1;
+int threads_in_core = 1;
 EXPORT_SYMBOL_GPL(boot_cpuid);
+EXPORT_SYMBOL_GPL(threads_in_core);
 
 /*
  * These are used in binfmt_elf.c to put aux entries on the stack
@@ -460,61 +462,11 @@ void __init smp_setup_cpu_maps(void)
 {
        struct device_node *dn;
        int cpu = 0;
-       int nthreads = 1;
-
-       DBG("smp_setup_cpu_maps()\n");
-
-       for_each_node_by_type(dn, "cpu") {
-               const __be32 *intserv;
-               __be32 cpu_be;
-               int j, len;
-
-               DBG("  * %pOF...\n", dn);
-
-               intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",
-                               &len);
-               if (intserv) {
-                       DBG("    ibm,ppc-interrupt-server#s -> %d threads\n",
-                           nthreads);
-               } else {
-                       DBG("    no ibm,ppc-interrupt-server#s -> 1 thread\n");
-                       intserv = of_get_property(dn, "reg", &len);
-                       if (!intserv) {
-                               cpu_be = cpu_to_be32(cpu);
-                               intserv = &cpu_be;      /* assume logical == phys */
-                               len = 4;
-                       }
-               }
-
-               nthreads = len / sizeof(int);
-
-               for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
-                       bool avail;
-
-                       DBG("    thread %d -> cpu %d (hard id %d)\n",
-                           j, cpu, be32_to_cpu(intserv[j]));
-
-                       avail = of_device_is_available(dn);
-                       if (!avail)
-                               avail = !of_property_match_string(dn,
-                                               "enable-method", "spin-table");
-
-                       set_cpu_present(cpu, avail);
-                       set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
-                       set_cpu_possible(cpu, true);
-                       cpu++;
-               }
-
-               if (cpu >= nr_cpu_ids) {
-                       of_node_put(dn);
-                       break;
-               }
-       }
 
        /* If no SMT supported, nthreads is forced to 1 */
        if (!cpu_has_feature(CPU_FTR_SMT)) {
                DBG("  SMT disabled ! nthreads forced to 1\n");
-               nthreads = 1;
+               threads_in_core = 1;
        }
 
 #ifdef CONFIG_PPC64
@@ -539,7 +491,7 @@ void __init smp_setup_cpu_maps(void)
 
                /* Double maxcpus for processors which have SMT capability */
                if (cpu_has_feature(CPU_FTR_SMT))
-                       maxcpus *= nthreads;
+                       maxcpus *= threads_in_core;
 
                if (maxcpus > nr_cpu_ids) {
                        printk(KERN_WARNING
@@ -565,7 +517,7 @@ void __init smp_setup_cpu_maps(void)
         * every CPU in the system. If that is not the case, then some code
         * here will have to be reworked
         */
-       cpu_init_thread_core_maps(nthreads);
+       cpu_init_thread_core_maps(threads_in_core);
 
        /* Now that possible cpus are set, set nr_cpu_ids for later use */
        setup_nr_cpu_ids();
drivers/of/fdt.c +1 -1
index 84aa9d6..16d6b02 100644
@@ -130,7 +130,7 @@ bool of_fdt_is_big_endian(const void *blob, unsigned long node)
        return false;
 }
 
-static bool of_fdt_device_is_available(const void *blob, unsigned long node)
+bool of_fdt_device_is_available(const void *blob, unsigned long node)
 {
        const char *status = fdt_getprop(blob, node, "status", NULL);
 
include/linux/of_fdt.h +2 -1
index b9cd9eb..28756c5 100644
@@ -30,6 +30,8 @@ extern void *of_fdt_get_property(const void *blob,
                                 int *size);
 extern bool of_fdt_is_big_endian(const void *blob,
                                 unsigned long node);
+extern bool of_fdt_device_is_available(const void *blob,
+                       unsigned long node);
 extern int of_fdt_match(const void *blob, unsigned long node,
                        const char *const *compat);
 extern void *of_fdt_unflatten_tree(const unsigned long *blob,
--

2.7.4

#2 lore raw
In reply to Pingfan Liu

Hi,

Le 22/05/2018 à 10:23, Pingfan Liu a écrit :

Quoted 11 lines

For kexec -p, the boot cpu can be not the cpu0, this causes the problem to alloc paca[]. In theory, there is no requirement to assign cpu's logical id as its present seq by device tree. But we have something like cpu_first_thread_sibling(), which makes assumption on the mapping inside a core. Hence partially changing the mapping, i.e. unbind the mapping of core while keep the mapping inside a core. After this patch, the core with boot-cpu will always be mapped into core 0.

And at present, the code to discovery cpu spreads over two functions:
early_init_dt_scan_cpus() and smp_setup_cpu_maps().
This patch tries to fold smp_setup_cpu_maps() into the "previous" one

This patch is pretty old and doesn't apply anymore. If still relevant can you please rebase and resubmit.

Thanks
Christophe

Quoted 342 lines

---
v5 -> v6:
simplify the loop logic (Hope it can answer Benjamin's concern) concentrate the cpu recovery code to early stage (Hope it can answer Michael's concern)
Todo: (if this method is accepted)
fold the whole smp_setup_cpu_maps()

  arch/powerpc/include/asm/smp.h     |   1 +
  arch/powerpc/kernel/prom.c         | 123 ++++++++++++++++++++++++++++---------
  arch/powerpc/kernel/setup-common.c |  58 ++---------------
  drivers/of/fdt.c                   |   2 +-
  include/linux/of_fdt.h             |   2 +
  5 files changed, 103 insertions(+), 83 deletions(-)
arch/powerpc/include/asm/smp.h +1 -0
index fac963e..80c7693 100644
@@ -30,6 +30,7 @@
  #include <asm/percpu.h>
 
  extern int boot_cpuid;
+extern int threads_in_core;
  extern int spinning_secondaries;
 
  extern void cpu_die(void);
arch/powerpc/kernel/prom.c +94 -29
index 4922162..2ae0b4a 100644
@@ -77,7 +77,6 @@ unsigned long tce_alloc_start, tce_alloc_end;
  u64 ppc64_rma_size;
  #endif
  static phys_addr_t first_memblock_size;
-static int __initdata boot_cpu_count;
 
  static int __init early_parse_mem(char *p)
  {
@@ -305,6 +304,14 @@ static void __init check_cpu_feature_properties(unsigned long node)
  	}
  }
 
+struct bootinfo {
+	int boot_thread_id;
+	unsigned int cpu_cnt;
+	int cpu_hwids[NR_CPUS];
+	bool avail[NR_CPUS];
+};
+static struct bootinfo *bt_info;
+
  static int __init early_init_dt_scan_cpus(unsigned long node,
  					  const char *uname, int depth,
  					  void *data)
@@ -312,10 +319,12 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
  	const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
  	const __be32 *prop;
  	const __be32 *intserv;
-	int i, nthreads;
+	int i, nthreads, maxidx;
  	int len;
-	int found = -1;
-	int found_thread = 0;
+	int found_thread = -1;
+	struct bootinfo *info = data;
+	bool avail;
+	int rotate_cnt, id;
 
  	/* We are scanning "cpu" nodes only */
  	if (type == NULL || strcmp(type, "cpu") != 0)
@@ -325,8 +334,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
  	intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len);
  	if (!intserv)
  		intserv = of_get_flat_dt_prop(node, "reg", &len);
+	avail = of_fdt_device_is_available(initial_boot_params, node);
+#if 0
+	//todo
+	if (!avail)
+		avail = !of_fdt_property_match_string(node,
+					"enable-method", "spin-table");
+#endif
 
-	nthreads = len / sizeof(int);
+	threads_in_core = nthreads = len / sizeof(int);
 
  	/*
  	 * Now see if any of these threads match our boot cpu.
@@ -338,9 +354,10 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
  		 * booted proc.
  		 */
  		if (fdt_version(initial_boot_params) >= 2) {
+			info->cpu_hwids[info->cpu_cnt] =
+					be32_to_cpu(intserv[i]);
  			if (be32_to_cpu(intserv[i]) ==
  			    fdt_boot_cpuid_phys(initial_boot_params)) {
-				found = boot_cpu_count;
  				found_thread = i;
  			}
  		} else {
@@ -351,22 +368,37 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
  			 */
  			if (of_get_flat_dt_prop(node,
  					"linux,boot-cpu", NULL) != NULL)
-				found = boot_cpu_count;
+				found_thread = info->cpu_cnt;
  		}
+		info->avail[info->cpu_cnt] = avail;
+
  #ifdef CONFIG_SMP
  		/* logical cpu id is always 0 on UP kernels */
-		boot_cpu_count++;
+		info->cpu_cnt++;
  #endif
  	}
 
  	/* Not the boot CPU */
-	if (found < 0)
+	if (found_thread < 0)
  		return 0;
 
-	DBG("boot cpu: logical %d physical %d\n", found,
+	/* always mapping boot-core to core 0 to cope with kexec -p */
+	maxidx = info->cpu_cnt - 1;
+	rotate_cnt = nthreads;
+	while (rotate_cnt-- > 0) {
+		avail = info->avail[maxidx];
+		id = info->cpu_hwids[maxidx];
+		for (i = maxidx; i > 0; i--) {
+			info->avail[i] = info->avail[i - 1];
+			info->cpu_hwids[i] = info->cpu_hwids[i - 1];
+		}
+		info->avail[i] = avail;
+		info->cpu_hwids[i] = id;
+	}
+
+	info->boot_thread_id = found_thread;
+	DBG("boot cpu: logical %d physical %d\n", found_thread,
  	    be32_to_cpu(intserv[found_thread]));
-	boot_cpuid = found;
-	set_hard_smp_processor_id(found, be32_to_cpu(intserv[found_thread]));
 
  	/*
  	 * PAPR defines "logical" PVR values for cpus that
@@ -675,6 +707,55 @@ static void __init tm_init(void)
  static void tm_init(void) { }
  #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
+static void early_setup_cpu_mapping(void)
+{
+	unsigned int cpu, cnt;
+	int nr_cpus_aligned;
+
+	bt_info = __va(memblock_alloc(sizeof(struct bootinfo),
+			sizeof(unsigned long)));
+	memset(bt_info, 0, sizeof(struct bootinfo));
+	bt_info->boot_thread_id = -1;
+	/* Retrieve CPU related informations from the flat tree
+	 * (altivec support, boot CPU ID, ...)
+	 */
+	of_scan_flat_dt(early_init_dt_scan_cpus, bt_info);
+
+	if (bt_info->boot_thread_id < 0) {
+		pr_err("Failed to identify boot CPU !\n");
+		BUG();
+	}
+
+	boot_cpuid = bt_info->boot_thread_id;
+	/* work around subcore mode */
+	nr_cpus_aligned = _ALIGN_UP(nr_cpu_ids, threads_in_core);
+	if (nr_cpus_aligned != nr_cpu_ids) {
+		pr_info("nr_cpus is forced to be aligned up from: %d to: %d\n",
+			nr_cpu_ids, nr_cpus_aligned);
+		nr_cpu_ids = nr_cpus_aligned;
+	}
+	cnt = (nr_cpu_ids < bt_info->cpu_cnt) ? nr_cpu_ids
+			: bt_info->cpu_cnt;
+
+	allocate_pacas();
+	for (cpu = 0; cpu < cnt; cpu++) {
+		set_cpu_present(cpu, bt_info->avail[cpu]);
+		DBG("set cpu present: %d -> hwid:%d\n",
+			cpu, bt_info->cpu_hwids[cpu]);
+		set_hard_smp_processor_id(cpu, bt_info->cpu_hwids[cpu]);
+		set_cpu_possible(cpu, true);
+	}
+
+#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
+	/* We'll later wait for secondaries to check in; there are
+	 * NCPUS-1 non-boot CPUs  :-)
+	 */
+	spinning_secondaries = bt_info->cpu_cnt - 1;
+#endif
+	memblock_free(__pa(bt_info), sizeof(struct bootinfo));
+	bt_info = NULL;
+}
+
  void __init early_init_devtree(void *params)
  {
  	phys_addr_t limit;
@@ -745,27 +826,11 @@ void __init early_init_devtree(void *params)
  	 * FIXME .. and the initrd too? */
  	move_device_tree();
 
-	allocate_pacas();
-
  	DBG("Scanning CPUs ...\n");
 
  	dt_cpu_ftrs_scan();
 
-	/* Retrieve CPU related informations from the flat tree
-	 * (altivec support, boot CPU ID, ...)
-	 */
-	of_scan_flat_dt(early_init_dt_scan_cpus, NULL);
-	if (boot_cpuid < 0) {
-		printk("Failed to identify boot CPU !\n");
-		BUG();
-	}
-
-#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
-	/* We'll later wait for secondaries to check in; there are
-	 * NCPUS-1 non-boot CPUs  :-)
-	 */
-	spinning_secondaries = boot_cpu_count - 1;
-#endif
+	early_setup_cpu_mapping();
 
  	mmu_early_init_devtree();
 
arch/powerpc/kernel/setup-common.c +5 -53
index 66f7cc6..46d034a 100644
@@ -86,7 +86,9 @@ struct machdep_calls *machine_id;
  EXPORT_SYMBOL(machine_id);
 
  int boot_cpuid = -1;
+int threads_in_core = 1;
  EXPORT_SYMBOL_GPL(boot_cpuid);
+EXPORT_SYMBOL_GPL(threads_in_core);
 
  /*
   * These are used in binfmt_elf.c to put aux entries on the stack
@@ -460,61 +462,11 @@ void __init smp_setup_cpu_maps(void)
  {
  	struct device_node *dn;
  	int cpu = 0;
-	int nthreads = 1;
-
-	DBG("smp_setup_cpu_maps()\n");
-
-	for_each_node_by_type(dn, "cpu") {
-		const __be32 *intserv;
-		__be32 cpu_be;
-		int j, len;
-
-		DBG("  * %pOF...\n", dn);
-
-		intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",
-				&len);
-		if (intserv) {
-			DBG("    ibm,ppc-interrupt-server#s -> %d threads\n",
-			    nthreads);
-		} else {
-			DBG("    no ibm,ppc-interrupt-server#s -> 1 thread\n");
-			intserv = of_get_property(dn, "reg", &len);
-			if (!intserv) {
-				cpu_be = cpu_to_be32(cpu);
-				intserv = &cpu_be;	/* assume logical == phys */
-				len = 4;
-			}
-		}
-
-		nthreads = len / sizeof(int);
-
-		for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
-			bool avail;
-
-			DBG("    thread %d -> cpu %d (hard id %d)\n",
-			    j, cpu, be32_to_cpu(intserv[j]));
-
-			avail = of_device_is_available(dn);
-			if (!avail)
-				avail = !of_property_match_string(dn,
-						"enable-method", "spin-table");
-
-			set_cpu_present(cpu, avail);
-			set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
-			set_cpu_possible(cpu, true);
-			cpu++;
-		}
-
-		if (cpu >= nr_cpu_ids) {
-			of_node_put(dn);
-			break;
-		}
-	}
 
  	/* If no SMT supported, nthreads is forced to 1 */
  	if (!cpu_has_feature(CPU_FTR_SMT)) {
  		DBG("  SMT disabled ! nthreads forced to 1\n");
-		nthreads = 1;
+		threads_in_core = 1;
  	}
 
  #ifdef CONFIG_PPC64
@@ -539,7 +491,7 @@ void __init smp_setup_cpu_maps(void)
 
  		/* Double maxcpus for processors which have SMT capability */
  		if (cpu_has_feature(CPU_FTR_SMT))
-			maxcpus *= nthreads;
+			maxcpus *= threads_in_core;
 
  		if (maxcpus > nr_cpu_ids) {
  			printk(KERN_WARNING
@@ -565,7 +517,7 @@ void __init smp_setup_cpu_maps(void)
  	 * every CPU in the system. If that is not the case, then some code
  	 * here will have to be reworked
  	 */
-	cpu_init_thread_core_maps(nthreads);
+	cpu_init_thread_core_maps(threads_in_core);
 
  	/* Now that possible cpus are set, set nr_cpu_ids for later use */
  	setup_nr_cpu_ids();
drivers/of/fdt.c +1 -1
index 84aa9d6..16d6b02 100644
@@ -130,7 +130,7 @@ bool of_fdt_is_big_endian(const void *blob, unsigned long node)
  	return false;
  }
 
-static bool of_fdt_device_is_available(const void *blob, unsigned long node)
+bool of_fdt_device_is_available(const void *blob, unsigned long node)
  {
  	const char *status = fdt_getprop(blob, node, "status", NULL);
 
include/linux/of_fdt.h +0 -0
index b9cd9eb..28756c5 100644

include/linux/of_fdt.h extern void *of_fdt_get_property(const void *blob, · line 37

Quoted 8 lines
include/linux/of_fdt.h +2 -0
@@ -30,6 +30,8 @@ extern void *of_fdt_get_property(const void *blob,
  				 int *size);
  extern bool of_fdt_is_big_endian(const void *blob,
  				 unsigned long node);
+extern bool of_fdt_device_is_available(const void *blob,
+			unsigned long node);
  extern int of_fdt_match(const void *blob, unsigned long node,
  			const char *const *compat);
  extern void *of_fdt_unflatten_tree(const unsigned long *blob,

_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec

#3 lore raw

Hi Christophe,

The latest series is
https://lore.kernel.org/linuxppc-dev/[email protected]/

And Michael has his implement on:
https://lore.kernel.org/all/[email protected]/T/#m46128446bce1095631162a1927415733a3bf0633

Thanks,

Pingfan

Quoted 376 lines · Christophe Leroy

Hi,

Quoted 11 lines · Pingfan Liu

For kexec -p, the boot cpu can be not the cpu0, this causes the problem to alloc paca[]. In theory, there is no requirement to assign cpu's logical id as its present seq by device tree. But we have something like cpu_first_thread_sibling(), which makes assumption on the mapping inside a core. Hence partially changing the mapping, i.e. unbind the mapping of core while keep the mapping inside a core. After this patch, the core with boot-cpu will always be mapped into core 0.

And at present, the code to discovery cpu spreads over two functions:
early_init_dt_scan_cpus() and smp_setup_cpu_maps().
This patch tries to fold smp_setup_cpu_maps() into the "previous" one

This patch is pretty old and doesn't apply anymore. If still relevant can you please rebase and resubmit.

Thanks
Christophe

Quoted 342 lines

---
v5 -> v6:
simplify the loop logic (Hope it can answer Benjamin's concern) concentrate the cpu recovery code to early stage (Hope it can answer Michael's concern)
Todo: (if this method is accepted)
fold the whole smp_setup_cpu_maps()

  arch/powerpc/include/asm/smp.h     |   1 +
  arch/powerpc/kernel/prom.c         | 123 ++++++++++++++++++++++++++++---------
  arch/powerpc/kernel/setup-common.c |  58 ++---------------
  drivers/of/fdt.c                   |   2 +-
  include/linux/of_fdt.h             |   2 +
  5 files changed, 103 insertions(+), 83 deletions(-)
arch/powerpc/include/asm/smp.h +1 -0
index fac963e..80c7693 100644
@@ -30,6 +30,7 @@
  #include <asm/percpu.h>
 
  extern int boot_cpuid;
+extern int threads_in_core;
  extern int spinning_secondaries;
 
  extern void cpu_die(void);
arch/powerpc/kernel/prom.c +94 -29
index 4922162..2ae0b4a 100644
@@ -77,7 +77,6 @@ unsigned long tce_alloc_start, tce_alloc_end;
  u64 ppc64_rma_size;
  #endif
  static phys_addr_t first_memblock_size;
-static int __initdata boot_cpu_count;
 
  static int __init early_parse_mem(char *p)
  {
@@ -305,6 +304,14 @@ static void __init check_cpu_feature_properties(unsigned long node)
      }
  }
 
+struct bootinfo {
+     int boot_thread_id;
+     unsigned int cpu_cnt;
+     int cpu_hwids[NR_CPUS];
+     bool avail[NR_CPUS];
+};
+static struct bootinfo *bt_info;
+
  static int __init early_init_dt_scan_cpus(unsigned long node,
                                        const char *uname, int depth,
                                        void *data)
@@ -312,10 +319,12 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
      const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
      const __be32 *prop;
      const __be32 *intserv;
-     int i, nthreads;
+     int i, nthreads, maxidx;
      int len;
-     int found = -1;
-     int found_thread = 0;
+     int found_thread = -1;
+     struct bootinfo *info = data;
+     bool avail;
+     int rotate_cnt, id;
 
      /* We are scanning "cpu" nodes only */
      if (type == NULL || strcmp(type, "cpu") != 0)
@@ -325,8 +334,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
      intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len);
      if (!intserv)
              intserv = of_get_flat_dt_prop(node, "reg", &len);
+     avail = of_fdt_device_is_available(initial_boot_params, node);
+#if 0
+     //todo
+     if (!avail)
+             avail = !of_fdt_property_match_string(node,
+                                     "enable-method", "spin-table");
+#endif
 
-     nthreads = len / sizeof(int);
+     threads_in_core = nthreads = len / sizeof(int);
 
      /*
       * Now see if any of these threads match our boot cpu.
@@ -338,9 +354,10 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
               * booted proc.
               */
              if (fdt_version(initial_boot_params) >= 2) {
+                     info->cpu_hwids[info->cpu_cnt] =
+                                     be32_to_cpu(intserv[i]);
                      if (be32_to_cpu(intserv[i]) ==
                          fdt_boot_cpuid_phys(initial_boot_params)) {
-                             found = boot_cpu_count;
                              found_thread = i;
                      }
              } else {
@@ -351,22 +368,37 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                       */
                      if (of_get_flat_dt_prop(node,
                                      "linux,boot-cpu", NULL) != NULL)
-                             found = boot_cpu_count;
+                             found_thread = info->cpu_cnt;
              }
+             info->avail[info->cpu_cnt] = avail;
+
  #ifdef CONFIG_SMP
              /* logical cpu id is always 0 on UP kernels */
-             boot_cpu_count++;
+             info->cpu_cnt++;
  #endif
      }
 
      /* Not the boot CPU */
-     if (found < 0)
+     if (found_thread < 0)
              return 0;
 
-     DBG("boot cpu: logical %d physical %d\n", found,
+     /* always mapping boot-core to core 0 to cope with kexec -p */
+     maxidx = info->cpu_cnt - 1;
+     rotate_cnt = nthreads;
+     while (rotate_cnt-- > 0) {
+             avail = info->avail[maxidx];
+             id = info->cpu_hwids[maxidx];
+             for (i = maxidx; i > 0; i--) {
+                     info->avail[i] = info->avail[i - 1];
+                     info->cpu_hwids[i] = info->cpu_hwids[i - 1];
+             }
+             info->avail[i] = avail;
+             info->cpu_hwids[i] = id;
+     }
+
+     info->boot_thread_id = found_thread;
+     DBG("boot cpu: logical %d physical %d\n", found_thread,
          be32_to_cpu(intserv[found_thread]));
-     boot_cpuid = found;
-     set_hard_smp_processor_id(found, be32_to_cpu(intserv[found_thread]));
 
      /*
       * PAPR defines "logical" PVR values for cpus that
@@ -675,6 +707,55 @@ static void __init tm_init(void)
  static void tm_init(void) { }
  #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
+static void early_setup_cpu_mapping(void)
+{
+     unsigned int cpu, cnt;
+     int nr_cpus_aligned;
+
+     bt_info = __va(memblock_alloc(sizeof(struct bootinfo),
+                     sizeof(unsigned long)));
+     memset(bt_info, 0, sizeof(struct bootinfo));
+     bt_info->boot_thread_id = -1;
+     /* Retrieve CPU related informations from the flat tree
+      * (altivec support, boot CPU ID, ...)
+      */
+     of_scan_flat_dt(early_init_dt_scan_cpus, bt_info);
+
+     if (bt_info->boot_thread_id < 0) {
+             pr_err("Failed to identify boot CPU !\n");
+             BUG();
+     }
+
+     boot_cpuid = bt_info->boot_thread_id;
+     /* work around subcore mode */
+     nr_cpus_aligned = _ALIGN_UP(nr_cpu_ids, threads_in_core);
+     if (nr_cpus_aligned != nr_cpu_ids) {
+             pr_info("nr_cpus is forced to be aligned up from: %d to: %d\n",
+                     nr_cpu_ids, nr_cpus_aligned);
+             nr_cpu_ids = nr_cpus_aligned;
+     }
+     cnt = (nr_cpu_ids < bt_info->cpu_cnt) ? nr_cpu_ids
+                     : bt_info->cpu_cnt;
+
+     allocate_pacas();
+     for (cpu = 0; cpu < cnt; cpu++) {
+             set_cpu_present(cpu, bt_info->avail[cpu]);
+             DBG("set cpu present: %d -> hwid:%d\n",
+                     cpu, bt_info->cpu_hwids[cpu]);
+             set_hard_smp_processor_id(cpu, bt_info->cpu_hwids[cpu]);
+             set_cpu_possible(cpu, true);
+     }
+
+#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
+     /* We'll later wait for secondaries to check in; there are
+      * NCPUS-1 non-boot CPUs  :-)
+      */
+     spinning_secondaries = bt_info->cpu_cnt - 1;
+#endif
+     memblock_free(__pa(bt_info), sizeof(struct bootinfo));
+     bt_info = NULL;
+}
+
  void __init early_init_devtree(void *params)
  {
      phys_addr_t limit;
@@ -745,27 +826,11 @@ void __init early_init_devtree(void *params)
       * FIXME .. and the initrd too? */
      move_device_tree();
 
-     allocate_pacas();
-
      DBG("Scanning CPUs ...\n");
 
      dt_cpu_ftrs_scan();
 
-     /* Retrieve CPU related informations from the flat tree
-      * (altivec support, boot CPU ID, ...)
-      */
-     of_scan_flat_dt(early_init_dt_scan_cpus, NULL);
-     if (boot_cpuid < 0) {
-             printk("Failed to identify boot CPU !\n");
-             BUG();
-     }
-
-#if defined(CONFIG_SMP) && defined(CONFIG_PPC64)
-     /* We'll later wait for secondaries to check in; there are
-      * NCPUS-1 non-boot CPUs  :-)
-      */
-     spinning_secondaries = boot_cpu_count - 1;
-#endif
+     early_setup_cpu_mapping();
 
      mmu_early_init_devtree();
 
arch/powerpc/kernel/setup-common.c +5 -53
index 66f7cc6..46d034a 100644
@@ -86,7 +86,9 @@ struct machdep_calls *machine_id;
  EXPORT_SYMBOL(machine_id);
 
  int boot_cpuid = -1;
+int threads_in_core = 1;
  EXPORT_SYMBOL_GPL(boot_cpuid);
+EXPORT_SYMBOL_GPL(threads_in_core);
 
  /*
   * These are used in binfmt_elf.c to put aux entries on the stack
@@ -460,61 +462,11 @@ void __init smp_setup_cpu_maps(void)
  {
      struct device_node *dn;
      int cpu = 0;
-     int nthreads = 1;
-
-     DBG("smp_setup_cpu_maps()\n");
-
-     for_each_node_by_type(dn, "cpu") {
-             const __be32 *intserv;
-             __be32 cpu_be;
-             int j, len;
-
-             DBG("  * %pOF...\n", dn);
-
-             intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",
-                             &len);
-             if (intserv) {
-                     DBG("    ibm,ppc-interrupt-server#s -> %d threads\n",
-                         nthreads);
-             } else {
-                     DBG("    no ibm,ppc-interrupt-server#s -> 1 thread\n");
-                     intserv = of_get_property(dn, "reg", &len);
-                     if (!intserv) {
-                             cpu_be = cpu_to_be32(cpu);
-                             intserv = &cpu_be;      /* assume logical == phys */
-                             len = 4;
-                     }
-             }
-
-             nthreads = len / sizeof(int);
-
-             for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
-                     bool avail;
-
-                     DBG("    thread %d -> cpu %d (hard id %d)\n",
-                         j, cpu, be32_to_cpu(intserv[j]));
-
-                     avail = of_device_is_available(dn);
-                     if (!avail)
-                             avail = !of_property_match_string(dn,
-                                             "enable-method", "spin-table");
-
-                     set_cpu_present(cpu, avail);
-                     set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
-                     set_cpu_possible(cpu, true);
-                     cpu++;
-             }
-
-             if (cpu >= nr_cpu_ids) {
-                     of_node_put(dn);
-                     break;
-             }
-     }
 
      /* If no SMT supported, nthreads is forced to 1 */
      if (!cpu_has_feature(CPU_FTR_SMT)) {
              DBG("  SMT disabled ! nthreads forced to 1\n");
-             nthreads = 1;
+             threads_in_core = 1;
      }
 
  #ifdef CONFIG_PPC64
@@ -539,7 +491,7 @@ void __init smp_setup_cpu_maps(void)
 
              /* Double maxcpus for processors which have SMT capability */
              if (cpu_has_feature(CPU_FTR_SMT))
-                     maxcpus *= nthreads;
+                     maxcpus *= threads_in_core;
 
              if (maxcpus > nr_cpu_ids) {
                      printk(KERN_WARNING
@@ -565,7 +517,7 @@ void __init smp_setup_cpu_maps(void)
       * every CPU in the system. If that is not the case, then some code
       * here will have to be reworked
       */
-     cpu_init_thread_core_maps(nthreads);
+     cpu_init_thread_core_maps(threads_in_core);
 
      /* Now that possible cpus are set, set nr_cpu_ids for later use */
      setup_nr_cpu_ids();
drivers/of/fdt.c +1 -1
index 84aa9d6..16d6b02 100644
@@ -130,7 +130,7 @@ bool of_fdt_is_big_endian(const void *blob, unsigned long node)
      return false;
  }
 
-static bool of_fdt_device_is_available(const void *blob, unsigned long node)
+bool of_fdt_device_is_available(const void *blob, unsigned long node)
  {
      const char *status = fdt_getprop(blob, node, "status", NULL);
 
include/linux/of_fdt.h +0 -0
index b9cd9eb..28756c5 100644

include/linux/of_fdt.h extern void *of_fdt_get_property(const void *blob, · line 37

Quoted 8 lines
include/linux/of_fdt.h +2 -0
@@ -30,6 +30,8 @@ extern void *of_fdt_get_property(const void *blob,
                               int *size);
  extern bool of_fdt_is_big_endian(const void *blob,
                               unsigned long node);
+extern bool of_fdt_device_is_available(const void *blob,
+                     unsigned long node);
  extern int of_fdt_match(const void *blob, unsigned long node,
                      const char *const *compat);
  extern void *of_fdt_unflatten_tree(const unsigned long *blob,

_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec