Message ID | 20230202215625.3248306-4-usama.arif@bytedance.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Parallel CPU bringup for x86_64 | expand |
On Thu, Feb 02 2023 at 21:56, Usama Arif wrote: > So any combination of prepare/start calls which depend on A-B ordering > for each CPU in turn, such as the X2APIC code which used to allocate a > cluster mask 'just in case' and store it in a global variable in the > prep stage, then potentially consume that preallocated structure from > the AP and set the global pointer to NULL to be reallocated in > CPUHP_X2APIC_PREPARE for the next CPU... would explode horribly. > > We believe that X2APIC was the only such case, for x86. But this is why > it remains an architecture opt-in. For now. We believe is not really a convincing technical argument. On x86 the X2APIC case was established to be the only one which relied on the full CPU hotplug serialization. It's unclear whether this affects any other architecture, so this optimization is opt-in. Hmm? > diff --git a/kernel/cpu.c b/kernel/cpu.c > index 6c0a92ca6bb5..5a8f1a93b57c 100644 > --- a/kernel/cpu.c > +++ b/kernel/cpu.c > @@ -1505,6 +1505,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(); https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#variable-declarations Aside of that I detest the mixture between unsigned and signed here. > + > + /* ∀ 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) { while (st <= CPUHP_BP_PARALLEL_DYN_END && cpuhp_hp_states[st].name) { 80 character limit has been updated quite some time ago Thanks, tglx
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 6c6859bfc454..e5a73ae6ccc0 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -133,6 +133,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, /* diff --git a/kernel/cpu.c b/kernel/cpu.c index 6c0a92ca6bb5..5a8f1a93b57c 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1505,6 +1505,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) @@ -1882,6 +1900,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; } @@ -1906,14 +1928,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_P*_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;