diff options
Diffstat (limited to 'kernel/cpu.c')
-rw-r--r-- | kernel/cpu.c | 111 |
1 files changed, 83 insertions, 28 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 025f419d16f6..f2ef10460698 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/notifier.h> | 9 | #include <linux/notifier.h> |
10 | #include <linux/sched/signal.h> | 10 | #include <linux/sched/signal.h> |
11 | #include <linux/sched/hotplug.h> | 11 | #include <linux/sched/hotplug.h> |
12 | #include <linux/sched/isolation.h> | ||
12 | #include <linux/sched/task.h> | 13 | #include <linux/sched/task.h> |
13 | #include <linux/sched/smt.h> | 14 | #include <linux/sched/smt.h> |
14 | #include <linux/unistd.h> | 15 | #include <linux/unistd.h> |
@@ -564,6 +565,20 @@ static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st) | |||
564 | cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); | 565 | cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); |
565 | } | 566 | } |
566 | 567 | ||
568 | static inline bool can_rollback_cpu(struct cpuhp_cpu_state *st) | ||
569 | { | ||
570 | if (IS_ENABLED(CONFIG_HOTPLUG_CPU)) | ||
571 | return true; | ||
572 | /* | ||
573 | * When CPU hotplug is disabled, then taking the CPU down is not | ||
574 | * possible because takedown_cpu() and the architecture and | ||
575 | * subsystem specific mechanisms are not available. So the CPU | ||
576 | * which would be completely unplugged again needs to stay around | ||
577 | * in the current state. | ||
578 | */ | ||
579 | return st->state <= CPUHP_BRINGUP_CPU; | ||
580 | } | ||
581 | |||
567 | static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, | 582 | static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, |
568 | enum cpuhp_state target) | 583 | enum cpuhp_state target) |
569 | { | 584 | { |
@@ -574,8 +589,10 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, | |||
574 | st->state++; | 589 | st->state++; |
575 | ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL); | 590 | ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL); |
576 | if (ret) { | 591 | if (ret) { |
577 | st->target = prev_state; | 592 | if (can_rollback_cpu(st)) { |
578 | undo_cpu_up(cpu, st); | 593 | st->target = prev_state; |
594 | undo_cpu_up(cpu, st); | ||
595 | } | ||
579 | break; | 596 | break; |
580 | } | 597 | } |
581 | } | 598 | } |
@@ -844,6 +861,8 @@ static int take_cpu_down(void *_param) | |||
844 | 861 | ||
845 | /* Give up timekeeping duties */ | 862 | /* Give up timekeeping duties */ |
846 | tick_handover_do_timer(); | 863 | tick_handover_do_timer(); |
864 | /* Remove CPU from timer broadcasting */ | ||
865 | tick_offline_cpu(cpu); | ||
847 | /* Park the stopper thread */ | 866 | /* Park the stopper thread */ |
848 | stop_machine_park(cpu); | 867 | stop_machine_park(cpu); |
849 | return 0; | 868 | return 0; |
@@ -1183,8 +1202,15 @@ int freeze_secondary_cpus(int primary) | |||
1183 | int cpu, error = 0; | 1202 | int cpu, error = 0; |
1184 | 1203 | ||
1185 | cpu_maps_update_begin(); | 1204 | cpu_maps_update_begin(); |
1186 | if (!cpu_online(primary)) | 1205 | if (primary == -1) { |
1187 | primary = cpumask_first(cpu_online_mask); | 1206 | primary = cpumask_first(cpu_online_mask); |
1207 | if (!housekeeping_cpu(primary, HK_FLAG_TIMER)) | ||
1208 | primary = housekeeping_any_cpu(HK_FLAG_TIMER); | ||
1209 | } else { | ||
1210 | if (!cpu_online(primary)) | ||
1211 | primary = cpumask_first(cpu_online_mask); | ||
1212 | } | ||
1213 | |||
1188 | /* | 1214 | /* |
1189 | * We take down all of the non-boot CPUs in one shot to avoid races | 1215 | * We take down all of the non-boot CPUs in one shot to avoid races |
1190 | * with the userspace trying to use the CPU hotplug at the same time | 1216 | * with the userspace trying to use the CPU hotplug at the same time |
@@ -2017,19 +2043,6 @@ static const struct attribute_group cpuhp_cpu_root_attr_group = { | |||
2017 | 2043 | ||
2018 | #ifdef CONFIG_HOTPLUG_SMT | 2044 | #ifdef CONFIG_HOTPLUG_SMT |
2019 | 2045 | ||
2020 | static const char *smt_states[] = { | ||
2021 | [CPU_SMT_ENABLED] = "on", | ||
2022 | [CPU_SMT_DISABLED] = "off", | ||
2023 | [CPU_SMT_FORCE_DISABLED] = "forceoff", | ||
2024 | [CPU_SMT_NOT_SUPPORTED] = "notsupported", | ||
2025 | }; | ||
2026 | |||
2027 | static ssize_t | ||
2028 | show_smt_control(struct device *dev, struct device_attribute *attr, char *buf) | ||
2029 | { | ||
2030 | return snprintf(buf, PAGE_SIZE - 2, "%s\n", smt_states[cpu_smt_control]); | ||
2031 | } | ||
2032 | |||
2033 | static void cpuhp_offline_cpu_device(unsigned int cpu) | 2046 | static void cpuhp_offline_cpu_device(unsigned int cpu) |
2034 | { | 2047 | { |
2035 | struct device *dev = get_cpu_device(cpu); | 2048 | struct device *dev = get_cpu_device(cpu); |
@@ -2100,9 +2113,10 @@ static int cpuhp_smt_enable(void) | |||
2100 | return ret; | 2113 | return ret; |
2101 | } | 2114 | } |
2102 | 2115 | ||
2116 | |||
2103 | static ssize_t | 2117 | static ssize_t |
2104 | store_smt_control(struct device *dev, struct device_attribute *attr, | 2118 | __store_smt_control(struct device *dev, struct device_attribute *attr, |
2105 | const char *buf, size_t count) | 2119 | const char *buf, size_t count) |
2106 | { | 2120 | { |
2107 | int ctrlval, ret; | 2121 | int ctrlval, ret; |
2108 | 2122 | ||
@@ -2140,14 +2154,44 @@ store_smt_control(struct device *dev, struct device_attribute *attr, | |||
2140 | unlock_device_hotplug(); | 2154 | unlock_device_hotplug(); |
2141 | return ret ? ret : count; | 2155 | return ret ? ret : count; |
2142 | } | 2156 | } |
2157 | |||
2158 | #else /* !CONFIG_HOTPLUG_SMT */ | ||
2159 | static ssize_t | ||
2160 | __store_smt_control(struct device *dev, struct device_attribute *attr, | ||
2161 | const char *buf, size_t count) | ||
2162 | { | ||
2163 | return -ENODEV; | ||
2164 | } | ||
2165 | #endif /* CONFIG_HOTPLUG_SMT */ | ||
2166 | |||
2167 | static const char *smt_states[] = { | ||
2168 | [CPU_SMT_ENABLED] = "on", | ||
2169 | [CPU_SMT_DISABLED] = "off", | ||
2170 | [CPU_SMT_FORCE_DISABLED] = "forceoff", | ||
2171 | [CPU_SMT_NOT_SUPPORTED] = "notsupported", | ||
2172 | [CPU_SMT_NOT_IMPLEMENTED] = "notimplemented", | ||
2173 | }; | ||
2174 | |||
2175 | static ssize_t | ||
2176 | show_smt_control(struct device *dev, struct device_attribute *attr, char *buf) | ||
2177 | { | ||
2178 | const char *state = smt_states[cpu_smt_control]; | ||
2179 | |||
2180 | return snprintf(buf, PAGE_SIZE - 2, "%s\n", state); | ||
2181 | } | ||
2182 | |||
2183 | static ssize_t | ||
2184 | store_smt_control(struct device *dev, struct device_attribute *attr, | ||
2185 | const char *buf, size_t count) | ||
2186 | { | ||
2187 | return __store_smt_control(dev, attr, buf, count); | ||
2188 | } | ||
2143 | static DEVICE_ATTR(control, 0644, show_smt_control, store_smt_control); | 2189 | static DEVICE_ATTR(control, 0644, show_smt_control, store_smt_control); |
2144 | 2190 | ||
2145 | static ssize_t | 2191 | static ssize_t |
2146 | show_smt_active(struct device *dev, struct device_attribute *attr, char *buf) | 2192 | show_smt_active(struct device *dev, struct device_attribute *attr, char *buf) |
2147 | { | 2193 | { |
2148 | bool active = topology_max_smt_threads() > 1; | 2194 | return snprintf(buf, PAGE_SIZE - 2, "%d\n", sched_smt_active()); |
2149 | |||
2150 | return snprintf(buf, PAGE_SIZE - 2, "%d\n", active); | ||
2151 | } | 2195 | } |
2152 | static DEVICE_ATTR(active, 0444, show_smt_active, NULL); | 2196 | static DEVICE_ATTR(active, 0444, show_smt_active, NULL); |
2153 | 2197 | ||
@@ -2163,21 +2207,17 @@ static const struct attribute_group cpuhp_smt_attr_group = { | |||
2163 | NULL | 2207 | NULL |
2164 | }; | 2208 | }; |
2165 | 2209 | ||
2166 | static int __init cpu_smt_state_init(void) | 2210 | static int __init cpu_smt_sysfs_init(void) |
2167 | { | 2211 | { |
2168 | return sysfs_create_group(&cpu_subsys.dev_root->kobj, | 2212 | return sysfs_create_group(&cpu_subsys.dev_root->kobj, |
2169 | &cpuhp_smt_attr_group); | 2213 | &cpuhp_smt_attr_group); |
2170 | } | 2214 | } |
2171 | 2215 | ||
2172 | #else | ||
2173 | static inline int cpu_smt_state_init(void) { return 0; } | ||
2174 | #endif | ||
2175 | |||
2176 | static int __init cpuhp_sysfs_init(void) | 2216 | static int __init cpuhp_sysfs_init(void) |
2177 | { | 2217 | { |
2178 | int cpu, ret; | 2218 | int cpu, ret; |
2179 | 2219 | ||
2180 | ret = cpu_smt_state_init(); | 2220 | ret = cpu_smt_sysfs_init(); |
2181 | if (ret) | 2221 | if (ret) |
2182 | return ret; | 2222 | return ret; |
2183 | 2223 | ||
@@ -2198,7 +2238,7 @@ static int __init cpuhp_sysfs_init(void) | |||
2198 | return 0; | 2238 | return 0; |
2199 | } | 2239 | } |
2200 | device_initcall(cpuhp_sysfs_init); | 2240 | device_initcall(cpuhp_sysfs_init); |
2201 | #endif | 2241 | #endif /* CONFIG_SYSFS && CONFIG_HOTPLUG_CPU */ |
2202 | 2242 | ||
2203 | /* | 2243 | /* |
2204 | * cpu_bit_bitmap[] is a special, "compressed" data structure that | 2244 | * cpu_bit_bitmap[] is a special, "compressed" data structure that |
@@ -2288,3 +2328,18 @@ void __init boot_cpu_hotplug_init(void) | |||
2288 | #endif | 2328 | #endif |
2289 | this_cpu_write(cpuhp_state.state, CPUHP_ONLINE); | 2329 | this_cpu_write(cpuhp_state.state, CPUHP_ONLINE); |
2290 | } | 2330 | } |
2331 | |||
2332 | enum cpu_mitigations cpu_mitigations __ro_after_init = CPU_MITIGATIONS_AUTO; | ||
2333 | |||
2334 | static int __init mitigations_parse_cmdline(char *arg) | ||
2335 | { | ||
2336 | if (!strcmp(arg, "off")) | ||
2337 | cpu_mitigations = CPU_MITIGATIONS_OFF; | ||
2338 | else if (!strcmp(arg, "auto")) | ||
2339 | cpu_mitigations = CPU_MITIGATIONS_AUTO; | ||
2340 | else if (!strcmp(arg, "auto,nosmt")) | ||
2341 | cpu_mitigations = CPU_MITIGATIONS_AUTO_NOSMT; | ||
2342 | |||
2343 | return 0; | ||
2344 | } | ||
2345 | early_param("mitigations", mitigations_parse_cmdline); | ||