Skip to content

Commit 05736e4

Browse files
committed
cpu/hotplug: Provide knobs to control SMT
Provide a command line and a sysfs knob to control SMT. The command line options are: 'nosmt': Enumerate secondary threads, but do not online them 'nosmt=force': Ignore secondary threads completely during enumeration via MP table and ACPI/MADT. The sysfs control file has the following states (read/write): 'on': SMT is enabled. Secondary threads can be freely onlined 'off': SMT is disabled. Secondary threads, even if enumerated cannot be onlined 'forceoff': SMT is permanentely disabled. Writes to the control file are rejected. 'notsupported': SMT is not supported by the CPU The command line option 'nosmt' sets the sysfs control to 'off'. This can be changed to 'on' to reenable SMT during runtime. The command line option 'nosmt=force' sets the sysfs control to 'forceoff'. This cannot be changed during runtime. When SMT is 'on' and the control file is changed to 'off' then all online secondary threads are offlined and attempts to online a secondary thread later on are rejected. When SMT is 'off' and the control file is changed to 'on' then secondary threads can be onlined again. The 'off' -> 'on' transition does not automatically online the secondary threads. When the control file is set to 'forceoff', the behaviour is the same as setting it to 'off', but the operation is irreversible and later writes to the control file are rejected. When the control status is 'notsupported' then writes to the control file are rejected. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Acked-by: Ingo Molnar <mingo@kernel.org>
1 parent cc1fe21 commit 05736e4

File tree

6 files changed

+215
-0
lines changed

6 files changed

+215
-0
lines changed

Documentation/ABI/testing/sysfs-devices-system-cpu

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,23 @@ Description: Information about CPU vulnerabilities
487487
"Not affected" CPU is not affected by the vulnerability
488488
"Vulnerable" CPU is affected and no mitigation in effect
489489
"Mitigation: $M" CPU is affected and mitigation $M is in effect
490+
491+
What: /sys/devices/system/cpu/smt
492+
/sys/devices/system/cpu/smt/active
493+
/sys/devices/system/cpu/smt/control
494+
Date: June 2018
495+
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
496+
Description: Control Symetric Multi Threading (SMT)
497+
498+
active: Tells whether SMT is active (enabled and siblings online)
499+
500+
control: Read/write interface to control SMT. Possible
501+
values:
502+
503+
"on" SMT is enabled
504+
"off" SMT is disabled
505+
"forceoff" SMT is force disabled. Cannot be changed.
506+
"notsupported" SMT is not supported by the CPU
507+
508+
If control status is "forceoff" or "notsupported" writes
509+
are rejected.

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2687,6 +2687,14 @@
26872687
nosmt [KNL,S390] Disable symmetric multithreading (SMT).
26882688
Equivalent to smt=1.
26892689

2690+
[KNL,x86] Disable symmetric multithreading (SMT).
2691+
nosmt=force: Force disable SMT, similar to disabling
2692+
it in the BIOS except that some of the
2693+
resource partitioning effects which are
2694+
caused by having SMT enabled in the BIOS
2695+
cannot be undone. Depending on the CPU
2696+
type this might have a performance impact.
2697+
26902698
nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2
26912699
(indirect branch prediction) vulnerability. System may
26922700
allow data leaks with this option, which is equivalent

arch/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ config KEXEC_CORE
1313
config HAVE_IMA_KEXEC
1414
bool
1515

16+
config HOTPLUG_SMT
17+
bool
18+
1619
config OPROFILE
1720
tristate "OProfile system profiling"
1821
depends on PROFILING

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ config X86
187187
select HAVE_SYSCALL_TRACEPOINTS
188188
select HAVE_UNSTABLE_SCHED_CLOCK
189189
select HAVE_USER_RETURN_NOTIFIER
190+
select HOTPLUG_SMT if SMP
190191
select IRQ_FORCED_THREADING
191192
select NEED_SG_DMA_LENGTH
192193
select PCI_LOCKLESS_CONFIG

include/linux/cpu.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,17 @@ void cpuhp_report_idle_dead(void);
168168
static inline void cpuhp_report_idle_dead(void) { }
169169
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
170170

171+
enum cpuhp_smt_control {
172+
CPU_SMT_ENABLED,
173+
CPU_SMT_DISABLED,
174+
CPU_SMT_FORCE_DISABLED,
175+
CPU_SMT_NOT_SUPPORTED,
176+
};
177+
178+
#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT)
179+
extern enum cpuhp_smt_control cpu_smt_control;
180+
#else
181+
# define cpu_smt_control (CPU_SMT_ENABLED)
182+
#endif
183+
171184
#endif /* _LINUX_CPU_H_ */

kernel/cpu.c

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,29 @@ EXPORT_SYMBOL(cpu_down);
933933
#define takedown_cpu NULL
934934
#endif /*CONFIG_HOTPLUG_CPU*/
935935

936+
#ifdef CONFIG_HOTPLUG_SMT
937+
enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED;
938+
939+
static int __init smt_cmdline_disable(char *str)
940+
{
941+
cpu_smt_control = CPU_SMT_DISABLED;
942+
if (str && !strcmp(str, "force")) {
943+
pr_info("SMT: Force disabled\n");
944+
cpu_smt_control = CPU_SMT_FORCE_DISABLED;
945+
}
946+
return 0;
947+
}
948+
early_param("nosmt", smt_cmdline_disable);
949+
950+
static inline bool cpu_smt_allowed(unsigned int cpu)
951+
{
952+
return cpu_smt_control == CPU_SMT_ENABLED ||
953+
topology_is_primary_thread(cpu);
954+
}
955+
#else
956+
static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
957+
#endif
958+
936959
/**
937960
* notify_cpu_starting(cpu) - Invoke the callbacks on the starting CPU
938961
* @cpu: cpu that just started
@@ -1056,6 +1079,10 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
10561079
err = -EBUSY;
10571080
goto out;
10581081
}
1082+
if (!cpu_smt_allowed(cpu)) {
1083+
err = -EPERM;
1084+
goto out;
1085+
}
10591086

10601087
err = _cpu_up(cpu, 0, target);
10611088
out:
@@ -1904,10 +1931,153 @@ static const struct attribute_group cpuhp_cpu_root_attr_group = {
19041931
NULL
19051932
};
19061933

1934+
#ifdef CONFIG_HOTPLUG_SMT
1935+
1936+
static const char *smt_states[] = {
1937+
[CPU_SMT_ENABLED] = "on",
1938+
[CPU_SMT_DISABLED] = "off",
1939+
[CPU_SMT_FORCE_DISABLED] = "forceoff",
1940+
[CPU_SMT_NOT_SUPPORTED] = "notsupported",
1941+
};
1942+
1943+
static ssize_t
1944+
show_smt_control(struct device *dev, struct device_attribute *attr, char *buf)
1945+
{
1946+
return snprintf(buf, PAGE_SIZE - 2, "%s\n", smt_states[cpu_smt_control]);
1947+
}
1948+
1949+
static void cpuhp_offline_cpu_device(unsigned int cpu)
1950+
{
1951+
struct device *dev = get_cpu_device(cpu);
1952+
1953+
dev->offline = true;
1954+
/* Tell user space about the state change */
1955+
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
1956+
}
1957+
1958+
static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
1959+
{
1960+
int cpu, ret = 0;
1961+
1962+
cpu_maps_update_begin();
1963+
for_each_online_cpu(cpu) {
1964+
if (topology_is_primary_thread(cpu))
1965+
continue;
1966+
ret = cpu_down_maps_locked(cpu, CPUHP_OFFLINE);
1967+
if (ret)
1968+
break;
1969+
/*
1970+
* As this needs to hold the cpu maps lock it's impossible
1971+
* to call device_offline() because that ends up calling
1972+
* cpu_down() which takes cpu maps lock. cpu maps lock
1973+
* needs to be held as this might race against in kernel
1974+
* abusers of the hotplug machinery (thermal management).
1975+
*
1976+
* So nothing would update device:offline state. That would
1977+
* leave the sysfs entry stale and prevent onlining after
1978+
* smt control has been changed to 'off' again. This is
1979+
* called under the sysfs hotplug lock, so it is properly
1980+
* serialized against the regular offline usage.
1981+
*/
1982+
cpuhp_offline_cpu_device(cpu);
1983+
}
1984+
if (!ret)
1985+
cpu_smt_control = ctrlval;
1986+
cpu_maps_update_done();
1987+
return ret;
1988+
}
1989+
1990+
static void cpuhp_smt_enable(void)
1991+
{
1992+
cpu_maps_update_begin();
1993+
cpu_smt_control = CPU_SMT_ENABLED;
1994+
cpu_maps_update_done();
1995+
}
1996+
1997+
static ssize_t
1998+
store_smt_control(struct device *dev, struct device_attribute *attr,
1999+
const char *buf, size_t count)
2000+
{
2001+
int ctrlval, ret;
2002+
2003+
if (sysfs_streq(buf, "on"))
2004+
ctrlval = CPU_SMT_ENABLED;
2005+
else if (sysfs_streq(buf, "off"))
2006+
ctrlval = CPU_SMT_DISABLED;
2007+
else if (sysfs_streq(buf, "forceoff"))
2008+
ctrlval = CPU_SMT_FORCE_DISABLED;
2009+
else
2010+
return -EINVAL;
2011+
2012+
if (cpu_smt_control == CPU_SMT_FORCE_DISABLED)
2013+
return -EPERM;
2014+
2015+
if (cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
2016+
return -ENODEV;
2017+
2018+
ret = lock_device_hotplug_sysfs();
2019+
if (ret)
2020+
return ret;
2021+
2022+
if (ctrlval != cpu_smt_control) {
2023+
switch (ctrlval) {
2024+
case CPU_SMT_ENABLED:
2025+
cpuhp_smt_enable();
2026+
break;
2027+
case CPU_SMT_DISABLED:
2028+
case CPU_SMT_FORCE_DISABLED:
2029+
ret = cpuhp_smt_disable(ctrlval);
2030+
break;
2031+
}
2032+
}
2033+
2034+
unlock_device_hotplug();
2035+
return ret ? ret : count;
2036+
}
2037+
static DEVICE_ATTR(control, 0644, show_smt_control, store_smt_control);
2038+
2039+
static ssize_t
2040+
show_smt_active(struct device *dev, struct device_attribute *attr, char *buf)
2041+
{
2042+
bool active = topology_max_smt_threads() > 1;
2043+
2044+
return snprintf(buf, PAGE_SIZE - 2, "%d\n", active);
2045+
}
2046+
static DEVICE_ATTR(active, 0444, show_smt_active, NULL);
2047+
2048+
static struct attribute *cpuhp_smt_attrs[] = {
2049+
&dev_attr_control.attr,
2050+
&dev_attr_active.attr,
2051+
NULL
2052+
};
2053+
2054+
static const struct attribute_group cpuhp_smt_attr_group = {
2055+
.attrs = cpuhp_smt_attrs,
2056+
.name = "smt",
2057+
NULL
2058+
};
2059+
2060+
static int __init cpu_smt_state_init(void)
2061+
{
2062+
if (!topology_smt_supported())
2063+
cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
2064+
2065+
return sysfs_create_group(&cpu_subsys.dev_root->kobj,
2066+
&cpuhp_smt_attr_group);
2067+
}
2068+
2069+
#else
2070+
static inline int cpu_smt_state_init(void) { return 0; }
2071+
#endif
2072+
19072073
static int __init cpuhp_sysfs_init(void)
19082074
{
19092075
int cpu, ret;
19102076

2077+
ret = cpu_smt_state_init();
2078+
if (ret)
2079+
return ret;
2080+
19112081
ret = sysfs_create_group(&cpu_subsys.dev_root->kobj,
19122082
&cpuhp_cpu_root_attr_group);
19132083
if (ret)

0 commit comments

Comments
 (0)