If the platform registers these states, bring all CPUs to each registered
state in parallel, before the final bringup to CPUHP_BRINGUP_CPU.

Signed-off-by: David Woodhouse <[email protected]>
---
 include/linux/cpuhotplug.h |  2 ++
 kernel/cpu.c               | 27 +++++++++++++++++++++++++--
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 0042ef362511..ae07cd1dafe3 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -92,6 +92,8 @@ enum cpuhp_state {
        CPUHP_MIPS_SOC_PREPARE,
        CPUHP_BP_PREPARE_DYN,
        CPUHP_BP_PREPARE_DYN_END                = CPUHP_BP_PREPARE_DYN + 20,
+       CPUHP_BP_PARALLEL_DYN,
+       CPUHP_BP_PARALLEL_DYN_END               = CPUHP_BP_PARALLEL_DYN + 4,
        CPUHP_BRINGUP_CPU,
        CPUHP_AP_IDLE_DEAD,
        CPUHP_AP_OFFLINE,
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 4e11e91010e1..11f9504b4845 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1335,6 +1335,24 @@ int bringup_hibernate_cpu(unsigned int sleep_cpu)
 void bringup_nonboot_cpus(unsigned int setup_max_cpus)
 {
        unsigned int cpu;
+       int n = setup_max_cpus - num_online_cpus();
+
+       /* ∀ parallel pre-bringup state, bring N CPUs to it */
+       if (n > 0) {
+               enum cpuhp_state st = CPUHP_BP_PARALLEL_DYN;
+
+               while (st <= CPUHP_BP_PARALLEL_DYN_END &&
+                      cpuhp_hp_states[st].name) {
+                       int i = n;
+
+                       for_each_present_cpu(cpu) {
+                               cpu_up(cpu, st);
+                               if (!--i)
+                                       break;
+                       }
+                       st++;
+               }
+       }
 
        for_each_present_cpu(cpu) {
                if (num_online_cpus() >= setup_max_cpus)
@@ -1702,6 +1720,10 @@ static int cpuhp_reserve_state(enum cpuhp_state state)
                step = cpuhp_hp_states + CPUHP_BP_PREPARE_DYN;
                end = CPUHP_BP_PREPARE_DYN_END;
                break;
+       case CPUHP_BP_PARALLEL_DYN:
+               step = cpuhp_hp_states + CPUHP_BP_PARALLEL_DYN;
+               end = CPUHP_BP_PARALLEL_DYN_END;
+               break;
        default:
                return -EINVAL;
        }
@@ -1726,14 +1748,15 @@ static int cpuhp_store_callbacks(enum cpuhp_state 
state, const char *name,
        /*
         * If name is NULL, then the state gets removed.
         *
-        * CPUHP_AP_ONLINE_DYN and CPUHP_BP_PREPARE_DYN are handed out on
+        * CPUHP_AP_ONLINE_DYN and CPUHP_BP_xxxxx_DYN are handed out on
         * the first allocation from these dynamic ranges, so the removal
         * would trigger a new allocation and clear the wrong (already
         * empty) state, leaving the callbacks of the to be cleared state
         * dangling, which causes wreckage on the next hotplug operation.
         */
        if (name && (state == CPUHP_AP_ONLINE_DYN ||
-                    state == CPUHP_BP_PREPARE_DYN)) {
+                    state == CPUHP_BP_PREPARE_DYN ||
+                    state == CPUHP_BP_PARALLEL_DYN)) {
                ret = cpuhp_reserve_state(state);
                if (ret < 0)
                        return ret;
-- 
2.29.2

Reply via email to