diff options
Diffstat (limited to 'kernel/cpu.c')
| -rw-r--r-- | kernel/cpu.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 12b7458f23b1..aa39dd7a3846 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/stop_machine.h> | 15 | #include <linux/stop_machine.h> |
| 16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
| 17 | #include <linux/gfp.h> | 17 | #include <linux/gfp.h> |
| 18 | #include <linux/suspend.h> | ||
| 18 | 19 | ||
| 19 | #ifdef CONFIG_SMP | 20 | #ifdef CONFIG_SMP |
| 20 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ | 21 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ |
| @@ -476,6 +477,79 @@ static int alloc_frozen_cpus(void) | |||
| 476 | return 0; | 477 | return 0; |
| 477 | } | 478 | } |
| 478 | core_initcall(alloc_frozen_cpus); | 479 | core_initcall(alloc_frozen_cpus); |
| 480 | |||
| 481 | /* | ||
| 482 | * Prevent regular CPU hotplug from racing with the freezer, by disabling CPU | ||
| 483 | * hotplug when tasks are about to be frozen. Also, don't allow the freezer | ||
| 484 | * to continue until any currently running CPU hotplug operation gets | ||
| 485 | * completed. | ||
| 486 | * To modify the 'cpu_hotplug_disabled' flag, we need to acquire the | ||
| 487 | * 'cpu_add_remove_lock'. And this same lock is also taken by the regular | ||
| 488 | * CPU hotplug path and released only after it is complete. Thus, we | ||
| 489 | * (and hence the freezer) will block here until any currently running CPU | ||
| 490 | * hotplug operation gets completed. | ||
| 491 | */ | ||
| 492 | void cpu_hotplug_disable_before_freeze(void) | ||
| 493 | { | ||
| 494 | cpu_maps_update_begin(); | ||
| 495 | cpu_hotplug_disabled = 1; | ||
| 496 | cpu_maps_update_done(); | ||
| 497 | } | ||
| 498 | |||
| 499 | |||
| 500 | /* | ||
| 501 | * When tasks have been thawed, re-enable regular CPU hotplug (which had been | ||
| 502 | * disabled while beginning to freeze tasks). | ||
| 503 | */ | ||
| 504 | void cpu_hotplug_enable_after_thaw(void) | ||
| 505 | { | ||
| 506 | cpu_maps_update_begin(); | ||
| 507 | cpu_hotplug_disabled = 0; | ||
| 508 | cpu_maps_update_done(); | ||
| 509 | } | ||
| 510 | |||
| 511 | /* | ||
| 512 | * When callbacks for CPU hotplug notifications are being executed, we must | ||
| 513 | * ensure that the state of the system with respect to the tasks being frozen | ||
| 514 | * or not, as reported by the notification, remains unchanged *throughout the | ||
| 515 | * duration* of the execution of the callbacks. | ||
| 516 | * Hence we need to prevent the freezer from racing with regular CPU hotplug. | ||
| 517 | * | ||
| 518 | * This synchronization is implemented by mutually excluding regular CPU | ||
| 519 | * hotplug and Suspend/Hibernate call paths by hooking onto the Suspend/ | ||
| 520 | * Hibernate notifications. | ||
| 521 | */ | ||
| 522 | static int | ||
| 523 | cpu_hotplug_pm_callback(struct notifier_block *nb, | ||
| 524 | unsigned long action, void *ptr) | ||
| 525 | { | ||
| 526 | switch (action) { | ||
| 527 | |||
| 528 | case PM_SUSPEND_PREPARE: | ||
| 529 | case PM_HIBERNATION_PREPARE: | ||
| 530 | cpu_hotplug_disable_before_freeze(); | ||
| 531 | break; | ||
| 532 | |||
| 533 | case PM_POST_SUSPEND: | ||
| 534 | case PM_POST_HIBERNATION: | ||
| 535 | cpu_hotplug_enable_after_thaw(); | ||
| 536 | break; | ||
| 537 | |||
| 538 | default: | ||
| 539 | return NOTIFY_DONE; | ||
| 540 | } | ||
| 541 | |||
| 542 | return NOTIFY_OK; | ||
| 543 | } | ||
| 544 | |||
| 545 | |||
| 546 | int cpu_hotplug_pm_sync_init(void) | ||
| 547 | { | ||
| 548 | pm_notifier(cpu_hotplug_pm_callback, 0); | ||
| 549 | return 0; | ||
| 550 | } | ||
| 551 | core_initcall(cpu_hotplug_pm_sync_init); | ||
| 552 | |||
| 479 | #endif /* CONFIG_PM_SLEEP_SMP */ | 553 | #endif /* CONFIG_PM_SLEEP_SMP */ |
| 480 | 554 | ||
| 481 | /** | 555 | /** |
