Skip to content

Commit a631be9

Browse files
KAGA-KOKOPeter Zijlstra
authored andcommitted
cpu/hotplug: Provide a split up CPUHP_BRINGUP mechanism
The bring up logic of a to be onlined CPU consists of several parts, which are considered to be a single hotplug state: 1) Control CPU issues the wake-up 2) To be onlined CPU starts up, does the minimal initialization, reports to be alive and waits for release into the complete bring-up. 3) Control CPU waits for the alive report and releases the upcoming CPU for the complete bring-up. Allow to split this into two states: 1) Control CPU issues the wake-up After that the to be onlined CPU starts up, does the minimal initialization, reports to be alive and waits for release into the full bring-up. As this can run after the control CPU dropped the hotplug locks the code which is executed on the AP before it reports alive has to be carefully audited to not violate any of the hotplug constraints, especially not modifying any of the various cpumasks. This is really only meant to avoid waiting for the AP to react on the wake-up. Of course an architecture can move strict CPU related setup functionality, e.g. microcode loading, with care before the synchronization point to save further pointless waiting time. 2) Control CPU waits for the alive report and releases the upcoming CPU for the complete bring-up. This allows that the two states can be split up to run all to be onlined CPUs up to state #1 on the control CPU and then at a later point run state #2. This spares some of the latencies of the full serialized per CPU bringup by avoiding the per CPU wakeup/wait serialization. The assumption is that the first AP already waits when the last AP has been woken up. This obvioulsy depends on the hardware latencies and depending on the timings this might still not completely eliminate all wait scenarios. This split is just a preparatory step for enabling the parallel bringup later. The boot time bringup is still fully serialized. It has a separate config switch so that architectures which want to support parallel bringup can test the split of the CPUHP_BRINGUG step separately. To enable this the architecture must support the CPU hotplug core sync mechanism and has to be audited that there are no implicit hotplug state dependencies which require a fully serialized bringup. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Tested-by: Michael Kelley <mikelley@microsoft.com> Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name> Tested-by: Helge Deller <deller@gmx.de> # parisc Tested-by: Guilherme G. Piccoli <gpiccoli@igalia.com> # Steam Deck Link: https://lore.kernel.org/r/20230512205257.080801387@linutronix.de
1 parent 6d712b9 commit a631be9

3 files changed

Lines changed: 76 additions & 2 deletions

File tree

arch/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ config HOTPLUG_CORE_SYNC_FULL
4949
select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU
5050
select HOTPLUG_CORE_SYNC
5151

52+
config HOTPLUG_SPLIT_STARTUP
53+
bool
54+
select HOTPLUG_CORE_SYNC_FULL
55+
5256
config GENERIC_ENTRY
5357
bool
5458

include/linux/cpuhotplug.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ enum cpuhp_state {
133133
CPUHP_MIPS_SOC_PREPARE,
134134
CPUHP_BP_PREPARE_DYN,
135135
CPUHP_BP_PREPARE_DYN_END = CPUHP_BP_PREPARE_DYN + 20,
136+
CPUHP_BP_KICK_AP,
136137
CPUHP_BRINGUP_CPU,
137138

138139
/*
@@ -517,9 +518,12 @@ void cpuhp_online_idle(enum cpuhp_state state);
517518
static inline void cpuhp_online_idle(enum cpuhp_state state) { }
518519
#endif
519520

521+
struct task_struct;
522+
520523
void cpuhp_ap_sync_alive(void);
521524
void arch_cpuhp_sync_state_poll(void);
522525
void arch_cpuhp_cleanup_kick_cpu(unsigned int cpu);
526+
int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle);
523527

524528
#ifdef CONFIG_HOTPLUG_CORE_SYNC_DEAD
525529
void cpuhp_ap_report_dead(void);

kernel/cpu.c

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,47 @@ static int bringup_wait_for_ap_online(unsigned int cpu)
761761
return 0;
762762
}
763763

764+
#ifdef CONFIG_HOTPLUG_SPLIT_STARTUP
765+
static int cpuhp_kick_ap_alive(unsigned int cpu)
766+
{
767+
if (!cpuhp_can_boot_ap(cpu))
768+
return -EAGAIN;
769+
770+
return arch_cpuhp_kick_ap_alive(cpu, idle_thread_get(cpu));
771+
}
772+
773+
static int cpuhp_bringup_ap(unsigned int cpu)
774+
{
775+
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
776+
int ret;
777+
778+
/*
779+
* Some architectures have to walk the irq descriptors to
780+
* setup the vector space for the cpu which comes online.
781+
* Prevent irq alloc/free across the bringup.
782+
*/
783+
irq_lock_sparse();
784+
785+
ret = cpuhp_bp_sync_alive(cpu);
786+
if (ret)
787+
goto out_unlock;
788+
789+
ret = bringup_wait_for_ap_online(cpu);
790+
if (ret)
791+
goto out_unlock;
792+
793+
irq_unlock_sparse();
794+
795+
if (st->target <= CPUHP_AP_ONLINE_IDLE)
796+
return 0;
797+
798+
return cpuhp_kick_ap(cpu, st, st->target);
799+
800+
out_unlock:
801+
irq_unlock_sparse();
802+
return ret;
803+
}
804+
#else
764805
static int bringup_cpu(unsigned int cpu)
765806
{
766807
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
@@ -781,7 +822,6 @@ static int bringup_cpu(unsigned int cpu)
781822
*/
782823
irq_lock_sparse();
783824

784-
/* Arch-specific enabling code. */
785825
ret = __cpu_up(cpu, idle);
786826
if (ret)
787827
goto out_unlock;
@@ -805,6 +845,7 @@ static int bringup_cpu(unsigned int cpu)
805845
irq_unlock_sparse();
806846
return ret;
807847
}
848+
#endif
808849

809850
static int finish_cpu(unsigned int cpu)
810851
{
@@ -1944,13 +1985,38 @@ static struct cpuhp_step cpuhp_hp_states[] = {
19441985
.startup.single = timers_prepare_cpu,
19451986
.teardown.single = timers_dead_cpu,
19461987
},
1947-
/* Kicks the plugged cpu into life */
1988+
1989+
#ifdef CONFIG_HOTPLUG_SPLIT_STARTUP
1990+
/*
1991+
* Kicks the AP alive. AP will wait in cpuhp_ap_sync_alive() until
1992+
* the next step will release it.
1993+
*/
1994+
[CPUHP_BP_KICK_AP] = {
1995+
.name = "cpu:kick_ap",
1996+
.startup.single = cpuhp_kick_ap_alive,
1997+
},
1998+
1999+
/*
2000+
* Waits for the AP to reach cpuhp_ap_sync_alive() and then
2001+
* releases it for the complete bringup.
2002+
*/
2003+
[CPUHP_BRINGUP_CPU] = {
2004+
.name = "cpu:bringup",
2005+
.startup.single = cpuhp_bringup_ap,
2006+
.teardown.single = finish_cpu,
2007+
.cant_stop = true,
2008+
},
2009+
#else
2010+
/*
2011+
* All-in-one CPU bringup state which includes the kick alive.
2012+
*/
19482013
[CPUHP_BRINGUP_CPU] = {
19492014
.name = "cpu:bringup",
19502015
.startup.single = bringup_cpu,
19512016
.teardown.single = finish_cpu,
19522017
.cant_stop = true,
19532018
},
2019+
#endif
19542020
/* Final state before CPU kills itself */
19552021
[CPUHP_AP_IDLE_DEAD] = {
19562022
.name = "idle:dead",

0 commit comments

Comments
 (0)