Skip to content

Commit 18415f3

Browse files
KAGA-KOKOPeter Zijlstra
authored andcommitted
cpu/hotplug: Allow "parallel" bringup up to CPUHP_BP_KICK_AP_STATE
There is often significant latency in the early stages of CPU bringup, and time is wasted by waking each CPU (e.g. with SIPI/INIT/INIT on x86) and then waiting for it to respond before moving on to the next. Allow a platform to enable parallel setup which brings all to be onlined CPUs up to the CPUHP_BP_KICK_AP state. While this state advancement on the control CPU (BP) is single-threaded the important part is the last state CPUHP_BP_KICK_AP which wakes the to be onlined CPUs up. This allows the CPUs to run up to the first sychronization point cpuhp_ap_sync_alive() where they wait for the control CPU to release them one by one for the full onlining procedure. This parallelism depends on the CPU hotplug core sync mechanism which ensures that the parallel brought up CPUs wait for release before touching any state which would make the CPU visible to anything outside the hotplug control mechanism. To handle the SMT constraints of X86 correctly the bringup happens in two iterations when CONFIG_HOTPLUG_SMT is enabled. The control CPU brings up the primary SMT threads of each core first, which can load the microcode without the need to rendevouz with the thread siblings. Once that's completed it brings up the secondary SMT threads. Co-developed-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> 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.240231377@linutronix.de
1 parent f54d443 commit 18415f3

4 files changed

Lines changed: 109 additions & 5 deletions

File tree

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,12 @@
838838
on every CPU online, such as boot, and resume from suspend.
839839
Default: 10000
840840

841+
cpuhp.parallel=
842+
[SMP] Enable/disable parallel bringup of secondary CPUs
843+
Format: <bool>
844+
Default is enabled if CONFIG_HOTPLUG_PARALLEL=y. Otherwise
845+
the parameter has no effect.
846+
841847
crash_kexec_post_notifiers
842848
Run kdump after running panic-notifiers and dumping
843849
kmsg. This only for the users who doubt kdump always

arch/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ config HOTPLUG_SPLIT_STARTUP
5353
bool
5454
select HOTPLUG_CORE_SYNC_FULL
5555

56+
config HOTPLUG_PARALLEL
57+
bool
58+
select HOTPLUG_SPLIT_STARTUP
59+
5660
config GENERIC_ENTRY
5761
bool
5862

include/linux/cpuhotplug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ void cpuhp_ap_sync_alive(void);
524524
void arch_cpuhp_sync_state_poll(void);
525525
void arch_cpuhp_cleanup_kick_cpu(unsigned int cpu);
526526
int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle);
527+
bool arch_cpuhp_init_parallel_bringup(void);
527528

528529
#ifdef CONFIG_HOTPLUG_CORE_SYNC_DEAD
529530
void cpuhp_ap_report_dead(void);

kernel/cpu.c

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -649,8 +649,23 @@ bool cpu_smt_possible(void)
649649
cpu_smt_control != CPU_SMT_NOT_SUPPORTED;
650650
}
651651
EXPORT_SYMBOL_GPL(cpu_smt_possible);
652+
653+
static inline bool cpuhp_smt_aware(void)
654+
{
655+
return topology_smt_supported();
656+
}
657+
658+
static inline const struct cpumask *cpuhp_get_primary_thread_mask(void)
659+
{
660+
return cpu_primary_thread_mask;
661+
}
652662
#else
653663
static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
664+
static inline bool cpuhp_smt_aware(void) { return false; }
665+
static inline const struct cpumask *cpuhp_get_primary_thread_mask(void)
666+
{
667+
return cpu_present_mask;
668+
}
654669
#endif
655670

656671
static inline enum cpuhp_state
@@ -1747,18 +1762,96 @@ int bringup_hibernate_cpu(unsigned int sleep_cpu)
17471762
return 0;
17481763
}
17491764

1750-
void __init bringup_nonboot_cpus(unsigned int setup_max_cpus)
1765+
static void __init cpuhp_bringup_mask(const struct cpumask *mask, unsigned int ncpus,
1766+
enum cpuhp_state target)
17511767
{
17521768
unsigned int cpu;
17531769

1754-
for_each_present_cpu(cpu) {
1755-
if (num_online_cpus() >= setup_max_cpus)
1770+
for_each_cpu(cpu, mask) {
1771+
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
1772+
1773+
if (!--ncpus)
17561774
break;
1757-
if (!cpu_online(cpu))
1758-
cpu_up(cpu, CPUHP_ONLINE);
1775+
1776+
if (cpu_up(cpu, target) && can_rollback_cpu(st)) {
1777+
/*
1778+
* If this failed then cpu_up() might have only
1779+
* rolled back to CPUHP_BP_KICK_AP for the final
1780+
* online. Clean it up. NOOP if already rolled back.
1781+
*/
1782+
WARN_ON(cpuhp_invoke_callback_range(false, cpu, st, CPUHP_OFFLINE));
1783+
}
17591784
}
17601785
}
17611786

1787+
#ifdef CONFIG_HOTPLUG_PARALLEL
1788+
static bool __cpuhp_parallel_bringup __ro_after_init = true;
1789+
1790+
static int __init parallel_bringup_parse_param(char *arg)
1791+
{
1792+
return kstrtobool(arg, &__cpuhp_parallel_bringup);
1793+
}
1794+
early_param("cpuhp.parallel", parallel_bringup_parse_param);
1795+
1796+
/*
1797+
* On architectures which have enabled parallel bringup this invokes all BP
1798+
* prepare states for each of the to be onlined APs first. The last state
1799+
* sends the startup IPI to the APs. The APs proceed through the low level
1800+
* bringup code in parallel and then wait for the control CPU to release
1801+
* them one by one for the final onlining procedure.
1802+
*
1803+
* This avoids waiting for each AP to respond to the startup IPI in
1804+
* CPUHP_BRINGUP_CPU.
1805+
*/
1806+
static bool __init cpuhp_bringup_cpus_parallel(unsigned int ncpus)
1807+
{
1808+
const struct cpumask *mask = cpu_present_mask;
1809+
1810+
if (__cpuhp_parallel_bringup)
1811+
__cpuhp_parallel_bringup = arch_cpuhp_init_parallel_bringup();
1812+
if (!__cpuhp_parallel_bringup)
1813+
return false;
1814+
1815+
if (cpuhp_smt_aware()) {
1816+
const struct cpumask *pmask = cpuhp_get_primary_thread_mask();
1817+
static struct cpumask tmp_mask __initdata;
1818+
1819+
/*
1820+
* X86 requires to prevent that SMT siblings stopped while
1821+
* the primary thread does a microcode update for various
1822+
* reasons. Bring the primary threads up first.
1823+
*/
1824+
cpumask_and(&tmp_mask, mask, pmask);
1825+
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_BP_KICK_AP);
1826+
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_ONLINE);
1827+
/* Account for the online CPUs */
1828+
ncpus -= num_online_cpus();
1829+
if (!ncpus)
1830+
return true;
1831+
/* Create the mask for secondary CPUs */
1832+
cpumask_andnot(&tmp_mask, mask, pmask);
1833+
mask = &tmp_mask;
1834+
}
1835+
1836+
/* Bring the not-yet started CPUs up */
1837+
cpuhp_bringup_mask(mask, ncpus, CPUHP_BP_KICK_AP);
1838+
cpuhp_bringup_mask(mask, ncpus, CPUHP_ONLINE);
1839+
return true;
1840+
}
1841+
#else
1842+
static inline bool cpuhp_bringup_cpus_parallel(unsigned int ncpus) { return false; }
1843+
#endif /* CONFIG_HOTPLUG_PARALLEL */
1844+
1845+
void __init bringup_nonboot_cpus(unsigned int setup_max_cpus)
1846+
{
1847+
/* Try parallel bringup optimization if enabled */
1848+
if (cpuhp_bringup_cpus_parallel(setup_max_cpus))
1849+
return;
1850+
1851+
/* Full per CPU serialized bringup */
1852+
cpuhp_bringup_mask(cpu_present_mask, setup_max_cpus, CPUHP_ONLINE);
1853+
}
1854+
17621855
#ifdef CONFIG_PM_SLEEP_SMP
17631856
static cpumask_var_t frozen_cpus;
17641857

0 commit comments

Comments
 (0)