Subject: [PATCHv10 3/3] powerpc/smp: Allow hole in paca_ptrs to accommodate boot_cpu

From: Pingfan Liu <[email-masked]>
Date: Wed, 27 Dec 2023 10:41:47 +0800

From: Pingfan Liu <[email-masked]>

This patch always forces the first core onlined due to some subsystem
needs cpu0. After core0, a hole may follow, then comes the crashed core.

Signed-off-by: Pingfan Liu <[email-masked]>
Cc: Michael Ellerman <[email-masked]>
Cc: Nicholas Piggin <[email-masked]>
Cc: Christophe Leroy <[email-masked]>
Cc: Mahesh Salgaonkar <[email-masked]>
Cc: Wen Xiong <[email-masked]>
Cc: Baoquan He <[email-masked]>
Cc: Ming Lei <[email-masked]>
Cc: Sourabh Jain <[email-masked]>
Cc: Hari Bathini <[email-masked]>
Cc: [email-masked]
To: [email-masked]
---
 arch/powerpc/include/asm/smp.h     |  1 +
 arch/powerpc/kernel/paca.c         |  7 +++++--
 arch/powerpc/kernel/prom.c         |  6 ++++++
 arch/powerpc/kernel/setup-common.c | 24 ++++++++++++++++++++----
 4 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index aaaa576d0e15..f01c7891b0d7 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -27,6 +27,7 @@
 
 extern int boot_cpuid;
 extern int boot_cpu_hwid; /* PPC64 only */
+extern int threads_in_core;
 extern int spinning_secondaries;
 extern u32 *cpu_to_phys_id;
 extern bool coregroup_enabled;
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c
index 840c74dd17d6..1fe0fd2a6021 100644
--- a/arch/powerpc/kernel/paca.c
+++ b/arch/powerpc/kernel/paca.c
@@ -242,9 +242,12 @@ static int __initdata paca_struct_size;
 
 void __init allocate_paca_ptrs(void)
 {
-	paca_last_cpu_num = nr_cpu_ids;
+	unsigned int cnt;
 
-	paca_ptrs_size = sizeof(struct paca_struct *) * paca_last_cpu_num;
+	/* paca_ptrs should be big enough to hold boot cpu */
+	cnt = max((unsigned int)ALIGN(boot_cpuid + 1, threads_in_core), nr_cpu_ids);
+	paca_last_cpu_num = cnt;
+	paca_ptrs_size = sizeof(struct paca_struct *) * cnt;
 	paca_ptrs = memblock_alloc_raw(paca_ptrs_size, SMP_CACHE_BYTES);
 	if (!paca_ptrs)
 		panic("Failed to allocate %d bytes for paca pointers\n",
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 0b5878c3125b..e1a671156941 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -371,9 +371,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 	DBG("boot cpu: logical %d physical %d\n", found,
 	    be32_to_cpu(intserv[found_thread]));
 	boot_cpuid = found;
+	/* This forces all threads in a core to be onlined */
+	set_nr_cpu_ids(ALIGN(nr_cpu_ids, nthreads));
+	/* Core 0 is always onlined and assure enough room for boot core */
+	if (nthreads -1 < boot_cpuid && nr_cpu_ids < 2 * nthreads)
+		set_nr_cpu_ids(2 * nthreads);
 
 	if (IS_ENABLED(CONFIG_PPC64))
 		boot_cpu_hwid = be32_to_cpu(intserv[found_thread]);
+	threads_in_core = nthreads;
 
 	/*
 	 * PAPR defines "logical" PVR values for cpus that
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index f9f5f313abf0..b70474e1b5fe 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -86,6 +86,7 @@ EXPORT_SYMBOL(machine_id);
 
 int boot_cpuid = -1;
 EXPORT_SYMBOL_GPL(boot_cpuid);
+int __initdata threads_in_core = 1;
 
 #ifdef CONFIG_PPC64
 int boot_cpu_hwid = -1;
@@ -448,8 +449,9 @@ u32 *cpu_to_phys_id = NULL;
 void __init smp_setup_cpu_maps(void)
 {
 	struct device_node *dn;
-	int cpu = 0;
+	int cpu_onlined = 0, cpu = 0;
 	int nthreads = 1;
+	bool bootcpu_covered = false;
 
 	DBG("smp_setup_cpu_maps()\n");
 
@@ -484,7 +486,19 @@ void __init smp_setup_cpu_maps(void)
 
 		nthreads = len / sizeof(int);
 
-		for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
+		if (!bootcpu_covered) {
+			if (cpu == ALIGN_DOWN(boot_cpuid, nthreads)) {
+				bootcpu_covered = true;
+				goto scan;
+
+			/* Reserve the last online slot for boot core */
+			} else if (cpu >= nr_cpu_ids - nthreads && !bootcpu_covered) {
+				cpu += nthreads;
+				continue;
+			}
+		}
+scan:
+		for (j = 0; j < nthreads && cpu_onlined < nr_cpu_ids; j++) {
 			bool avail;
 
 			DBG("    thread %d -> cpu %d (hard id %d)\n",
@@ -499,9 +513,10 @@ void __init smp_setup_cpu_maps(void)
 			set_cpu_possible(cpu, true);
 			cpu_to_phys_id[cpu] = be32_to_cpu(intserv[j]);
 			cpu++;
+			cpu_onlined++;
 		}
 
-		if (cpu >= nr_cpu_ids) {
+		if (cpu_onlined >= nr_cpu_ids) {
 			of_node_put(dn);
 			break;
 		}
@@ -547,7 +562,8 @@ void __init smp_setup_cpu_maps(void)
 			printk(KERN_INFO "Partition configured for %d cpus.\n",
 			       maxcpus);
 
-		for (cpu = 0; cpu < maxcpus; cpu++)
+		/* Bits below #cpu have been set */
+		for (; cpu < maxcpus; cpu++)
 			set_cpu_possible(cpu, true);
 	out:
 		of_node_put(dn);
-- 
2.31.1