@@ -41,6 +41,9 @@ extern unsigned int debug_smp_processor_id(void); /* from linux/smp.h */
#define get_lppaca() (get_paca()->lppaca_ptr)
#define get_slb_shadow() (get_paca()->slb_shadow_ptr)
+/* Maximum number of threads per core. */
+#define MAX_SMT 8
+
struct task_struct;
/*
@@ -30,6 +30,7 @@
#include <asm/percpu.h>
extern int boot_cpuid;
+extern int boot_hw_cpuid;
extern int spinning_secondaries;
extern void cpu_die(void);
@@ -206,6 +206,7 @@ void __init allocate_pacas(void)
{
u64 limit;
int cpu;
+ int nr_cpus;
limit = ppc64_rma_size;
@@ -218,20 +219,32 @@ void __init allocate_pacas(void)
limit = min(0x10000000ULL, limit);
#endif
- paca_size = PAGE_ALIGN(sizeof(struct paca_struct) * nr_cpu_ids);
+ /*
+ * Always align up the nr_cpu_ids to SMT threads and allocate
+ * the paca. This will help us to prepare for a situation where
+ * boot cpu id > nr_cpus_id. We will use the last nthreads
+ * slots (nthreads == threads per core) to accommodate a core
+ * that contains boot cpu thread.
+ *
+ * Do not change nr_cpu_ids value here. Let us do that in
+ * early_init_dt_scan_cpus() where we know exact value
+ * of threads per core.
+ */
+ nr_cpus = _ALIGN_UP(nr_cpu_ids, MAX_SMT);
+ paca_size = PAGE_ALIGN(sizeof(struct paca_struct) * nr_cpus);
paca = __va(memblock_alloc_base(paca_size, PAGE_SIZE, limit));
memset(paca, 0, paca_size);
printk(KERN_DEBUG "Allocated %u bytes for %d pacas at %p\n",
- paca_size, nr_cpu_ids, paca);
+ paca_size, nr_cpus, paca);
- allocate_lppacas(nr_cpu_ids, limit);
+ allocate_lppacas(nr_cpus, limit);
- allocate_slb_shadows(nr_cpu_ids, limit);
+ allocate_slb_shadows(nr_cpus, limit);
/* Can't use for_each_*_cpu, as they aren't functional yet */
- for (cpu = 0; cpu < nr_cpu_ids; cpu++)
+ for (cpu = 0; cpu < nr_cpus; cpu++)
initialise_paca(&paca[cpu], cpu);
}
@@ -291,6 +291,29 @@ static void __init check_cpu_feature_properties(unsigned long node)
}
}
+/*
+ * Adjust the logical id of a boot cpu to fall under nr_cpu_ids. Map it to
+ * last core slot in the allocated paca array.
+ *
+ * e.g. on SMT=8 system, kernel booted with nr_cpus=1 and boot cpu = 33,
+ * align nr_cpu_ids to MAX_SMT value 8. Allocate paca array to hold up-to
+ * MAX_SMT=8 cpus. Since boot cpu 33 is greater than nr_cpus (8), adjust
+ * its logical id so that new id becomes less than nr_cpu_ids. Make sure
+ * that boot cpu's new logical id is aligned to its thread id and falls
+ * under last nthreads slots available in paca array. In this case the
+ * boot cpu 33 is adjusted to new boot cpu id 1.
+ *
+ */
+static inline void adjust_boot_cpuid(int nthreads, int phys_id)
+{
+ boot_hw_cpuid = phys_id;
+ if (boot_cpuid >= nr_cpu_ids) {
+ boot_cpuid = (boot_cpuid % nthreads) + (nr_cpu_ids - nthreads);
+ pr_info("Adjusted logical boot cpu id: logical %d physical %d\n",
+ boot_cpuid, phys_id);
+ }
+}
+
static int __init early_init_dt_scan_cpus(unsigned long node,
const char *uname, int depth,
void *data)
@@ -314,6 +337,18 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
nthreads = len / sizeof(int);
+#ifdef CONFIG_SMP
+ /*
+ * Now that we know threads per core lets align nr_cpu_ids to
+ * correct SMT value.
+ */
+ if (nr_cpu_ids % nthreads) {
+ nr_cpu_ids = _ALIGN_UP(nr_cpu_ids, nthreads);
+ pr_info("Aligned nr_cpus to SMT=%d, nr_cpu_ids = %d\n",
+ nthreads, nr_cpu_ids);
+ }
+#endif
+
/*
* Now see if any of these threads match our boot cpu.
* NOTE: This must match the parsing done in smp_setup_cpu_maps.
@@ -352,7 +387,9 @@ 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;
- set_hard_smp_processor_id(found, be32_to_cpu(intserv[found_thread]));
+ adjust_boot_cpuid(nthreads, be32_to_cpu(intserv[found_thread]));
+ set_hard_smp_processor_id(boot_cpuid,
+ be32_to_cpu(intserv[found_thread]));
/*
* PAPR defines "logical" PVR values for cpus that
@@ -77,6 +77,7 @@ struct machdep_calls *machine_id;
EXPORT_SYMBOL(machine_id);
int boot_cpuid = -1;
+int boot_hw_cpuid = -1;
EXPORT_SYMBOL_GPL(boot_cpuid);
unsigned long klimit = (unsigned long) _end;
@@ -444,6 +445,7 @@ void __init smp_setup_cpu_maps(void)
struct device_node *dn = NULL;
int cpu = 0;
int nthreads = 1;
+ bool boot_cpu_added = false;
DBG("smp_setup_cpu_maps()\n");
@@ -470,6 +472,24 @@ void __init smp_setup_cpu_maps(void)
}
nthreads = len / sizeof(int);
+ /*
+ * If boot cpu hasn't been added to paca and there are only
+ * last nthreads slots available in paca array then wait
+ * for boot cpu to show up.
+ */
+ if (!boot_cpu_added && (cpu + nthreads) >= nr_cpu_ids) {
+ int found = 0;
+
+ DBG("Holding last nthreads paca slots for boot cpu\n");
+ for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
+ if (boot_hw_cpuid == be32_to_cpu(intserv[j])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ }
for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
bool avail;
@@ -485,6 +505,11 @@ void __init smp_setup_cpu_maps(void)
set_cpu_present(cpu, avail);
set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
set_cpu_possible(cpu, true);
+ if (boot_hw_cpuid == be32_to_cpu(intserv[j])) {
+ DBG("Boot cpu %d (hard id %d) added to paca\n",
+ cpu, be32_to_cpu(intserv[j]));
+ boot_cpu_added = true;
+ }
cpu++;
}
}