aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/disk.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-07-19 04:47:30 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:42 -0400
commita634cc10164d1c229fbeca33923e6a0ed939e894 (patch)
treea2cdc5403127ca71b2cf378feb86d46745022ac1 /kernel/power/disk.c
parent7777fab989b5d006903188c966058ebcd2d6342a (diff)
swsusp: introduce restore platform operations
At least on some machines it is necessary to prepare the ACPI firmware for the restoration of the system memory state from the hibernation image if the "platform" mode of hibernation has been used. Namely, in that cases we need to disable the GPEs before replacing the "boot" kernel with the "frozen" kernel (cf. http://bugzilla.kernel.org/show_bug.cgi?id=7887). After the restore they will be re-enabled by hibernation_ops->finish(), but if the restore fails, they have to be re-enabled by the restore code explicitly. For this purpose we can introduce two additional hibernation operations, called pre_restore() and restore_cleanup() and call them from the restore code path. Still, they should be called if the "platform" mode of hibernation has been used, so we need to pass the information about the hibernation mode from the "frozen" kernel to the "boot" kernel in the image header. Apparently, we can't drop the disabling of GPEs before the restore because of Bug #7887 .  We also can't do it unconditionally, because the GPEs wouldn't have been enabled after a successful restore if the suspend had been done in the 'shutdown' or 'reboot' mode. In principle we could (and probably should) unconditionally disable the GPEs before each snapshot creation *and* before the restore, but then we'd have to unconditionally enable them after the snapshot creation as well as after the restore (or restore failure)   Still, for this purpose we'd need to modify acpi_enter_sleep_state_prep() and acpi_leave_sleep_state() and we'd have to introduce some mechanism synchronizing the disablind/enabling of the GPEs with the device drivers' .suspend()/.resume() routines and with disable_/enable_nonboot_cpus().  However, this would have affected the suspend (ie. s2ram) code as well as the hibernation, which I'd like to avoid in this patch series. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Cc: Nigel Cunningham <nigel@nigel.suspend2.net> Cc: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r--kernel/power/disk.c56
1 files changed, 46 insertions, 10 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 47882bfa610e..fa3b43b7206d 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -54,7 +54,8 @@ static struct hibernation_ops *hibernation_ops;
54 54
55void hibernation_set_ops(struct hibernation_ops *ops) 55void hibernation_set_ops(struct hibernation_ops *ops)
56{ 56{
57 if (ops && !(ops->prepare && ops->enter && ops->finish)) { 57 if (ops && !(ops->prepare && ops->enter && ops->finish
58 && ops->pre_restore && ops->restore_cleanup)) {
58 WARN_ON(1); 59 WARN_ON(1);
59 return; 60 return;
60 } 61 }
@@ -92,6 +93,31 @@ static void platform_finish(int platform_mode)
92} 93}
93 94
94/** 95/**
96 * platform_pre_restore - prepare the platform for the restoration from a
97 * hibernation image. If the restore fails after this function has been
98 * called, platform_restore_cleanup() must be called.
99 */
100
101static int platform_pre_restore(int platform_mode)
102{
103 return (platform_mode && hibernation_ops) ?
104 hibernation_ops->pre_restore() : 0;
105}
106
107/**
108 * platform_restore_cleanup - switch the platform to the normal mode of
109 * operation after a failing restore. If platform_pre_restore() has been
110 * called before the failing restore, this function must be called too,
111 * regardless of the result of platform_pre_restore().
112 */
113
114static void platform_restore_cleanup(int platform_mode)
115{
116 if (platform_mode && hibernation_ops)
117 hibernation_ops->restore_cleanup();
118}
119
120/**
95 * hibernation_snapshot - quiesce devices and create the hibernation 121 * hibernation_snapshot - quiesce devices and create the hibernation
96 * snapshot image. 122 * snapshot image.
97 * @platform_mode - if set, use the platform driver, if available, to 123 * @platform_mode - if set, use the platform driver, if available, to
@@ -141,11 +167,13 @@ int hibernation_snapshot(int platform_mode)
141/** 167/**
142 * hibernation_restore - quiesce devices and restore the hibernation 168 * hibernation_restore - quiesce devices and restore the hibernation
143 * snapshot image. If successful, control returns in hibernation_snaphot() 169 * snapshot image. If successful, control returns in hibernation_snaphot()
170 * @platform_mode - if set, use the platform driver, if available, to
171 * prepare the platform frimware for the transition.
144 * 172 *
145 * Must be called with pm_mutex held 173 * Must be called with pm_mutex held
146 */ 174 */
147 175
148int hibernation_restore(void) 176int hibernation_restore(int platform_mode)
149{ 177{
150 int error; 178 int error;
151 179
@@ -155,11 +183,14 @@ int hibernation_restore(void)
155 if (error) 183 if (error)
156 goto Finish; 184 goto Finish;
157 185
158 error = disable_nonboot_cpus(); 186 error = platform_pre_restore(platform_mode);
159 if (!error) 187 if (!error) {
160 error = swsusp_resume(); 188 error = disable_nonboot_cpus();
161 189 if (!error)
162 enable_nonboot_cpus(); 190 error = swsusp_resume();
191 enable_nonboot_cpus();
192 }
193 platform_restore_cleanup(platform_mode);
163 Finish: 194 Finish:
164 device_resume(); 195 device_resume();
165 resume_console(); 196 resume_console();
@@ -260,8 +291,12 @@ int hibernate(void)
260 } 291 }
261 error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); 292 error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
262 if (in_suspend && !error) { 293 if (in_suspend && !error) {
294 unsigned int flags = 0;
295
296 if (hibernation_mode == HIBERNATION_PLATFORM)
297 flags |= SF_PLATFORM_MODE;
263 pr_debug("PM: writing image.\n"); 298 pr_debug("PM: writing image.\n");
264 error = swsusp_write(); 299 error = swsusp_write(flags);
265 swsusp_free(); 300 swsusp_free();
266 if (!error) 301 if (!error)
267 power_down(); 302 power_down();
@@ -295,6 +330,7 @@ int hibernate(void)
295static int software_resume(void) 330static int software_resume(void)
296{ 331{
297 int error; 332 int error;
333 unsigned int flags;
298 334
299 mutex_lock(&pm_mutex); 335 mutex_lock(&pm_mutex);
300 if (!swsusp_resume_device) { 336 if (!swsusp_resume_device) {
@@ -342,9 +378,9 @@ static int software_resume(void)
342 378
343 pr_debug("PM: Reading swsusp image.\n"); 379 pr_debug("PM: Reading swsusp image.\n");
344 380
345 error = swsusp_read(); 381 error = swsusp_read(&flags);
346 if (!error) 382 if (!error)
347 hibernation_restore(); 383 hibernation_restore(flags & SF_PLATFORM_MODE);
348 384
349 printk(KERN_ERR "PM: Restore failed, recovering.\n"); 385 printk(KERN_ERR "PM: Restore failed, recovering.\n");
350 swsusp_free(); 386 swsusp_free();