Commit 2319dd9d authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Stefan Bader

cpu/hotplug: Fix SMT supported evaluation

Josh reported that the late SMT evaluation in cpu_smt_state_init() sets
cpu_smt_control to CPU_SMT_NOT_SUPPORTED in case that 'nosmt' was supplied
on the kernel command line as it cannot differentiate between SMT disabled
by BIOS and SMT soft disable via 'nosmt'. That wreckages the state and
makes the sysfs interface unusable.

Rework this so that during bringup of the non boot CPUs the availability of
SMT is determined in cpu_smt_allowed(). If a newly booted CPU is not a
'primary' thread then set the local cpu_smt_available marker and evaluate
this explicitely right after the initial SMP bringup has finished.

SMT evaulation on x86 is a trainwreck as the firmware has all the
information _before_ booting the kernel, but there is no interface to query
it.

Fixes: 73d5e2b4 ("cpu/hotplug: detect SMT disabled by BIOS")
Reported-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>

CVE-2018-3620
CVE-2018-3646

[smb: Context and also adjust to alternative booted_once scheme,
      including a move of the smt check into _cpu_up()]
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
parent 0502ecb7
...@@ -62,7 +62,7 @@ void __init check_bugs(void) ...@@ -62,7 +62,7 @@ void __init check_bugs(void)
* identify_boot_cpu() initialized SMT support information, let the * identify_boot_cpu() initialized SMT support information, let the
* core code know. * core code know.
*/ */
cpu_smt_check_topology(); cpu_smt_check_topology_early();
if (!IS_ENABLED(CONFIG_SMP)) { if (!IS_ENABLED(CONFIG_SMP)) {
pr_info("CPU: "); pr_info("CPU: ");
......
...@@ -305,10 +305,12 @@ enum cpuhp_smt_control { ...@@ -305,10 +305,12 @@ enum cpuhp_smt_control {
#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) #if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT)
extern enum cpuhp_smt_control cpu_smt_control; extern enum cpuhp_smt_control cpu_smt_control;
extern void cpu_smt_disable(bool force); extern void cpu_smt_disable(bool force);
extern void cpu_smt_check_topology_early(void);
extern void cpu_smt_check_topology(void); extern void cpu_smt_check_topology(void);
#else #else
# define cpu_smt_control (CPU_SMT_ENABLED) # define cpu_smt_control (CPU_SMT_ENABLED)
static inline void cpu_smt_disable(bool force) { } static inline void cpu_smt_disable(bool force) { }
static inline void cpu_smt_check_topology_early(void) { }
static inline void cpu_smt_check_topology(void) { } static inline void cpu_smt_check_topology(void) { }
#endif #endif
......
...@@ -201,6 +201,8 @@ EXPORT_SYMBOL_GPL(cpu_hotplug_enable); ...@@ -201,6 +201,8 @@ EXPORT_SYMBOL_GPL(cpu_hotplug_enable);
enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED;
EXPORT_SYMBOL_GPL(cpu_smt_control); EXPORT_SYMBOL_GPL(cpu_smt_control);
static bool cpu_smt_available __read_mostly;
void __init cpu_smt_disable(bool force) void __init cpu_smt_disable(bool force)
{ {
if (cpu_smt_control == CPU_SMT_FORCE_DISABLED || if (cpu_smt_control == CPU_SMT_FORCE_DISABLED ||
...@@ -217,14 +219,28 @@ void __init cpu_smt_disable(bool force) ...@@ -217,14 +219,28 @@ void __init cpu_smt_disable(bool force)
/* /*
* The decision whether SMT is supported can only be done after the full * The decision whether SMT is supported can only be done after the full
* CPU identification. Called from architecture code. * CPU identification. Called from architecture code before non boot CPUs
* are brought up.
*/ */
void __init cpu_smt_check_topology(void) void __init cpu_smt_check_topology_early(void)
{ {
if (!topology_smt_supported()) if (!topology_smt_supported())
cpu_smt_control = CPU_SMT_NOT_SUPPORTED; cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
} }
/*
* If SMT was disabled by BIOS, detect it here, after the CPUs have been
* brought online. This ensures the smt/l1tf sysfs entries are consistent
* with reality. cpu_smt_available is set to true during the bringup of non
* boot CPUs when a SMT sibling is detected. Note, this may overwrite
* cpu_smt_control's previous setting.
*/
void __init cpu_smt_check_topology(void)
{
if (!cpu_smt_available)
cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
}
static int __init smt_cmdline_disable(char *str) static int __init smt_cmdline_disable(char *str)
{ {
cpu_smt_disable(str && !strcmp(str, "force")); cpu_smt_disable(str && !strcmp(str, "force"));
...@@ -234,10 +250,10 @@ early_param("nosmt", smt_cmdline_disable); ...@@ -234,10 +250,10 @@ early_param("nosmt", smt_cmdline_disable);
static inline bool cpu_smt_allowed(unsigned int cpu) static inline bool cpu_smt_allowed(unsigned int cpu)
{ {
if (cpu_smt_control == CPU_SMT_ENABLED) if (topology_is_primary_thread(cpu))
return true; return true;
if (topology_is_primary_thread(cpu)) if (cpu_smt_control == CPU_SMT_ENABLED)
return true; return true;
/* /*
...@@ -581,6 +597,18 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen) ...@@ -581,6 +597,18 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen)
#ifdef CONFIG_HOTPLUG_SMT #ifdef CONFIG_HOTPLUG_SMT
cpumask_set_cpu(cpu, to_cpumask(cpu_bootonce_bits)); cpumask_set_cpu(cpu, to_cpumask(cpu_bootonce_bits));
/*
* If the CPU is not a 'primary' thread and the booted_once bit is
* set then the processor has SMT support. Store this information
* for the late check of SMT support in cpu_smt_check_topology().
*
* Backport notice, since without the hotplug state machine there
* is no additional cpu_smt_allowed() check in the callback. There
* are no callbacks at all. So make the change here when the bit
* for booted once was just set for a non primary thread.
*/
if (!topology_is_primary_thread(cpu))
cpu_smt_available = true;
#endif #endif
BUG_ON(!cpu_online(cpu)); BUG_ON(!cpu_online(cpu));
...@@ -944,15 +972,6 @@ static const struct attribute_group cpuhp_smt_attr_group = { ...@@ -944,15 +972,6 @@ static const struct attribute_group cpuhp_smt_attr_group = {
static int __init cpu_smt_state_init(void) static int __init cpu_smt_state_init(void)
{ {
/*
* If SMT was disabled by BIOS, detect it here, after the CPUs have
* been brought online. This ensures the smt/l1tf sysfs entries are
* consistent with reality. Note this may overwrite cpu_smt_control's
* previous setting.
*/
if (topology_max_smt_threads() == 1)
cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
return sysfs_create_group(&cpu_subsys.dev_root->kobj, return sysfs_create_group(&cpu_subsys.dev_root->kobj,
&cpuhp_smt_attr_group); &cpuhp_smt_attr_group);
} }
......
...@@ -641,6 +641,8 @@ void __init smp_init(void) ...@@ -641,6 +641,8 @@ void __init smp_init(void)
} }
#endif #endif
/* Final decision about SMT support */
cpu_smt_check_topology();
/* Any cleanup work */ /* Any cleanup work */
smp_announce(); smp_announce();
smp_cpus_done(setup_max_cpus); smp_cpus_done(setup_max_cpus);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment