| From foo@baz Tue Aug 14 16:14:56 CEST 2018 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Tue, 29 May 2018 17:48:27 +0200 |
| Subject: cpu/hotplug: Provide knobs to control SMT |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 05736e4ac13c08a4a9b1ef2de26dd31a32cbee57 upstream |
| |
| 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> |
| Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| Documentation/ABI/testing/sysfs-devices-system-cpu | 20 ++ |
| Documentation/kernel-parameters.txt | 8 |
| arch/Kconfig | 3 |
| arch/x86/Kconfig | 1 |
| include/linux/cpu.h | 13 + |
| kernel/cpu.c | 170 +++++++++++++++++++++ |
| 6 files changed, 215 insertions(+) |
| |
| --- a/Documentation/ABI/testing/sysfs-devices-system-cpu |
| +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu |
| @@ -367,3 +367,23 @@ Description: Information about CPU vulne |
| "Not affected" CPU is not affected by the vulnerability |
| "Vulnerable" CPU is affected and no mitigation in effect |
| "Mitigation: $M" CPU is affected and mitigation $M is in effect |
| + |
| +What: /sys/devices/system/cpu/smt |
| + /sys/devices/system/cpu/smt/active |
| + /sys/devices/system/cpu/smt/control |
| +Date: June 2018 |
| +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> |
| +Description: Control Symetric Multi Threading (SMT) |
| + |
| + active: Tells whether SMT is active (enabled and siblings online) |
| + |
| + control: Read/write interface to control SMT. Possible |
| + values: |
| + |
| + "on" SMT is enabled |
| + "off" SMT is disabled |
| + "forceoff" SMT is force disabled. Cannot be changed. |
| + "notsupported" SMT is not supported by the CPU |
| + |
| + If control status is "forceoff" or "notsupported" writes |
| + are rejected. |
| --- a/Documentation/kernel-parameters.txt |
| +++ b/Documentation/kernel-parameters.txt |
| @@ -2694,6 +2694,14 @@ bytes respectively. Such letter suffixes |
| nosmt [KNL,S390] Disable symmetric multithreading (SMT). |
| Equivalent to smt=1. |
| |
| + [KNL,x86] Disable symmetric multithreading (SMT). |
| + nosmt=force: Force disable SMT, similar to disabling |
| + it in the BIOS except that some of the |
| + resource partitioning effects which are |
| + caused by having SMT enabled in the BIOS |
| + cannot be undone. Depending on the CPU |
| + type this might have a performance impact. |
| + |
| nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2 |
| (indirect branch prediction) vulnerability. System may |
| allow data leaks with this option, which is equivalent |
| --- a/arch/Kconfig |
| +++ b/arch/Kconfig |
| @@ -5,6 +5,9 @@ |
| config KEXEC_CORE |
| bool |
| |
| +config HOTPLUG_SMT |
| + bool |
| + |
| config OPROFILE |
| tristate "OProfile system profiling" |
| depends on PROFILING |
| --- a/arch/x86/Kconfig |
| +++ b/arch/x86/Kconfig |
| @@ -147,6 +147,7 @@ config X86 |
| select HAVE_UID16 if X86_32 || IA32_EMULATION |
| select HAVE_UNSTABLE_SCHED_CLOCK |
| select HAVE_USER_RETURN_NOTIFIER |
| + select HOTPLUG_SMT if SMP |
| select IRQ_FORCED_THREADING |
| select MODULES_USE_ELF_RELA if X86_64 |
| select MODULES_USE_ELF_REL if X86_32 |
| --- a/include/linux/cpu.h |
| +++ b/include/linux/cpu.h |
| @@ -257,4 +257,17 @@ void cpuhp_report_idle_dead(void); |
| static inline void cpuhp_report_idle_dead(void) { } |
| #endif /* #ifdef CONFIG_HOTPLUG_CPU */ |
| |
| +enum cpuhp_smt_control { |
| + CPU_SMT_ENABLED, |
| + CPU_SMT_DISABLED, |
| + CPU_SMT_FORCE_DISABLED, |
| + CPU_SMT_NOT_SUPPORTED, |
| +}; |
| + |
| +#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) |
| +extern enum cpuhp_smt_control cpu_smt_control; |
| +#else |
| +# define cpu_smt_control (CPU_SMT_ENABLED) |
| +#endif |
| + |
| #endif /* _LINUX_CPU_H_ */ |
| --- a/kernel/cpu.c |
| +++ b/kernel/cpu.c |
| @@ -978,6 +978,29 @@ int cpu_down(unsigned int cpu) |
| EXPORT_SYMBOL(cpu_down); |
| #endif /*CONFIG_HOTPLUG_CPU*/ |
| |
| +#ifdef CONFIG_HOTPLUG_SMT |
| +enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; |
| + |
| +static int __init smt_cmdline_disable(char *str) |
| +{ |
| + cpu_smt_control = CPU_SMT_DISABLED; |
| + if (str && !strcmp(str, "force")) { |
| + pr_info("SMT: Force disabled\n"); |
| + cpu_smt_control = CPU_SMT_FORCE_DISABLED; |
| + } |
| + return 0; |
| +} |
| +early_param("nosmt", smt_cmdline_disable); |
| + |
| +static inline bool cpu_smt_allowed(unsigned int cpu) |
| +{ |
| + return cpu_smt_control == CPU_SMT_ENABLED || |
| + topology_is_primary_thread(cpu); |
| +} |
| +#else |
| +static inline bool cpu_smt_allowed(unsigned int cpu) { return true; } |
| +#endif |
| + |
| /** |
| * notify_cpu_starting(cpu) - Invoke the callbacks on the starting CPU |
| * @cpu: cpu that just started |
| @@ -1096,6 +1119,10 @@ static int do_cpu_up(unsigned int cpu, e |
| err = -EBUSY; |
| goto out; |
| } |
| + if (!cpu_smt_allowed(cpu)) { |
| + err = -EPERM; |
| + goto out; |
| + } |
| |
| err = _cpu_up(cpu, 0, target); |
| out: |
| @@ -1842,10 +1869,153 @@ static struct attribute_group cpuhp_cpu_ |
| NULL |
| }; |
| |
| +#ifdef CONFIG_HOTPLUG_SMT |
| + |
| +static const char *smt_states[] = { |
| + [CPU_SMT_ENABLED] = "on", |
| + [CPU_SMT_DISABLED] = "off", |
| + [CPU_SMT_FORCE_DISABLED] = "forceoff", |
| + [CPU_SMT_NOT_SUPPORTED] = "notsupported", |
| +}; |
| + |
| +static ssize_t |
| +show_smt_control(struct device *dev, struct device_attribute *attr, char *buf) |
| +{ |
| + return snprintf(buf, PAGE_SIZE - 2, "%s\n", smt_states[cpu_smt_control]); |
| +} |
| + |
| +static void cpuhp_offline_cpu_device(unsigned int cpu) |
| +{ |
| + struct device *dev = get_cpu_device(cpu); |
| + |
| + dev->offline = true; |
| + /* Tell user space about the state change */ |
| + kobject_uevent(&dev->kobj, KOBJ_OFFLINE); |
| +} |
| + |
| +static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) |
| +{ |
| + int cpu, ret = 0; |
| + |
| + cpu_maps_update_begin(); |
| + for_each_online_cpu(cpu) { |
| + if (topology_is_primary_thread(cpu)) |
| + continue; |
| + ret = cpu_down_maps_locked(cpu, CPUHP_OFFLINE); |
| + if (ret) |
| + break; |
| + /* |
| + * As this needs to hold the cpu maps lock it's impossible |
| + * to call device_offline() because that ends up calling |
| + * cpu_down() which takes cpu maps lock. cpu maps lock |
| + * needs to be held as this might race against in kernel |
| + * abusers of the hotplug machinery (thermal management). |
| + * |
| + * So nothing would update device:offline state. That would |
| + * leave the sysfs entry stale and prevent onlining after |
| + * smt control has been changed to 'off' again. This is |
| + * called under the sysfs hotplug lock, so it is properly |
| + * serialized against the regular offline usage. |
| + */ |
| + cpuhp_offline_cpu_device(cpu); |
| + } |
| + if (!ret) |
| + cpu_smt_control = ctrlval; |
| + cpu_maps_update_done(); |
| + return ret; |
| +} |
| + |
| +static void cpuhp_smt_enable(void) |
| +{ |
| + cpu_maps_update_begin(); |
| + cpu_smt_control = CPU_SMT_ENABLED; |
| + cpu_maps_update_done(); |
| +} |
| + |
| +static ssize_t |
| +store_smt_control(struct device *dev, struct device_attribute *attr, |
| + const char *buf, size_t count) |
| +{ |
| + int ctrlval, ret; |
| + |
| + if (sysfs_streq(buf, "on")) |
| + ctrlval = CPU_SMT_ENABLED; |
| + else if (sysfs_streq(buf, "off")) |
| + ctrlval = CPU_SMT_DISABLED; |
| + else if (sysfs_streq(buf, "forceoff")) |
| + ctrlval = CPU_SMT_FORCE_DISABLED; |
| + else |
| + return -EINVAL; |
| + |
| + if (cpu_smt_control == CPU_SMT_FORCE_DISABLED) |
| + return -EPERM; |
| + |
| + if (cpu_smt_control == CPU_SMT_NOT_SUPPORTED) |
| + return -ENODEV; |
| + |
| + ret = lock_device_hotplug_sysfs(); |
| + if (ret) |
| + return ret; |
| + |
| + if (ctrlval != cpu_smt_control) { |
| + switch (ctrlval) { |
| + case CPU_SMT_ENABLED: |
| + cpuhp_smt_enable(); |
| + break; |
| + case CPU_SMT_DISABLED: |
| + case CPU_SMT_FORCE_DISABLED: |
| + ret = cpuhp_smt_disable(ctrlval); |
| + break; |
| + } |
| + } |
| + |
| + unlock_device_hotplug(); |
| + return ret ? ret : count; |
| +} |
| +static DEVICE_ATTR(control, 0644, show_smt_control, store_smt_control); |
| + |
| +static ssize_t |
| +show_smt_active(struct device *dev, struct device_attribute *attr, char *buf) |
| +{ |
| + bool active = topology_max_smt_threads() > 1; |
| + |
| + return snprintf(buf, PAGE_SIZE - 2, "%d\n", active); |
| +} |
| +static DEVICE_ATTR(active, 0444, show_smt_active, NULL); |
| + |
| +static struct attribute *cpuhp_smt_attrs[] = { |
| + &dev_attr_control.attr, |
| + &dev_attr_active.attr, |
| + NULL |
| +}; |
| + |
| +static const struct attribute_group cpuhp_smt_attr_group = { |
| + .attrs = cpuhp_smt_attrs, |
| + .name = "smt", |
| + NULL |
| +}; |
| + |
| +static int __init cpu_smt_state_init(void) |
| +{ |
| + if (!topology_smt_supported()) |
| + cpu_smt_control = CPU_SMT_NOT_SUPPORTED; |
| + |
| + return sysfs_create_group(&cpu_subsys.dev_root->kobj, |
| + &cpuhp_smt_attr_group); |
| +} |
| + |
| +#else |
| +static inline int cpu_smt_state_init(void) { return 0; } |
| +#endif |
| + |
| static int __init cpuhp_sysfs_init(void) |
| { |
| int cpu, ret; |
| |
| + ret = cpu_smt_state_init(); |
| + if (ret) |
| + return ret; |
| + |
| ret = sysfs_create_group(&cpu_subsys.dev_root->kobj, |
| &cpuhp_cpu_root_attr_group); |
| if (ret) |