]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
cpu/hotplug: Provide a split up CPUHP_BRINGUP mechanism
authorThomas Gleixner <tglx@linutronix.de>
Wed, 29 Mar 2023 20:13:18 +0000 (22:13 +0200)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 30 Mar 2023 12:27:07 +0000 (14:27 +0200)
The bringup logic of a to be onlined CPU consists of several parts, which
are considered to be one atomic hotplug state:

  1) Control CPU issues the wakeup

  2) To be onlined CPU starts up, does the minimal initialization,
     reports to be alive and waits for release into the complete bringup.

  3) Control CPU waits for the alive report and releases the upcoming CPU
     for the complete bringup.

Provide a mechnaism to split this bringup into two states:

  1) Control CPU issues the wakeup

     The be onlined CPU starts up, does the minimal initialization, reports
     to be alive and waits for release into the complete bringup.

  2) Control CPU waits for the alive report and releases the upcoming CPU
     for the complete bringup.

This allows that the two states can be split up to run all to be onlined
CPUs up to state #1 and then at a later point run state #2. This allows to
avoid some of the latencies of the full serialized per CPU bringup by
avoiding the per CPU wakeup/wait serialization.

To enable this the architecture must support the CPU hotplug core sync
mechanism and has to be audited that there are no implicit state
dependencies which require a full atomic bringup.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/Kconfig
include/linux/cpuhotplug.h
kernel/cpu.c

index acd39cdbb8ad30ee7e1b90107d83c059f23b11fa..4bef497ab3aeee53f0b9bfa1ec33ba163a858df3 100644 (file)
@@ -37,6 +37,10 @@ config HOTPLUG_SMT
 config HOTPLUG_CORE_SYNC
        bool
 
+config HOTPLUG_SPLIT_STARTUP
+       bool
+       select HOTPLUG_CORE_SYNC
+
 config GENERIC_ENTRY
        bool
 
index 46281352bba409c24b80ee6474c50f3a7683147c..d63a16425112dc4b3b80ae0e48a9d3cf86ee3cdd 100644 (file)
@@ -134,6 +134,7 @@ enum cpuhp_state {
        CPUHP_BP_PREPARE_DYN,
        CPUHP_BP_PREPARE_DYN_END                = CPUHP_BP_PREPARE_DYN + 20,
 
+       CPUHP_BP_KICK_AP,
        CPUHP_BRINGUP_CPU,
 
        /*
@@ -520,8 +521,11 @@ void cpuhp_online_idle(enum cpuhp_state state);
 static inline void cpuhp_online_idle(enum cpuhp_state state) { }
 #endif
 
+struct task_struct;
+
 void cpuhp_ap_sync_alive(void);
 void cpuhp_ap_report_dead(void);
+int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle);
 void arch_cpuhp_cleanup_kick_cpu(unsigned int cpu);
 void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu);
 
index 4b814c1fe04c4c6a6f792a067bb01671a4161f66..39de0d26171f52547bad175a893aced8d191ae43 100644 (file)
@@ -801,10 +801,16 @@ static int bringup_cpu(unsigned int cpu)
         */
        irq_lock_sparse();
 
-       /* Arch-specific enabling code. */
-       ret = __cpu_up(cpu, idle);
-       if (ret)
-               goto out_unlock;
+       /*
+        * If split startup is not enabled, use the all-in-one architecture
+        * specific __cpu_up() function to kick the CPU alive and eventually
+        * do their own synchronization.
+        */
+       if (!IS_ENABLED(CONFIG_HOTPLUG_SPLIT_STARTUP)) {
+               ret = __cpu_up(cpu, idle);
+               if (ret)
+                       goto out_unlock;
+       }
 
        ret = cpuhp_bp_sync_alive(cpu);
        if (ret)
@@ -826,6 +832,18 @@ out_unlock:
        return ret;
 }
 
+#ifdef CONFIG_HOTPLUG_SPLIT_STARTUP
+static int cpuhp_kick_ap_alive(unsigned int cpu)
+{
+       int ret = cpuhp_can_boot_ap(cpu);
+
+       if (ret)
+               return ret;
+
+       return arch_cpuhp_kick_ap_alive(cpu, idle_thread_get(cpu));
+}
+#endif
+
 static int finish_cpu(unsigned int cpu)
 {
        struct task_struct *idle = idle_thread_get(cpu);
@@ -1965,13 +1983,41 @@ static struct cpuhp_step cpuhp_hp_states[] = {
                .teardown.single        = timers_dead_cpu,
        },
 
-       /* Kicks the plugged cpu into life */
+#ifdef CONFIG_HOTPLUG_SPLIT_STARTUP
+       /*
+        * Kicks the AP alive. AP will wait in cpuhp_ap_sync_alive() until
+        * the next step will release it.
+        */
+       [CPUHP_BP_KICK_AP] = {
+               .name                   = "cpu:kick_ap",
+               .startup.single         = cpuhp_kick_ap_alive,
+       },
+
+       /*
+        * Waits for the AP to reach cpuhp_ap_sync_alive() and then
+        * releases it for the complete bringup.
+        *
+        * Deliberately separate from the below for the non-split case for
+        * documentation purposes and in preparation for possible further
+        * split ups.
+        */
        [CPUHP_BRINGUP_CPU] = {
                .name                   = "cpu:bringup",
                .startup.single         = bringup_cpu,
                .teardown.single        = finish_cpu,
                .cant_stop              = true,
        },
+#else
+       /*
+        * All-in-one CPU bringup state which includes the kick alive.
+        */
+       [CPUHP_BRINGUP_CPU] = {
+               .name                   = "cpu:bringup",
+               .startup.single         = bringup_cpu,
+               .teardown.single        = finish_cpu,
+               .cant_stop              = true,
+       },
+#endif
        /* Final state before CPU kills itself */
        [CPUHP_AP_IDLE_DEAD] = {
                .name                   = "idle:dead",