diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-10-09 03:02:35 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-10-09 03:02:35 -0400 |
| commit | 1236d6bb6e19fc72ffc6bbcdeb1bfefe450e54ee (patch) | |
| tree | 47da3feee8e263e8c9352c85cf518e624be3c211 /kernel/cpu.c | |
| parent | 750b1a6894ecc9b178c6e3d0a1170122971b2036 (diff) | |
| parent | 8a5776a5f49812d29fe4b2d0a2d71675c3facf3f (diff) | |
Merge 4.14-rc4 into staging-next
We want the staging/iio fixes in here as well to handle merge issues.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/cpu.c')
| -rw-r--r-- | kernel/cpu.c | 512 |
1 files changed, 370 insertions, 142 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index acf5308fad51..d851df22f5c5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/lockdep.h> | 24 | #include <linux/lockdep.h> |
| 25 | #include <linux/tick.h> | 25 | #include <linux/tick.h> |
| 26 | #include <linux/irq.h> | 26 | #include <linux/irq.h> |
| 27 | #include <linux/nmi.h> | ||
| 27 | #include <linux/smpboot.h> | 28 | #include <linux/smpboot.h> |
| 28 | #include <linux/relay.h> | 29 | #include <linux/relay.h> |
| 29 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
| @@ -46,11 +47,13 @@ | |||
| 46 | * @bringup: Single callback bringup or teardown selector | 47 | * @bringup: Single callback bringup or teardown selector |
| 47 | * @cb_state: The state for a single callback (install/uninstall) | 48 | * @cb_state: The state for a single callback (install/uninstall) |
| 48 | * @result: Result of the operation | 49 | * @result: Result of the operation |
| 49 | * @done: Signal completion to the issuer of the task | 50 | * @done_up: Signal completion to the issuer of the task for cpu-up |
| 51 | * @done_down: Signal completion to the issuer of the task for cpu-down | ||
| 50 | */ | 52 | */ |
| 51 | struct cpuhp_cpu_state { | 53 | struct cpuhp_cpu_state { |
| 52 | enum cpuhp_state state; | 54 | enum cpuhp_state state; |
| 53 | enum cpuhp_state target; | 55 | enum cpuhp_state target; |
| 56 | enum cpuhp_state fail; | ||
| 54 | #ifdef CONFIG_SMP | 57 | #ifdef CONFIG_SMP |
| 55 | struct task_struct *thread; | 58 | struct task_struct *thread; |
| 56 | bool should_run; | 59 | bool should_run; |
| @@ -58,18 +61,39 @@ struct cpuhp_cpu_state { | |||
| 58 | bool single; | 61 | bool single; |
| 59 | bool bringup; | 62 | bool bringup; |
| 60 | struct hlist_node *node; | 63 | struct hlist_node *node; |
| 64 | struct hlist_node *last; | ||
| 61 | enum cpuhp_state cb_state; | 65 | enum cpuhp_state cb_state; |
| 62 | int result; | 66 | int result; |
| 63 | struct completion done; | 67 | struct completion done_up; |
| 68 | struct completion done_down; | ||
| 64 | #endif | 69 | #endif |
| 65 | }; | 70 | }; |
| 66 | 71 | ||
| 67 | static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); | 72 | static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state) = { |
| 73 | .fail = CPUHP_INVALID, | ||
| 74 | }; | ||
| 68 | 75 | ||
| 69 | #if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP) | 76 | #if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP) |
| 70 | static struct lock_class_key cpuhp_state_key; | 77 | static struct lockdep_map cpuhp_state_up_map = |
| 71 | static struct lockdep_map cpuhp_state_lock_map = | 78 | STATIC_LOCKDEP_MAP_INIT("cpuhp_state-up", &cpuhp_state_up_map); |
| 72 | STATIC_LOCKDEP_MAP_INIT("cpuhp_state", &cpuhp_state_key); | 79 | static struct lockdep_map cpuhp_state_down_map = |
| 80 | STATIC_LOCKDEP_MAP_INIT("cpuhp_state-down", &cpuhp_state_down_map); | ||
| 81 | |||
| 82 | |||
| 83 | static void inline cpuhp_lock_acquire(bool bringup) | ||
| 84 | { | ||
| 85 | lock_map_acquire(bringup ? &cpuhp_state_up_map : &cpuhp_state_down_map); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void inline cpuhp_lock_release(bool bringup) | ||
| 89 | { | ||
| 90 | lock_map_release(bringup ? &cpuhp_state_up_map : &cpuhp_state_down_map); | ||
| 91 | } | ||
| 92 | #else | ||
| 93 | |||
| 94 | static void inline cpuhp_lock_acquire(bool bringup) { } | ||
| 95 | static void inline cpuhp_lock_release(bool bringup) { } | ||
| 96 | |||
| 73 | #endif | 97 | #endif |
| 74 | 98 | ||
| 75 | /** | 99 | /** |
| @@ -123,13 +147,16 @@ static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state) | |||
| 123 | /** | 147 | /** |
| 124 | * cpuhp_invoke_callback _ Invoke the callbacks for a given state | 148 | * cpuhp_invoke_callback _ Invoke the callbacks for a given state |
| 125 | * @cpu: The cpu for which the callback should be invoked | 149 | * @cpu: The cpu for which the callback should be invoked |
| 126 | * @step: The step in the state machine | 150 | * @state: The state to do callbacks for |
| 127 | * @bringup: True if the bringup callback should be invoked | 151 | * @bringup: True if the bringup callback should be invoked |
| 152 | * @node: For multi-instance, do a single entry callback for install/remove | ||
| 153 | * @lastp: For multi-instance rollback, remember how far we got | ||
| 128 | * | 154 | * |
| 129 | * Called from cpu hotplug and from the state register machinery. | 155 | * Called from cpu hotplug and from the state register machinery. |
| 130 | */ | 156 | */ |
| 131 | static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, | 157 | static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, |
| 132 | bool bringup, struct hlist_node *node) | 158 | bool bringup, struct hlist_node *node, |
| 159 | struct hlist_node **lastp) | ||
| 133 | { | 160 | { |
| 134 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 161 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 135 | struct cpuhp_step *step = cpuhp_get_step(state); | 162 | struct cpuhp_step *step = cpuhp_get_step(state); |
| @@ -137,7 +164,17 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, | |||
| 137 | int (*cb)(unsigned int cpu); | 164 | int (*cb)(unsigned int cpu); |
| 138 | int ret, cnt; | 165 | int ret, cnt; |
| 139 | 166 | ||
| 167 | if (st->fail == state) { | ||
| 168 | st->fail = CPUHP_INVALID; | ||
| 169 | |||
| 170 | if (!(bringup ? step->startup.single : step->teardown.single)) | ||
| 171 | return 0; | ||
| 172 | |||
| 173 | return -EAGAIN; | ||
| 174 | } | ||
| 175 | |||
| 140 | if (!step->multi_instance) { | 176 | if (!step->multi_instance) { |
| 177 | WARN_ON_ONCE(lastp && *lastp); | ||
| 141 | cb = bringup ? step->startup.single : step->teardown.single; | 178 | cb = bringup ? step->startup.single : step->teardown.single; |
| 142 | if (!cb) | 179 | if (!cb) |
| 143 | return 0; | 180 | return 0; |
| @@ -152,6 +189,7 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, | |||
| 152 | 189 | ||
| 153 | /* Single invocation for instance add/remove */ | 190 | /* Single invocation for instance add/remove */ |
| 154 | if (node) { | 191 | if (node) { |
| 192 | WARN_ON_ONCE(lastp && *lastp); | ||
| 155 | trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); | 193 | trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); |
| 156 | ret = cbm(cpu, node); | 194 | ret = cbm(cpu, node); |
| 157 | trace_cpuhp_exit(cpu, st->state, state, ret); | 195 | trace_cpuhp_exit(cpu, st->state, state, ret); |
| @@ -161,13 +199,23 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, | |||
| 161 | /* State transition. Invoke on all instances */ | 199 | /* State transition. Invoke on all instances */ |
| 162 | cnt = 0; | 200 | cnt = 0; |
| 163 | hlist_for_each(node, &step->list) { | 201 | hlist_for_each(node, &step->list) { |
| 202 | if (lastp && node == *lastp) | ||
| 203 | break; | ||
| 204 | |||
| 164 | trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); | 205 | trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); |
| 165 | ret = cbm(cpu, node); | 206 | ret = cbm(cpu, node); |
| 166 | trace_cpuhp_exit(cpu, st->state, state, ret); | 207 | trace_cpuhp_exit(cpu, st->state, state, ret); |
| 167 | if (ret) | 208 | if (ret) { |
| 168 | goto err; | 209 | if (!lastp) |
| 210 | goto err; | ||
| 211 | |||
| 212 | *lastp = node; | ||
| 213 | return ret; | ||
| 214 | } | ||
| 169 | cnt++; | 215 | cnt++; |
| 170 | } | 216 | } |
| 217 | if (lastp) | ||
| 218 | *lastp = NULL; | ||
| 171 | return 0; | 219 | return 0; |
| 172 | err: | 220 | err: |
| 173 | /* Rollback the instances if one failed */ | 221 | /* Rollback the instances if one failed */ |
| @@ -178,12 +226,39 @@ err: | |||
| 178 | hlist_for_each(node, &step->list) { | 226 | hlist_for_each(node, &step->list) { |
| 179 | if (!cnt--) | 227 | if (!cnt--) |
| 180 | break; | 228 | break; |
| 181 | cbm(cpu, node); | 229 | |
| 230 | trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); | ||
| 231 | ret = cbm(cpu, node); | ||
| 232 | trace_cpuhp_exit(cpu, st->state, state, ret); | ||
| 233 | /* | ||
| 234 | * Rollback must not fail, | ||
| 235 | */ | ||
| 236 | WARN_ON_ONCE(ret); | ||
| 182 | } | 237 | } |
| 183 | return ret; | 238 | return ret; |
| 184 | } | 239 | } |
| 185 | 240 | ||
| 186 | #ifdef CONFIG_SMP | 241 | #ifdef CONFIG_SMP |
| 242 | static inline void wait_for_ap_thread(struct cpuhp_cpu_state *st, bool bringup) | ||
| 243 | { | ||
| 244 | struct completion *done = bringup ? &st->done_up : &st->done_down; | ||
| 245 | wait_for_completion(done); | ||
| 246 | } | ||
| 247 | |||
| 248 | static inline void complete_ap_thread(struct cpuhp_cpu_state *st, bool bringup) | ||
| 249 | { | ||
| 250 | struct completion *done = bringup ? &st->done_up : &st->done_down; | ||
| 251 | complete(done); | ||
| 252 | } | ||
| 253 | |||
| 254 | /* | ||
| 255 | * The former STARTING/DYING states, ran with IRQs disabled and must not fail. | ||
| 256 | */ | ||
| 257 | static bool cpuhp_is_atomic_state(enum cpuhp_state state) | ||
| 258 | { | ||
| 259 | return CPUHP_AP_IDLE_DEAD <= state && state < CPUHP_AP_ONLINE; | ||
| 260 | } | ||
| 261 | |||
| 187 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ | 262 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ |
| 188 | static DEFINE_MUTEX(cpu_add_remove_lock); | 263 | static DEFINE_MUTEX(cpu_add_remove_lock); |
| 189 | bool cpuhp_tasks_frozen; | 264 | bool cpuhp_tasks_frozen; |
| @@ -271,14 +346,79 @@ void cpu_hotplug_enable(void) | |||
| 271 | EXPORT_SYMBOL_GPL(cpu_hotplug_enable); | 346 | EXPORT_SYMBOL_GPL(cpu_hotplug_enable); |
| 272 | #endif /* CONFIG_HOTPLUG_CPU */ | 347 | #endif /* CONFIG_HOTPLUG_CPU */ |
| 273 | 348 | ||
| 274 | static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st); | 349 | static inline enum cpuhp_state |
| 350 | cpuhp_set_state(struct cpuhp_cpu_state *st, enum cpuhp_state target) | ||
| 351 | { | ||
| 352 | enum cpuhp_state prev_state = st->state; | ||
| 353 | |||
| 354 | st->rollback = false; | ||
| 355 | st->last = NULL; | ||
| 356 | |||
| 357 | st->target = target; | ||
| 358 | st->single = false; | ||
| 359 | st->bringup = st->state < target; | ||
| 360 | |||
| 361 | return prev_state; | ||
| 362 | } | ||
| 363 | |||
| 364 | static inline void | ||
| 365 | cpuhp_reset_state(struct cpuhp_cpu_state *st, enum cpuhp_state prev_state) | ||
| 366 | { | ||
| 367 | st->rollback = true; | ||
| 368 | |||
| 369 | /* | ||
| 370 | * If we have st->last we need to undo partial multi_instance of this | ||
| 371 | * state first. Otherwise start undo at the previous state. | ||
| 372 | */ | ||
| 373 | if (!st->last) { | ||
| 374 | if (st->bringup) | ||
| 375 | st->state--; | ||
| 376 | else | ||
| 377 | st->state++; | ||
| 378 | } | ||
| 379 | |||
| 380 | st->target = prev_state; | ||
| 381 | st->bringup = !st->bringup; | ||
| 382 | } | ||
| 383 | |||
| 384 | /* Regular hotplug invocation of the AP hotplug thread */ | ||
| 385 | static void __cpuhp_kick_ap(struct cpuhp_cpu_state *st) | ||
| 386 | { | ||
| 387 | if (!st->single && st->state == st->target) | ||
| 388 | return; | ||
| 389 | |||
| 390 | st->result = 0; | ||
| 391 | /* | ||
| 392 | * Make sure the above stores are visible before should_run becomes | ||
| 393 | * true. Paired with the mb() above in cpuhp_thread_fun() | ||
| 394 | */ | ||
| 395 | smp_mb(); | ||
| 396 | st->should_run = true; | ||
| 397 | wake_up_process(st->thread); | ||
| 398 | wait_for_ap_thread(st, st->bringup); | ||
| 399 | } | ||
| 400 | |||
| 401 | static int cpuhp_kick_ap(struct cpuhp_cpu_state *st, enum cpuhp_state target) | ||
| 402 | { | ||
| 403 | enum cpuhp_state prev_state; | ||
| 404 | int ret; | ||
| 405 | |||
| 406 | prev_state = cpuhp_set_state(st, target); | ||
| 407 | __cpuhp_kick_ap(st); | ||
| 408 | if ((ret = st->result)) { | ||
| 409 | cpuhp_reset_state(st, prev_state); | ||
| 410 | __cpuhp_kick_ap(st); | ||
| 411 | } | ||
| 412 | |||
| 413 | return ret; | ||
| 414 | } | ||
| 275 | 415 | ||
| 276 | static int bringup_wait_for_ap(unsigned int cpu) | 416 | static int bringup_wait_for_ap(unsigned int cpu) |
| 277 | { | 417 | { |
| 278 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 418 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 279 | 419 | ||
| 280 | /* Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE */ | 420 | /* Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE */ |
| 281 | wait_for_completion(&st->done); | 421 | wait_for_ap_thread(st, true); |
| 282 | if (WARN_ON_ONCE((!cpu_online(cpu)))) | 422 | if (WARN_ON_ONCE((!cpu_online(cpu)))) |
| 283 | return -ECANCELED; | 423 | return -ECANCELED; |
| 284 | 424 | ||
| @@ -286,12 +426,10 @@ static int bringup_wait_for_ap(unsigned int cpu) | |||
| 286 | stop_machine_unpark(cpu); | 426 | stop_machine_unpark(cpu); |
| 287 | kthread_unpark(st->thread); | 427 | kthread_unpark(st->thread); |
| 288 | 428 | ||
| 289 | /* Should we go further up ? */ | 429 | if (st->target <= CPUHP_AP_ONLINE_IDLE) |
| 290 | if (st->target > CPUHP_AP_ONLINE_IDLE) { | 430 | return 0; |
| 291 | __cpuhp_kick_ap_work(st); | 431 | |
| 292 | wait_for_completion(&st->done); | 432 | return cpuhp_kick_ap(st, st->target); |
| 293 | } | ||
| 294 | return st->result; | ||
| 295 | } | 433 | } |
| 296 | 434 | ||
| 297 | static int bringup_cpu(unsigned int cpu) | 435 | static int bringup_cpu(unsigned int cpu) |
| @@ -317,32 +455,6 @@ static int bringup_cpu(unsigned int cpu) | |||
| 317 | /* | 455 | /* |
| 318 | * Hotplug state machine related functions | 456 | * Hotplug state machine related functions |
| 319 | */ | 457 | */ |
| 320 | static void undo_cpu_down(unsigned int cpu, struct cpuhp_cpu_state *st) | ||
| 321 | { | ||
| 322 | for (st->state++; st->state < st->target; st->state++) { | ||
| 323 | struct cpuhp_step *step = cpuhp_get_step(st->state); | ||
| 324 | |||
| 325 | if (!step->skip_onerr) | ||
| 326 | cpuhp_invoke_callback(cpu, st->state, true, NULL); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, | ||
| 331 | enum cpuhp_state target) | ||
| 332 | { | ||
| 333 | enum cpuhp_state prev_state = st->state; | ||
| 334 | int ret = 0; | ||
| 335 | |||
| 336 | for (; st->state > target; st->state--) { | ||
| 337 | ret = cpuhp_invoke_callback(cpu, st->state, false, NULL); | ||
| 338 | if (ret) { | ||
| 339 | st->target = prev_state; | ||
| 340 | undo_cpu_down(cpu, st); | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | return ret; | ||
| 345 | } | ||
| 346 | 458 | ||
| 347 | static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st) | 459 | static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st) |
| 348 | { | 460 | { |
| @@ -350,7 +462,7 @@ static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st) | |||
| 350 | struct cpuhp_step *step = cpuhp_get_step(st->state); | 462 | struct cpuhp_step *step = cpuhp_get_step(st->state); |
| 351 | 463 | ||
| 352 | if (!step->skip_onerr) | 464 | if (!step->skip_onerr) |
| 353 | cpuhp_invoke_callback(cpu, st->state, false, NULL); | 465 | cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); |
| 354 | } | 466 | } |
| 355 | } | 467 | } |
| 356 | 468 | ||
| @@ -362,7 +474,7 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, | |||
| 362 | 474 | ||
| 363 | while (st->state < target) { | 475 | while (st->state < target) { |
| 364 | st->state++; | 476 | st->state++; |
| 365 | ret = cpuhp_invoke_callback(cpu, st->state, true, NULL); | 477 | ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL); |
| 366 | if (ret) { | 478 | if (ret) { |
| 367 | st->target = prev_state; | 479 | st->target = prev_state; |
| 368 | undo_cpu_up(cpu, st); | 480 | undo_cpu_up(cpu, st); |
| @@ -379,7 +491,8 @@ static void cpuhp_create(unsigned int cpu) | |||
| 379 | { | 491 | { |
| 380 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 492 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 381 | 493 | ||
| 382 | init_completion(&st->done); | 494 | init_completion(&st->done_up); |
| 495 | init_completion(&st->done_down); | ||
| 383 | } | 496 | } |
| 384 | 497 | ||
| 385 | static int cpuhp_should_run(unsigned int cpu) | 498 | static int cpuhp_should_run(unsigned int cpu) |
| @@ -389,69 +502,90 @@ static int cpuhp_should_run(unsigned int cpu) | |||
| 389 | return st->should_run; | 502 | return st->should_run; |
| 390 | } | 503 | } |
| 391 | 504 | ||
| 392 | /* Execute the teardown callbacks. Used to be CPU_DOWN_PREPARE */ | ||
| 393 | static int cpuhp_ap_offline(unsigned int cpu, struct cpuhp_cpu_state *st) | ||
| 394 | { | ||
| 395 | enum cpuhp_state target = max((int)st->target, CPUHP_TEARDOWN_CPU); | ||
| 396 | |||
| 397 | return cpuhp_down_callbacks(cpu, st, target); | ||
| 398 | } | ||
| 399 | |||
| 400 | /* Execute the online startup callbacks. Used to be CPU_ONLINE */ | ||
| 401 | static int cpuhp_ap_online(unsigned int cpu, struct cpuhp_cpu_state *st) | ||
| 402 | { | ||
| 403 | return cpuhp_up_callbacks(cpu, st, st->target); | ||
| 404 | } | ||
| 405 | |||
| 406 | /* | 505 | /* |
| 407 | * Execute teardown/startup callbacks on the plugged cpu. Also used to invoke | 506 | * Execute teardown/startup callbacks on the plugged cpu. Also used to invoke |
| 408 | * callbacks when a state gets [un]installed at runtime. | 507 | * callbacks when a state gets [un]installed at runtime. |
| 508 | * | ||
| 509 | * Each invocation of this function by the smpboot thread does a single AP | ||
| 510 | * state callback. | ||
| 511 | * | ||
| 512 | * It has 3 modes of operation: | ||
| 513 | * - single: runs st->cb_state | ||
| 514 | * - up: runs ++st->state, while st->state < st->target | ||
| 515 | * - down: runs st->state--, while st->state > st->target | ||
| 516 | * | ||
| 517 | * When complete or on error, should_run is cleared and the completion is fired. | ||
| 409 | */ | 518 | */ |
| 410 | static void cpuhp_thread_fun(unsigned int cpu) | 519 | static void cpuhp_thread_fun(unsigned int cpu) |
| 411 | { | 520 | { |
| 412 | struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); | 521 | struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); |
| 413 | int ret = 0; | 522 | bool bringup = st->bringup; |
| 523 | enum cpuhp_state state; | ||
| 414 | 524 | ||
| 415 | /* | 525 | /* |
| 416 | * Paired with the mb() in cpuhp_kick_ap_work and | 526 | * ACQUIRE for the cpuhp_should_run() load of ->should_run. Ensures |
| 417 | * cpuhp_invoke_ap_callback, so the work set is consistent visible. | 527 | * that if we see ->should_run we also see the rest of the state. |
| 418 | */ | 528 | */ |
| 419 | smp_mb(); | 529 | smp_mb(); |
| 420 | if (!st->should_run) | 530 | |
| 531 | if (WARN_ON_ONCE(!st->should_run)) | ||
| 421 | return; | 532 | return; |
| 422 | 533 | ||
| 423 | st->should_run = false; | 534 | cpuhp_lock_acquire(bringup); |
| 424 | 535 | ||
| 425 | lock_map_acquire(&cpuhp_state_lock_map); | ||
| 426 | /* Single callback invocation for [un]install ? */ | ||
| 427 | if (st->single) { | 536 | if (st->single) { |
| 428 | if (st->cb_state < CPUHP_AP_ONLINE) { | 537 | state = st->cb_state; |
| 429 | local_irq_disable(); | 538 | st->should_run = false; |
| 430 | ret = cpuhp_invoke_callback(cpu, st->cb_state, | 539 | } else { |
| 431 | st->bringup, st->node); | 540 | if (bringup) { |
| 432 | local_irq_enable(); | 541 | st->state++; |
| 542 | state = st->state; | ||
| 543 | st->should_run = (st->state < st->target); | ||
| 544 | WARN_ON_ONCE(st->state > st->target); | ||
| 433 | } else { | 545 | } else { |
| 434 | ret = cpuhp_invoke_callback(cpu, st->cb_state, | 546 | state = st->state; |
| 435 | st->bringup, st->node); | 547 | st->state--; |
| 548 | st->should_run = (st->state > st->target); | ||
| 549 | WARN_ON_ONCE(st->state < st->target); | ||
| 436 | } | 550 | } |
| 437 | } else if (st->rollback) { | 551 | } |
| 438 | BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); | 552 | |
| 553 | WARN_ON_ONCE(!cpuhp_is_ap_state(state)); | ||
| 554 | |||
| 555 | if (st->rollback) { | ||
| 556 | struct cpuhp_step *step = cpuhp_get_step(state); | ||
| 557 | if (step->skip_onerr) | ||
| 558 | goto next; | ||
| 559 | } | ||
| 560 | |||
| 561 | if (cpuhp_is_atomic_state(state)) { | ||
| 562 | local_irq_disable(); | ||
| 563 | st->result = cpuhp_invoke_callback(cpu, state, bringup, st->node, &st->last); | ||
| 564 | local_irq_enable(); | ||
| 439 | 565 | ||
| 440 | undo_cpu_down(cpu, st); | 566 | /* |
| 441 | st->rollback = false; | 567 | * STARTING/DYING must not fail! |
| 568 | */ | ||
| 569 | WARN_ON_ONCE(st->result); | ||
| 442 | } else { | 570 | } else { |
| 443 | /* Cannot happen .... */ | 571 | st->result = cpuhp_invoke_callback(cpu, state, bringup, st->node, &st->last); |
| 444 | BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); | 572 | } |
| 445 | 573 | ||
| 446 | /* Regular hotplug work */ | 574 | if (st->result) { |
| 447 | if (st->state < st->target) | 575 | /* |
| 448 | ret = cpuhp_ap_online(cpu, st); | 576 | * If we fail on a rollback, we're up a creek without no |
| 449 | else if (st->state > st->target) | 577 | * paddle, no way forward, no way back. We loose, thanks for |
| 450 | ret = cpuhp_ap_offline(cpu, st); | 578 | * playing. |
| 579 | */ | ||
| 580 | WARN_ON_ONCE(st->rollback); | ||
| 581 | st->should_run = false; | ||
| 451 | } | 582 | } |
| 452 | lock_map_release(&cpuhp_state_lock_map); | 583 | |
| 453 | st->result = ret; | 584 | next: |
| 454 | complete(&st->done); | 585 | cpuhp_lock_release(bringup); |
| 586 | |||
| 587 | if (!st->should_run) | ||
| 588 | complete_ap_thread(st, bringup); | ||
| 455 | } | 589 | } |
| 456 | 590 | ||
| 457 | /* Invoke a single callback on a remote cpu */ | 591 | /* Invoke a single callback on a remote cpu */ |
| @@ -460,62 +594,64 @@ cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup, | |||
| 460 | struct hlist_node *node) | 594 | struct hlist_node *node) |
| 461 | { | 595 | { |
| 462 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 596 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 597 | int ret; | ||
| 463 | 598 | ||
| 464 | if (!cpu_online(cpu)) | 599 | if (!cpu_online(cpu)) |
| 465 | return 0; | 600 | return 0; |
| 466 | 601 | ||
| 467 | lock_map_acquire(&cpuhp_state_lock_map); | 602 | cpuhp_lock_acquire(false); |
| 468 | lock_map_release(&cpuhp_state_lock_map); | 603 | cpuhp_lock_release(false); |
| 604 | |||
| 605 | cpuhp_lock_acquire(true); | ||
| 606 | cpuhp_lock_release(true); | ||
| 469 | 607 | ||
| 470 | /* | 608 | /* |
| 471 | * If we are up and running, use the hotplug thread. For early calls | 609 | * If we are up and running, use the hotplug thread. For early calls |
| 472 | * we invoke the thread function directly. | 610 | * we invoke the thread function directly. |
| 473 | */ | 611 | */ |
| 474 | if (!st->thread) | 612 | if (!st->thread) |
| 475 | return cpuhp_invoke_callback(cpu, state, bringup, node); | 613 | return cpuhp_invoke_callback(cpu, state, bringup, node, NULL); |
| 614 | |||
| 615 | st->rollback = false; | ||
| 616 | st->last = NULL; | ||
| 476 | 617 | ||
| 618 | st->node = node; | ||
| 619 | st->bringup = bringup; | ||
| 477 | st->cb_state = state; | 620 | st->cb_state = state; |
| 478 | st->single = true; | 621 | st->single = true; |
| 479 | st->bringup = bringup; | ||
| 480 | st->node = node; | ||
| 481 | 622 | ||
| 482 | /* | 623 | __cpuhp_kick_ap(st); |
| 483 | * Make sure the above stores are visible before should_run becomes | ||
| 484 | * true. Paired with the mb() above in cpuhp_thread_fun() | ||
| 485 | */ | ||
| 486 | smp_mb(); | ||
| 487 | st->should_run = true; | ||
| 488 | wake_up_process(st->thread); | ||
| 489 | wait_for_completion(&st->done); | ||
| 490 | return st->result; | ||
| 491 | } | ||
| 492 | 624 | ||
| 493 | /* Regular hotplug invocation of the AP hotplug thread */ | ||
| 494 | static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st) | ||
| 495 | { | ||
| 496 | st->result = 0; | ||
| 497 | st->single = false; | ||
| 498 | /* | 625 | /* |
| 499 | * Make sure the above stores are visible before should_run becomes | 626 | * If we failed and did a partial, do a rollback. |
| 500 | * true. Paired with the mb() above in cpuhp_thread_fun() | ||
| 501 | */ | 627 | */ |
| 502 | smp_mb(); | 628 | if ((ret = st->result) && st->last) { |
| 503 | st->should_run = true; | 629 | st->rollback = true; |
| 504 | wake_up_process(st->thread); | 630 | st->bringup = !bringup; |
| 631 | |||
| 632 | __cpuhp_kick_ap(st); | ||
| 633 | } | ||
| 634 | |||
| 635 | return ret; | ||
| 505 | } | 636 | } |
| 506 | 637 | ||
| 507 | static int cpuhp_kick_ap_work(unsigned int cpu) | 638 | static int cpuhp_kick_ap_work(unsigned int cpu) |
| 508 | { | 639 | { |
| 509 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 640 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 510 | enum cpuhp_state state = st->state; | 641 | enum cpuhp_state prev_state = st->state; |
| 642 | int ret; | ||
| 643 | |||
| 644 | cpuhp_lock_acquire(false); | ||
| 645 | cpuhp_lock_release(false); | ||
| 511 | 646 | ||
| 512 | trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work); | 647 | cpuhp_lock_acquire(true); |
| 513 | lock_map_acquire(&cpuhp_state_lock_map); | 648 | cpuhp_lock_release(true); |
| 514 | lock_map_release(&cpuhp_state_lock_map); | 649 | |
| 515 | __cpuhp_kick_ap_work(st); | 650 | trace_cpuhp_enter(cpu, st->target, prev_state, cpuhp_kick_ap_work); |
| 516 | wait_for_completion(&st->done); | 651 | ret = cpuhp_kick_ap(st, st->target); |
| 517 | trace_cpuhp_exit(cpu, st->state, state, st->result); | 652 | trace_cpuhp_exit(cpu, st->state, prev_state, ret); |
| 518 | return st->result; | 653 | |
| 654 | return ret; | ||
| 519 | } | 655 | } |
| 520 | 656 | ||
| 521 | static struct smp_hotplug_thread cpuhp_threads = { | 657 | static struct smp_hotplug_thread cpuhp_threads = { |
| @@ -581,6 +717,7 @@ static int take_cpu_down(void *_param) | |||
| 581 | struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); | 717 | struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); |
| 582 | enum cpuhp_state target = max((int)st->target, CPUHP_AP_OFFLINE); | 718 | enum cpuhp_state target = max((int)st->target, CPUHP_AP_OFFLINE); |
| 583 | int err, cpu = smp_processor_id(); | 719 | int err, cpu = smp_processor_id(); |
| 720 | int ret; | ||
| 584 | 721 | ||
| 585 | /* Ensure this CPU doesn't handle any more interrupts. */ | 722 | /* Ensure this CPU doesn't handle any more interrupts. */ |
| 586 | err = __cpu_disable(); | 723 | err = __cpu_disable(); |
| @@ -594,8 +731,13 @@ static int take_cpu_down(void *_param) | |||
| 594 | WARN_ON(st->state != CPUHP_TEARDOWN_CPU); | 731 | WARN_ON(st->state != CPUHP_TEARDOWN_CPU); |
| 595 | st->state--; | 732 | st->state--; |
| 596 | /* Invoke the former CPU_DYING callbacks */ | 733 | /* Invoke the former CPU_DYING callbacks */ |
| 597 | for (; st->state > target; st->state--) | 734 | for (; st->state > target; st->state--) { |
| 598 | cpuhp_invoke_callback(cpu, st->state, false, NULL); | 735 | ret = cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); |
| 736 | /* | ||
| 737 | * DYING must not fail! | ||
| 738 | */ | ||
| 739 | WARN_ON_ONCE(ret); | ||
| 740 | } | ||
| 599 | 741 | ||
| 600 | /* Give up timekeeping duties */ | 742 | /* Give up timekeeping duties */ |
| 601 | tick_handover_do_timer(); | 743 | tick_handover_do_timer(); |
| @@ -639,7 +781,7 @@ static int takedown_cpu(unsigned int cpu) | |||
| 639 | * | 781 | * |
| 640 | * Wait for the stop thread to go away. | 782 | * Wait for the stop thread to go away. |
| 641 | */ | 783 | */ |
| 642 | wait_for_completion(&st->done); | 784 | wait_for_ap_thread(st, false); |
| 643 | BUG_ON(st->state != CPUHP_AP_IDLE_DEAD); | 785 | BUG_ON(st->state != CPUHP_AP_IDLE_DEAD); |
| 644 | 786 | ||
| 645 | /* Interrupts are moved away from the dying cpu, reenable alloc/free */ | 787 | /* Interrupts are moved away from the dying cpu, reenable alloc/free */ |
| @@ -658,7 +800,7 @@ static void cpuhp_complete_idle_dead(void *arg) | |||
| 658 | { | 800 | { |
| 659 | struct cpuhp_cpu_state *st = arg; | 801 | struct cpuhp_cpu_state *st = arg; |
| 660 | 802 | ||
| 661 | complete(&st->done); | 803 | complete_ap_thread(st, false); |
| 662 | } | 804 | } |
| 663 | 805 | ||
| 664 | void cpuhp_report_idle_dead(void) | 806 | void cpuhp_report_idle_dead(void) |
| @@ -676,11 +818,32 @@ void cpuhp_report_idle_dead(void) | |||
| 676 | cpuhp_complete_idle_dead, st, 0); | 818 | cpuhp_complete_idle_dead, st, 0); |
| 677 | } | 819 | } |
| 678 | 820 | ||
| 679 | #else | 821 | static void undo_cpu_down(unsigned int cpu, struct cpuhp_cpu_state *st) |
| 680 | #define takedown_cpu NULL | 822 | { |
| 681 | #endif | 823 | for (st->state++; st->state < st->target; st->state++) { |
| 824 | struct cpuhp_step *step = cpuhp_get_step(st->state); | ||
| 682 | 825 | ||
| 683 | #ifdef CONFIG_HOTPLUG_CPU | 826 | if (!step->skip_onerr) |
| 827 | cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL); | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, | ||
| 832 | enum cpuhp_state target) | ||
| 833 | { | ||
| 834 | enum cpuhp_state prev_state = st->state; | ||
| 835 | int ret = 0; | ||
| 836 | |||
| 837 | for (; st->state > target; st->state--) { | ||
| 838 | ret = cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL); | ||
| 839 | if (ret) { | ||
| 840 | st->target = prev_state; | ||
| 841 | undo_cpu_down(cpu, st); | ||
| 842 | break; | ||
| 843 | } | ||
| 844 | } | ||
| 845 | return ret; | ||
| 846 | } | ||
| 684 | 847 | ||
| 685 | /* Requires cpu_add_remove_lock to be held */ | 848 | /* Requires cpu_add_remove_lock to be held */ |
| 686 | static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, | 849 | static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, |
| @@ -699,13 +862,13 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, | |||
| 699 | 862 | ||
| 700 | cpuhp_tasks_frozen = tasks_frozen; | 863 | cpuhp_tasks_frozen = tasks_frozen; |
| 701 | 864 | ||
| 702 | prev_state = st->state; | 865 | prev_state = cpuhp_set_state(st, target); |
| 703 | st->target = target; | ||
| 704 | /* | 866 | /* |
| 705 | * If the current CPU state is in the range of the AP hotplug thread, | 867 | * If the current CPU state is in the range of the AP hotplug thread, |
| 706 | * then we need to kick the thread. | 868 | * then we need to kick the thread. |
| 707 | */ | 869 | */ |
| 708 | if (st->state > CPUHP_TEARDOWN_CPU) { | 870 | if (st->state > CPUHP_TEARDOWN_CPU) { |
| 871 | st->target = max((int)target, CPUHP_TEARDOWN_CPU); | ||
| 709 | ret = cpuhp_kick_ap_work(cpu); | 872 | ret = cpuhp_kick_ap_work(cpu); |
| 710 | /* | 873 | /* |
| 711 | * The AP side has done the error rollback already. Just | 874 | * The AP side has done the error rollback already. Just |
| @@ -720,6 +883,8 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, | |||
| 720 | */ | 883 | */ |
| 721 | if (st->state > CPUHP_TEARDOWN_CPU) | 884 | if (st->state > CPUHP_TEARDOWN_CPU) |
| 722 | goto out; | 885 | goto out; |
| 886 | |||
| 887 | st->target = target; | ||
| 723 | } | 888 | } |
| 724 | /* | 889 | /* |
| 725 | * The AP brought itself down to CPUHP_TEARDOWN_CPU. So we need | 890 | * The AP brought itself down to CPUHP_TEARDOWN_CPU. So we need |
| @@ -727,13 +892,17 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, | |||
| 727 | */ | 892 | */ |
| 728 | ret = cpuhp_down_callbacks(cpu, st, target); | 893 | ret = cpuhp_down_callbacks(cpu, st, target); |
| 729 | if (ret && st->state > CPUHP_TEARDOWN_CPU && st->state < prev_state) { | 894 | if (ret && st->state > CPUHP_TEARDOWN_CPU && st->state < prev_state) { |
| 730 | st->target = prev_state; | 895 | cpuhp_reset_state(st, prev_state); |
| 731 | st->rollback = true; | 896 | __cpuhp_kick_ap(st); |
| 732 | cpuhp_kick_ap_work(cpu); | ||
| 733 | } | 897 | } |
| 734 | 898 | ||
| 735 | out: | 899 | out: |
| 736 | cpus_write_unlock(); | 900 | cpus_write_unlock(); |
| 901 | /* | ||
| 902 | * Do post unplug cleanup. This is still protected against | ||
| 903 | * concurrent CPU hotplug via cpu_add_remove_lock. | ||
| 904 | */ | ||
| 905 | lockup_detector_cleanup(); | ||
| 737 | return ret; | 906 | return ret; |
| 738 | } | 907 | } |
| 739 | 908 | ||
| @@ -754,11 +923,15 @@ out: | |||
| 754 | cpu_maps_update_done(); | 923 | cpu_maps_update_done(); |
| 755 | return err; | 924 | return err; |
| 756 | } | 925 | } |
| 926 | |||
| 757 | int cpu_down(unsigned int cpu) | 927 | int cpu_down(unsigned int cpu) |
| 758 | { | 928 | { |
| 759 | return do_cpu_down(cpu, CPUHP_OFFLINE); | 929 | return do_cpu_down(cpu, CPUHP_OFFLINE); |
| 760 | } | 930 | } |
| 761 | EXPORT_SYMBOL(cpu_down); | 931 | EXPORT_SYMBOL(cpu_down); |
| 932 | |||
| 933 | #else | ||
| 934 | #define takedown_cpu NULL | ||
| 762 | #endif /*CONFIG_HOTPLUG_CPU*/ | 935 | #endif /*CONFIG_HOTPLUG_CPU*/ |
| 763 | 936 | ||
| 764 | /** | 937 | /** |
| @@ -772,11 +945,16 @@ void notify_cpu_starting(unsigned int cpu) | |||
| 772 | { | 945 | { |
| 773 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | 946 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 774 | enum cpuhp_state target = min((int)st->target, CPUHP_AP_ONLINE); | 947 | enum cpuhp_state target = min((int)st->target, CPUHP_AP_ONLINE); |
| 948 | int ret; | ||
| 775 | 949 | ||
| 776 | rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */ | 950 | rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */ |
| 777 | while (st->state < target) { | 951 | while (st->state < target) { |
| 778 | st->state++; | 952 | st->state++; |
| 779 | cpuhp_invoke_callback(cpu, st->state, true, NULL); | 953 | ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL); |
| 954 | /* | ||
| 955 | * STARTING must not fail! | ||
| 956 | */ | ||
| 957 | WARN_ON_ONCE(ret); | ||
| 780 | } | 958 | } |
| 781 | } | 959 | } |
| 782 | 960 | ||
| @@ -794,7 +972,7 @@ void cpuhp_online_idle(enum cpuhp_state state) | |||
| 794 | return; | 972 | return; |
| 795 | 973 | ||
| 796 | st->state = CPUHP_AP_ONLINE_IDLE; | 974 | st->state = CPUHP_AP_ONLINE_IDLE; |
| 797 | complete(&st->done); | 975 | complete_ap_thread(st, true); |
| 798 | } | 976 | } |
| 799 | 977 | ||
| 800 | /* Requires cpu_add_remove_lock to be held */ | 978 | /* Requires cpu_add_remove_lock to be held */ |
| @@ -829,7 +1007,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target) | |||
| 829 | 1007 | ||
| 830 | cpuhp_tasks_frozen = tasks_frozen; | 1008 | cpuhp_tasks_frozen = tasks_frozen; |
| 831 | 1009 | ||
| 832 | st->target = target; | 1010 | cpuhp_set_state(st, target); |
| 833 | /* | 1011 | /* |
| 834 | * If the current CPU state is in the range of the AP hotplug thread, | 1012 | * If the current CPU state is in the range of the AP hotplug thread, |
| 835 | * then we need to kick the thread once more. | 1013 | * then we need to kick the thread once more. |
| @@ -1296,6 +1474,10 @@ static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup, | |||
| 1296 | struct cpuhp_step *sp = cpuhp_get_step(state); | 1474 | struct cpuhp_step *sp = cpuhp_get_step(state); |
| 1297 | int ret; | 1475 | int ret; |
| 1298 | 1476 | ||
| 1477 | /* | ||
| 1478 | * If there's nothing to do, we done. | ||
| 1479 | * Relies on the union for multi_instance. | ||
| 1480 | */ | ||
| 1299 | if ((bringup && !sp->startup.single) || | 1481 | if ((bringup && !sp->startup.single) || |
| 1300 | (!bringup && !sp->teardown.single)) | 1482 | (!bringup && !sp->teardown.single)) |
| 1301 | return 0; | 1483 | return 0; |
| @@ -1307,9 +1489,9 @@ static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup, | |||
| 1307 | if (cpuhp_is_ap_state(state)) | 1489 | if (cpuhp_is_ap_state(state)) |
| 1308 | ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node); | 1490 | ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node); |
| 1309 | else | 1491 | else |
| 1310 | ret = cpuhp_invoke_callback(cpu, state, bringup, node); | 1492 | ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL); |
| 1311 | #else | 1493 | #else |
| 1312 | ret = cpuhp_invoke_callback(cpu, state, bringup, node); | 1494 | ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL); |
| 1313 | #endif | 1495 | #endif |
| 1314 | BUG_ON(ret && !bringup); | 1496 | BUG_ON(ret && !bringup); |
| 1315 | return ret; | 1497 | return ret; |
| @@ -1641,9 +1823,55 @@ static ssize_t show_cpuhp_target(struct device *dev, | |||
| 1641 | } | 1823 | } |
| 1642 | static DEVICE_ATTR(target, 0644, show_cpuhp_target, write_cpuhp_target); | 1824 | static DEVICE_ATTR(target, 0644, show_cpuhp_target, write_cpuhp_target); |
| 1643 | 1825 | ||
| 1826 | |||
| 1827 | static ssize_t write_cpuhp_fail(struct device *dev, | ||
| 1828 | struct device_attribute *attr, | ||
| 1829 | const char *buf, size_t count) | ||
| 1830 | { | ||
| 1831 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, dev->id); | ||
| 1832 | struct cpuhp_step *sp; | ||
| 1833 | int fail, ret; | ||
| 1834 | |||
| 1835 | ret = kstrtoint(buf, 10, &fail); | ||
| 1836 | if (ret) | ||
| 1837 | return ret; | ||
| 1838 | |||
| 1839 | /* | ||
| 1840 | * Cannot fail STARTING/DYING callbacks. | ||
| 1841 | */ | ||
| 1842 | if (cpuhp_is_atomic_state(fail)) | ||
| 1843 | return -EINVAL; | ||
| 1844 | |||
| 1845 | /* | ||
| 1846 | * Cannot fail anything that doesn't have callbacks. | ||
| 1847 | */ | ||
| 1848 | mutex_lock(&cpuhp_state_mutex); | ||
| 1849 | sp = cpuhp_get_step(fail); | ||
| 1850 | if (!sp->startup.single && !sp->teardown.single) | ||
| 1851 | ret = -EINVAL; | ||
| 1852 | mutex_unlock(&cpuhp_state_mutex); | ||
| 1853 | if (ret) | ||
| 1854 | return ret; | ||
| 1855 | |||
| 1856 | st->fail = fail; | ||
| 1857 | |||
| 1858 | return count; | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | static ssize_t show_cpuhp_fail(struct device *dev, | ||
| 1862 | struct device_attribute *attr, char *buf) | ||
| 1863 | { | ||
| 1864 | struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, dev->id); | ||
| 1865 | |||
| 1866 | return sprintf(buf, "%d\n", st->fail); | ||
| 1867 | } | ||
| 1868 | |||
| 1869 | static DEVICE_ATTR(fail, 0644, show_cpuhp_fail, write_cpuhp_fail); | ||
| 1870 | |||
| 1644 | static struct attribute *cpuhp_cpu_attrs[] = { | 1871 | static struct attribute *cpuhp_cpu_attrs[] = { |
| 1645 | &dev_attr_state.attr, | 1872 | &dev_attr_state.attr, |
| 1646 | &dev_attr_target.attr, | 1873 | &dev_attr_target.attr, |
| 1874 | &dev_attr_fail.attr, | ||
| 1647 | NULL | 1875 | NULL |
| 1648 | }; | 1876 | }; |
| 1649 | 1877 | ||
