diff options
author | Lianwei Wang <lianwei.wang@gmail.com> | 2016-06-20 02:52:27 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-06-27 18:38:55 -0400 |
commit | ea00f4f4f00cc2bc3b63ad512a4e6df3b20832b9 (patch) | |
tree | 148dbab1f94bc7dd515c6ddda12f85bae310dd4f /kernel/power | |
parent | ca5f2b4c4fb7bb7397317ee2ead83485aa295a3e (diff) |
PM / sleep: make PM notifiers called symmetrically
This makes pm notifier PREPARE/POST symmetrical: if PREPARE
fails, we will only undo what ever happened on PREPARE.
It fixes the unbalanced CPU hotplug enable in CPU PM notifier.
Signed-off-by: Lianwei Wang <lianwei.wang@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/hibernate.c | 20 | ||||
-rw-r--r-- | kernel/power/main.c | 11 | ||||
-rw-r--r-- | kernel/power/power.h | 2 | ||||
-rw-r--r-- | kernel/power/suspend.c | 10 | ||||
-rw-r--r-- | kernel/power/user.c | 14 |
5 files changed, 37 insertions, 20 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index fca9254280ee..126e24caa82e 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c | |||
@@ -647,7 +647,7 @@ static void power_down(void) | |||
647 | */ | 647 | */ |
648 | int hibernate(void) | 648 | int hibernate(void) |
649 | { | 649 | { |
650 | int error; | 650 | int error, nr_calls = 0; |
651 | 651 | ||
652 | if (!hibernation_available()) { | 652 | if (!hibernation_available()) { |
653 | pr_debug("PM: Hibernation not available.\n"); | 653 | pr_debug("PM: Hibernation not available.\n"); |
@@ -662,9 +662,11 @@ int hibernate(void) | |||
662 | } | 662 | } |
663 | 663 | ||
664 | pm_prepare_console(); | 664 | pm_prepare_console(); |
665 | error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); | 665 | error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); |
666 | if (error) | 666 | if (error) { |
667 | nr_calls--; | ||
667 | goto Exit; | 668 | goto Exit; |
669 | } | ||
668 | 670 | ||
669 | printk(KERN_INFO "PM: Syncing filesystems ... "); | 671 | printk(KERN_INFO "PM: Syncing filesystems ... "); |
670 | sys_sync(); | 672 | sys_sync(); |
@@ -714,7 +716,7 @@ int hibernate(void) | |||
714 | /* Don't bother checking whether freezer_test_done is true */ | 716 | /* Don't bother checking whether freezer_test_done is true */ |
715 | freezer_test_done = false; | 717 | freezer_test_done = false; |
716 | Exit: | 718 | Exit: |
717 | pm_notifier_call_chain(PM_POST_HIBERNATION); | 719 | __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL); |
718 | pm_restore_console(); | 720 | pm_restore_console(); |
719 | atomic_inc(&snapshot_device_available); | 721 | atomic_inc(&snapshot_device_available); |
720 | Unlock: | 722 | Unlock: |
@@ -740,7 +742,7 @@ int hibernate(void) | |||
740 | */ | 742 | */ |
741 | static int software_resume(void) | 743 | static int software_resume(void) |
742 | { | 744 | { |
743 | int error; | 745 | int error, nr_calls = 0; |
744 | unsigned int flags; | 746 | unsigned int flags; |
745 | 747 | ||
746 | /* | 748 | /* |
@@ -827,9 +829,11 @@ static int software_resume(void) | |||
827 | } | 829 | } |
828 | 830 | ||
829 | pm_prepare_console(); | 831 | pm_prepare_console(); |
830 | error = pm_notifier_call_chain(PM_RESTORE_PREPARE); | 832 | error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); |
831 | if (error) | 833 | if (error) { |
834 | nr_calls--; | ||
832 | goto Close_Finish; | 835 | goto Close_Finish; |
836 | } | ||
833 | 837 | ||
834 | pr_debug("PM: Preparing processes for restore.\n"); | 838 | pr_debug("PM: Preparing processes for restore.\n"); |
835 | error = freeze_processes(); | 839 | error = freeze_processes(); |
@@ -855,7 +859,7 @@ static int software_resume(void) | |||
855 | unlock_device_hotplug(); | 859 | unlock_device_hotplug(); |
856 | thaw_processes(); | 860 | thaw_processes(); |
857 | Finish: | 861 | Finish: |
858 | pm_notifier_call_chain(PM_POST_RESTORE); | 862 | __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); |
859 | pm_restore_console(); | 863 | pm_restore_console(); |
860 | atomic_inc(&snapshot_device_available); | 864 | atomic_inc(&snapshot_device_available); |
861 | /* For success case, the suspend path will release the lock */ | 865 | /* For success case, the suspend path will release the lock */ |
diff --git a/kernel/power/main.c b/kernel/power/main.c index 27946975eff0..5ea50b1b7595 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -38,12 +38,19 @@ int unregister_pm_notifier(struct notifier_block *nb) | |||
38 | } | 38 | } |
39 | EXPORT_SYMBOL_GPL(unregister_pm_notifier); | 39 | EXPORT_SYMBOL_GPL(unregister_pm_notifier); |
40 | 40 | ||
41 | int pm_notifier_call_chain(unsigned long val) | 41 | int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls) |
42 | { | 42 | { |
43 | int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL); | 43 | int ret; |
44 | |||
45 | ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL, | ||
46 | nr_to_call, nr_calls); | ||
44 | 47 | ||
45 | return notifier_to_errno(ret); | 48 | return notifier_to_errno(ret); |
46 | } | 49 | } |
50 | int pm_notifier_call_chain(unsigned long val) | ||
51 | { | ||
52 | return __pm_notifier_call_chain(val, -1, NULL); | ||
53 | } | ||
47 | 54 | ||
48 | /* If set, devices may be suspended and resumed asynchronously. */ | 55 | /* If set, devices may be suspended and resumed asynchronously. */ |
49 | int pm_async_enabled = 1; | 56 | int pm_async_enabled = 1; |
diff --git a/kernel/power/power.h b/kernel/power/power.h index efe1b3b17c88..51f02ecaf125 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -200,6 +200,8 @@ static inline void suspend_test_finish(const char *label) {} | |||
200 | 200 | ||
201 | #ifdef CONFIG_PM_SLEEP | 201 | #ifdef CONFIG_PM_SLEEP |
202 | /* kernel/power/main.c */ | 202 | /* kernel/power/main.c */ |
203 | extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call, | ||
204 | int *nr_calls); | ||
203 | extern int pm_notifier_call_chain(unsigned long val); | 205 | extern int pm_notifier_call_chain(unsigned long val); |
204 | #endif | 206 | #endif |
205 | 207 | ||
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 5b70d64b871e..0acab9d7f96f 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -266,16 +266,18 @@ static int suspend_test(int level) | |||
266 | */ | 266 | */ |
267 | static int suspend_prepare(suspend_state_t state) | 267 | static int suspend_prepare(suspend_state_t state) |
268 | { | 268 | { |
269 | int error; | 269 | int error, nr_calls = 0; |
270 | 270 | ||
271 | if (!sleep_state_supported(state)) | 271 | if (!sleep_state_supported(state)) |
272 | return -EPERM; | 272 | return -EPERM; |
273 | 273 | ||
274 | pm_prepare_console(); | 274 | pm_prepare_console(); |
275 | 275 | ||
276 | error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); | 276 | error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls); |
277 | if (error) | 277 | if (error) { |
278 | nr_calls--; | ||
278 | goto Finish; | 279 | goto Finish; |
280 | } | ||
279 | 281 | ||
280 | trace_suspend_resume(TPS("freeze_processes"), 0, true); | 282 | trace_suspend_resume(TPS("freeze_processes"), 0, true); |
281 | error = suspend_freeze_processes(); | 283 | error = suspend_freeze_processes(); |
@@ -286,7 +288,7 @@ static int suspend_prepare(suspend_state_t state) | |||
286 | suspend_stats.failed_freeze++; | 288 | suspend_stats.failed_freeze++; |
287 | dpm_save_failed_step(SUSPEND_FREEZE); | 289 | dpm_save_failed_step(SUSPEND_FREEZE); |
288 | Finish: | 290 | Finish: |
289 | pm_notifier_call_chain(PM_POST_SUSPEND); | 291 | __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL); |
290 | pm_restore_console(); | 292 | pm_restore_console(); |
291 | return error; | 293 | return error; |
292 | } | 294 | } |
diff --git a/kernel/power/user.c b/kernel/power/user.c index 526e8911460a..35310b627388 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -47,7 +47,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1); | |||
47 | static int snapshot_open(struct inode *inode, struct file *filp) | 47 | static int snapshot_open(struct inode *inode, struct file *filp) |
48 | { | 48 | { |
49 | struct snapshot_data *data; | 49 | struct snapshot_data *data; |
50 | int error; | 50 | int error, nr_calls = 0; |
51 | 51 | ||
52 | if (!hibernation_available()) | 52 | if (!hibernation_available()) |
53 | return -EPERM; | 53 | return -EPERM; |
@@ -74,9 +74,9 @@ static int snapshot_open(struct inode *inode, struct file *filp) | |||
74 | swap_type_of(swsusp_resume_device, 0, NULL) : -1; | 74 | swap_type_of(swsusp_resume_device, 0, NULL) : -1; |
75 | data->mode = O_RDONLY; | 75 | data->mode = O_RDONLY; |
76 | data->free_bitmaps = false; | 76 | data->free_bitmaps = false; |
77 | error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); | 77 | error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); |
78 | if (error) | 78 | if (error) |
79 | pm_notifier_call_chain(PM_POST_HIBERNATION); | 79 | __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL); |
80 | } else { | 80 | } else { |
81 | /* | 81 | /* |
82 | * Resuming. We may need to wait for the image device to | 82 | * Resuming. We may need to wait for the image device to |
@@ -86,13 +86,15 @@ static int snapshot_open(struct inode *inode, struct file *filp) | |||
86 | 86 | ||
87 | data->swap = -1; | 87 | data->swap = -1; |
88 | data->mode = O_WRONLY; | 88 | data->mode = O_WRONLY; |
89 | error = pm_notifier_call_chain(PM_RESTORE_PREPARE); | 89 | error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); |
90 | if (!error) { | 90 | if (!error) { |
91 | error = create_basic_memory_bitmaps(); | 91 | error = create_basic_memory_bitmaps(); |
92 | data->free_bitmaps = !error; | 92 | data->free_bitmaps = !error; |
93 | } | 93 | } else |
94 | nr_calls--; | ||
95 | |||
94 | if (error) | 96 | if (error) |
95 | pm_notifier_call_chain(PM_POST_RESTORE); | 97 | __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); |
96 | } | 98 | } |
97 | if (error) | 99 | if (error) |
98 | atomic_inc(&snapshot_device_available); | 100 | atomic_inc(&snapshot_device_available); |