@@ -133,6 +133,28 @@ enum cpuhp_state {
CPUHP_MIPS_SOC_PREPARE,
CPUHP_BP_PREPARE_DYN,
CPUHP_BP_PREPARE_DYN_END = CPUHP_BP_PREPARE_DYN + 20,
+ /*
+ * This is an optional state if the architecture supports parallel
+ * startup. It's used to start bringing the CPU online (e.g. send
+ * the startup IPI) so that the APs can run in parallel through
+ * the low level startup code instead of waking them one by one in
+ * CPUHP_BRINGUP_CPU. This avoids waiting for the AP to react and
+ * shortens the serialized phase of the bringup.
+ *
+ * If the architecture registers this state, all APs will be taken
+ * to it (and thus through all prior states) before any is taken
+ * to the subsequent CPUHP_BRINGUP_CPU state.
+ */
+ CPUHP_BP_PARALLEL_STARTUP,
+
+ /*
+ * This step brings the AP online and takes it to the point where it
+ * manages its own state from here on. For the time being, the rest
+ * of the AP bringup is fully serialized despite running on the AP.
+ * If the architecture doesn't use the CPUHP_BP_PARALLEL_STARTUP
+ * state, this step also does all the work of bringing the CPU
+ * online.
+ */
CPUHP_BRINGUP_CPU,
/*
@@ -1504,13 +1504,45 @@ int bringup_hibernate_cpu(unsigned int sleep_cpu)
void bringup_nonboot_cpus(unsigned int setup_max_cpus)
{
- unsigned int cpu;
+ unsigned int cpu, n = num_online_cpus();
+ /*
+ * On architectures which have setup the CPUHP_BP_PARALLEL_STARTUP
+ * state, this invokes all BP prepare states and the parallel
+ * startup state sends the startup IPI to each of the to be onlined
+ * APs. This avoids waiting for each AP to respond to the startup
+ * IPI in CPUHP_BRINGUP_CPU. The APs proceed through the low level
+ * bringup code and then wait for the control CPU to release them
+ * one by one for the final onlining procedure in the loop below.
+ *
+ * For architectures which do not support parallel bringup all
+ * states are fully serialized in the loop below.
+ */
+ if (!cpuhp_step_empty(true, cpuhp_get_step(CPUHP_BP_PARALLEL_STARTUP))) {
+ for_each_present_cpu(cpu) {
+ if (n++ >= setup_max_cpus)
+ break;
+ cpu_up(cpu, CPUHP_BP_PARALLEL_STARTUP);
+ }
+ }
+
+ /* Do the per CPU serialized bringup to ONLINE state */
for_each_present_cpu(cpu) {
if (num_online_cpus() >= setup_max_cpus)
break;
- if (!cpu_online(cpu))
- cpu_up(cpu, CPUHP_ONLINE);
+
+ if (!cpu_online(cpu)) {
+ struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
+ int ret = cpu_up(cpu, CPUHP_ONLINE);
+
+ /*
+ * Due to the above preparation loop a failed online attempt
+ * might have only rolled back to CPUHP_BP_PARALLEL_STARTUP. Do the
+ * remaining cleanups. NOOP for the non parallel case.
+ */
+ if (ret && can_rollback_cpu(st))
+ WARN_ON(cpuhp_invoke_callback_range(false, cpu, st, CPUHP_OFFLINE));
+ }
}
}