|
22 | 22 | #include <linux/lockdep.h> |
23 | 23 | #include <linux/tick.h> |
24 | 24 | #include <linux/irq.h> |
| 25 | +#include <linux/smpboot.h> |
25 | 26 |
|
26 | 27 | #include <trace/events/power.h> |
27 | 28 | #define CREATE_TRACE_POINTS |
|
33 | 34 | * cpuhp_cpu_state - Per cpu hotplug state storage |
34 | 35 | * @state: The current cpu state |
35 | 36 | * @target: The target state |
| 37 | + * @thread: Pointer to the hotplug thread |
| 38 | + * @should_run: Thread should execute |
| 39 | + * @cb_stat: The state for a single callback (install/uninstall) |
| 40 | + * @cb: Single callback function (install/uninstall) |
| 41 | + * @result: Result of the operation |
| 42 | + * @done: Signal completion to the issuer of the task |
36 | 43 | */ |
37 | 44 | struct cpuhp_cpu_state { |
38 | 45 | enum cpuhp_state state; |
39 | 46 | enum cpuhp_state target; |
| 47 | +#ifdef CONFIG_SMP |
| 48 | + struct task_struct *thread; |
| 49 | + bool should_run; |
| 50 | + enum cpuhp_state cb_state; |
| 51 | + int (*cb)(unsigned int cpu); |
| 52 | + int result; |
| 53 | + struct completion done; |
| 54 | +#endif |
40 | 55 | }; |
41 | 56 |
|
42 | 57 | static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); |
@@ -394,6 +409,134 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, |
394 | 409 | return ret; |
395 | 410 | } |
396 | 411 |
|
| 412 | +/* |
| 413 | + * The cpu hotplug threads manage the bringup and teardown of the cpus |
| 414 | + */ |
| 415 | +static void cpuhp_create(unsigned int cpu) |
| 416 | +{ |
| 417 | + struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 418 | + |
| 419 | + init_completion(&st->done); |
| 420 | +} |
| 421 | + |
| 422 | +static int cpuhp_should_run(unsigned int cpu) |
| 423 | +{ |
| 424 | + struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); |
| 425 | + |
| 426 | + return st->should_run; |
| 427 | +} |
| 428 | + |
| 429 | +/* Execute the teardown callbacks. Used to be CPU_DOWN_PREPARE */ |
| 430 | +static int cpuhp_ap_offline(unsigned int cpu, struct cpuhp_cpu_state *st) |
| 431 | +{ |
| 432 | + enum cpuhp_state target = max((int)st->target, CPUHP_AP_ONLINE); |
| 433 | + |
| 434 | + return cpuhp_down_callbacks(cpu, st, cpuhp_ap_states, target); |
| 435 | +} |
| 436 | + |
| 437 | +/* Execute the online startup callbacks. Used to be CPU_ONLINE */ |
| 438 | +static int cpuhp_ap_online(unsigned int cpu, struct cpuhp_cpu_state *st) |
| 439 | +{ |
| 440 | + return cpuhp_up_callbacks(cpu, st, cpuhp_ap_states, st->target); |
| 441 | +} |
| 442 | + |
| 443 | +/* |
| 444 | + * Execute teardown/startup callbacks on the plugged cpu. Also used to invoke |
| 445 | + * callbacks when a state gets [un]installed at runtime. |
| 446 | + */ |
| 447 | +static void cpuhp_thread_fun(unsigned int cpu) |
| 448 | +{ |
| 449 | + struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); |
| 450 | + int ret = 0; |
| 451 | + |
| 452 | + /* |
| 453 | + * Paired with the mb() in cpuhp_kick_ap_work and |
| 454 | + * cpuhp_invoke_ap_callback, so the work set is consistent visible. |
| 455 | + */ |
| 456 | + smp_mb(); |
| 457 | + if (!st->should_run) |
| 458 | + return; |
| 459 | + |
| 460 | + st->should_run = false; |
| 461 | + |
| 462 | + /* Single callback invocation for [un]install ? */ |
| 463 | + if (st->cb) { |
| 464 | + if (st->cb_state < CPUHP_AP_ONLINE) { |
| 465 | + local_irq_disable(); |
| 466 | + ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb); |
| 467 | + local_irq_enable(); |
| 468 | + } else { |
| 469 | + ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb); |
| 470 | + } |
| 471 | + } else { |
| 472 | + /* Regular hotplug work */ |
| 473 | + if (st->state < st->target) |
| 474 | + ret = cpuhp_ap_online(cpu, st); |
| 475 | + else if (st->state > st->target) |
| 476 | + ret = cpuhp_ap_offline(cpu, st); |
| 477 | + } |
| 478 | + st->result = ret; |
| 479 | + complete(&st->done); |
| 480 | +} |
| 481 | + |
| 482 | +/* Invoke a single callback on a remote cpu */ |
| 483 | +static int cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, |
| 484 | + int (*cb)(unsigned int)) |
| 485 | +{ |
| 486 | + struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 487 | + |
| 488 | + if (!cpu_online(cpu)) |
| 489 | + return 0; |
| 490 | + |
| 491 | + st->cb_state = state; |
| 492 | + st->cb = cb; |
| 493 | + /* |
| 494 | + * Make sure the above stores are visible before should_run becomes |
| 495 | + * true. Paired with the mb() above in cpuhp_thread_fun() |
| 496 | + */ |
| 497 | + smp_mb(); |
| 498 | + st->should_run = true; |
| 499 | + wake_up_process(st->thread); |
| 500 | + wait_for_completion(&st->done); |
| 501 | + return st->result; |
| 502 | +} |
| 503 | + |
| 504 | +/* Regular hotplug invocation of the AP hotplug thread */ |
| 505 | +static int cpuhp_kick_ap_work(unsigned int cpu) |
| 506 | +{ |
| 507 | + struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); |
| 508 | + enum cpuhp_state state = st->state; |
| 509 | + |
| 510 | + trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work); |
| 511 | + st->result = 0; |
| 512 | + st->cb = NULL; |
| 513 | + /* |
| 514 | + * Make sure the above stores are visible before should_run becomes |
| 515 | + * true. Paired with the mb() above in cpuhp_thread_fun() |
| 516 | + */ |
| 517 | + smp_mb(); |
| 518 | + st->should_run = true; |
| 519 | + wake_up_process(st->thread); |
| 520 | + wait_for_completion(&st->done); |
| 521 | + trace_cpuhp_exit(cpu, st->state, state, st->result); |
| 522 | + return st->result; |
| 523 | +} |
| 524 | + |
| 525 | +static struct smp_hotplug_thread cpuhp_threads = { |
| 526 | + .store = &cpuhp_state.thread, |
| 527 | + .create = &cpuhp_create, |
| 528 | + .thread_should_run = cpuhp_should_run, |
| 529 | + .thread_fn = cpuhp_thread_fun, |
| 530 | + .thread_comm = "cpuhp/%u", |
| 531 | + .selfparking = true, |
| 532 | +}; |
| 533 | + |
| 534 | +void __init cpuhp_threads_init(void) |
| 535 | +{ |
| 536 | + BUG_ON(smpboot_register_percpu_thread(&cpuhp_threads)); |
| 537 | + kthread_unpark(this_cpu_read(cpuhp_state.thread)); |
| 538 | +} |
| 539 | + |
397 | 540 | #ifdef CONFIG_HOTPLUG_CPU |
398 | 541 | EXPORT_SYMBOL(register_cpu_notifier); |
399 | 542 | EXPORT_SYMBOL(__register_cpu_notifier); |
@@ -997,7 +1140,7 @@ static int cpuhp_cb_check(enum cpuhp_state state) |
997 | 1140 |
|
998 | 1141 | static bool cpuhp_is_ap_state(enum cpuhp_state state) |
999 | 1142 | { |
1000 | | - return (state > CPUHP_AP_OFFLINE && state < CPUHP_AP_ONLINE); |
| 1143 | + return (state >= CPUHP_AP_OFFLINE && state <= CPUHP_AP_ONLINE); |
1001 | 1144 | } |
1002 | 1145 |
|
1003 | 1146 | static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state) |
|
0 commit comments