[PATCH] smp: force all cpu to boot once under maxcpus option

From: Pingfan Liu
Date: Wed Jul 10 2019 - 04:37:27 EST


On x86 it's required to boot all logical CPUs at least once so that the
init code can get a chance to set CR4.MCE on each CPU. Otherwise, a
broadacasted MCE observing CR4.MCE=0b on any core will shutdown the
machine.

The option 'nosmt' has already complied with the above rule. In the case of
maxcpus, the initialization of capped out cpus may be deferred indefinitely
until a user brings them up. This exposes the machine under the risk of
sudden shutdown indefinitely.

Minimize the risk window by initializing all cpus at boot time.

Signed-off-by: Pingfan Liu <kernelfans@xxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: "Peter Zijlstra (Intel)" <peterz@xxxxxxxxxxxxx>
Cc: Rik van Riel <riel@xxxxxxxxxxx>
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Jiri Kosina <jkosina@xxxxxxx>
Cc: Mukesh Ojha <mojha@xxxxxxxxxxxxxx>
Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxxxxxx>
Cc: linux-kernel@xxxxxxxxxxxxxxx
---
include/linux/smp.h | 1 +
kernel/cpu.c | 20 ++++++++++++++++++--
kernel/smp.c | 4 ++++
3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/include/linux/smp.h b/include/linux/smp.h
index a56f08f..9d2c692 100644
--- a/include/linux/smp.h
+++ b/include/linux/smp.h
@@ -130,6 +130,7 @@ extern void __init setup_nr_cpu_ids(void);
extern void __init smp_init(void);

extern int __boot_cpu_id;
+extern bool smp_boot_done;

static inline int get_boot_cpu_id(void)
{
diff --git a/kernel/cpu.c b/kernel/cpu.c
index ef1c565..ab19dc8 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -439,6 +439,21 @@ static inline bool cpu_smt_allowed(unsigned int cpu)
static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
#endif

+static inline bool maxcpus_allowed(unsigned int cpu)
+{
+ /* maxcpus only takes effect during system bootup */
+ if (smp_boot_done)
+ return true;
+ if (num_online_cpus() < setup_max_cpus)
+ return true;
+ /*
+ * maxcpus should allow cpu to set CR4.MCE asap, otherwise the set may
+ * be deferred indefinitely.
+ */
+ if (!per_cpu(cpuhp_state, cpu).booted_once)
+ return true;
+}
+
static inline enum cpuhp_state
cpuhp_set_state(struct cpuhp_cpu_state *st, enum cpuhp_state target)
{
@@ -525,8 +540,9 @@ static int bringup_wait_for_ap(unsigned int cpu)
* CPU marked itself as booted_once in cpu_notify_starting() so the
* cpu_smt_allowed() check will now return false if this is not the
* primary sibling.
+ * In case of maxcpus, the capped out cpus comply with the same rule.
*/
- if (!cpu_smt_allowed(cpu))
+ if (!cpu_smt_allowed(cpu) || !maxcpus_allowed(cpu))
return -ECANCELED;

if (st->target <= CPUHP_AP_ONLINE_IDLE)
@@ -1177,7 +1193,7 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
err = -EBUSY;
goto out;
}
- if (!cpu_smt_allowed(cpu)) {
+ if (!cpu_smt_allowed(cpu) || !maxcpus_allowed(cpu)) {
err = -EPERM;
goto out;
}
diff --git a/kernel/smp.c b/kernel/smp.c
index d155374..a5f82d53 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -560,6 +560,9 @@ void __init setup_nr_cpu_ids(void)
nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
}

+bool smp_boot_done __read_mostly;
+EXPORT_SYMBOL(smp_boot_done);
+
/* Called by boot processor to activate the rest. */
void __init smp_init(void)
{
@@ -587,6 +590,7 @@ void __init smp_init(void)

/* Any cleanup work */
smp_cpus_done(setup_max_cpus);
+ smp_boot_done = true;
}

/*
--
2.7.5