aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/acpi/sleep/main.c16
-rw-r--r--include/linux/suspend.h4
-rw-r--r--kernel/power/disk.c56
-rw-r--r--kernel/power/power.h13
-rw-r--r--kernel/power/swap.c20
-rw-r--r--kernel/power/user.c2
6 files changed, 92 insertions, 19 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index bc7e16ec8393..42127c0d612c 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -217,10 +217,26 @@ static void acpi_hibernation_finish(void)
217 } 217 }
218} 218}
219 219
220static int acpi_hibernation_pre_restore(void)
221{
222 acpi_status status;
223
224 status = acpi_hw_disable_all_gpes();
225
226 return ACPI_SUCCESS(status) ? 0 : -EFAULT;
227}
228
229static void acpi_hibernation_restore_cleanup(void)
230{
231 acpi_hw_enable_all_runtime_gpes();
232}
233
220static struct hibernation_ops acpi_hibernation_ops = { 234static struct hibernation_ops acpi_hibernation_ops = {
221 .prepare = acpi_hibernation_prepare, 235 .prepare = acpi_hibernation_prepare,
222 .enter = acpi_hibernation_enter, 236 .enter = acpi_hibernation_enter,
223 .finish = acpi_hibernation_finish, 237 .finish = acpi_hibernation_finish,
238 .pre_restore = acpi_hibernation_pre_restore,
239 .restore_cleanup = acpi_hibernation_restore_cleanup,
224}; 240};
225#endif /* CONFIG_SOFTWARE_SUSPEND */ 241#endif /* CONFIG_SOFTWARE_SUSPEND */
226 242
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 9c7cb6430666..d235c146da2b 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -43,11 +43,15 @@ static inline void pm_restore_console(void) {}
43 * @prepare: prepare system for hibernation 43 * @prepare: prepare system for hibernation
44 * @enter: shut down system after state has been saved to disk 44 * @enter: shut down system after state has been saved to disk
45 * @finish: finish/clean up after state has been reloaded 45 * @finish: finish/clean up after state has been reloaded
46 * @pre_restore: prepare system for the restoration from a hibernation image
47 * @restore_cleanup: clean up after a failing image restoration
46 */ 48 */
47struct hibernation_ops { 49struct hibernation_ops {
48 int (*prepare)(void); 50 int (*prepare)(void);
49 int (*enter)(void); 51 int (*enter)(void);
50 void (*finish)(void); 52 void (*finish)(void);
53 int (*pre_restore)(void);
54 void (*restore_cleanup)(void);
51}; 55};
52 56
53#if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) 57#if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND)
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();
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 70c378b3f85a..eab3603b7caf 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -27,7 +27,7 @@ struct swsusp_info {
27 27
28/* kernel/power/disk.c */ 28/* kernel/power/disk.c */
29extern int hibernation_snapshot(int platform_mode); 29extern int hibernation_snapshot(int platform_mode);
30extern int hibernation_restore(void); 30extern int hibernation_restore(int platform_mode);
31extern int hibernation_platform_enter(void); 31extern int hibernation_platform_enter(void);
32#endif 32#endif
33 33
@@ -155,13 +155,20 @@ extern sector_t alloc_swapdev_block(int swap);
155extern void free_all_swap_pages(int swap); 155extern void free_all_swap_pages(int swap);
156extern int swsusp_swap_in_use(void); 156extern int swsusp_swap_in_use(void);
157 157
158/*
159 * Flags that can be passed from the hibernatig hernel to the "boot" kernel in
160 * the image header.
161 */
162#define SF_PLATFORM_MODE 1
163
164/* kernel/power/disk.c */
158extern int swsusp_check(void); 165extern int swsusp_check(void);
159extern int swsusp_shrink_memory(void); 166extern int swsusp_shrink_memory(void);
160extern void swsusp_free(void); 167extern void swsusp_free(void);
161extern int swsusp_suspend(void); 168extern int swsusp_suspend(void);
162extern int swsusp_resume(void); 169extern int swsusp_resume(void);
163extern int swsusp_read(void); 170extern int swsusp_read(unsigned int *flags_p);
164extern int swsusp_write(void); 171extern int swsusp_write(unsigned int flags);
165extern void swsusp_close(void); 172extern void swsusp_close(void);
166extern int suspend_enter(suspend_state_t state); 173extern int suspend_enter(suspend_state_t state);
167 174
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 8b1a1b837145..917aba100575 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -33,8 +33,9 @@ extern char resume_file[];
33#define SWSUSP_SIG "S1SUSPEND" 33#define SWSUSP_SIG "S1SUSPEND"
34 34
35struct swsusp_header { 35struct swsusp_header {
36 char reserved[PAGE_SIZE - 20 - sizeof(sector_t)]; 36 char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int)];
37 sector_t image; 37 sector_t image;
38 unsigned int flags; /* Flags to pass to the "boot" kernel */
38 char orig_sig[10]; 39 char orig_sig[10];
39 char sig[10]; 40 char sig[10];
40} __attribute__((packed)); 41} __attribute__((packed));
@@ -138,7 +139,7 @@ static int wait_on_bio_chain(struct bio **bio_chain)
138 * Saving part 139 * Saving part
139 */ 140 */
140 141
141static int mark_swapfiles(sector_t start) 142static int mark_swapfiles(sector_t start, unsigned int flags)
142{ 143{
143 int error; 144 int error;
144 145
@@ -148,6 +149,7 @@ static int mark_swapfiles(sector_t start)
148 memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); 149 memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10);
149 memcpy(swsusp_header->sig,SWSUSP_SIG, 10); 150 memcpy(swsusp_header->sig,SWSUSP_SIG, 10);
150 swsusp_header->image = start; 151 swsusp_header->image = start;
152 swsusp_header->flags = flags;
151 error = bio_write_page(swsusp_resume_block, 153 error = bio_write_page(swsusp_resume_block,
152 swsusp_header, NULL); 154 swsusp_header, NULL);
153 } else { 155 } else {
@@ -369,6 +371,7 @@ static int enough_swap(unsigned int nr_pages)
369 371
370/** 372/**
371 * swsusp_write - Write entire image and metadata. 373 * swsusp_write - Write entire image and metadata.
374 * @flags: flags to pass to the "boot" kernel in the image header
372 * 375 *
373 * It is important _NOT_ to umount filesystems at this point. We want 376 * It is important _NOT_ to umount filesystems at this point. We want
374 * them synced (in case something goes wrong) but we DO not want to mark 377 * them synced (in case something goes wrong) but we DO not want to mark
@@ -376,7 +379,7 @@ static int enough_swap(unsigned int nr_pages)
376 * correctly, we'll mark system clean, anyway.) 379 * correctly, we'll mark system clean, anyway.)
377 */ 380 */
378 381
379int swsusp_write(void) 382int swsusp_write(unsigned int flags)
380{ 383{
381 struct swap_map_handle handle; 384 struct swap_map_handle handle;
382 struct snapshot_handle snapshot; 385 struct snapshot_handle snapshot;
@@ -415,7 +418,7 @@ int swsusp_write(void)
415 if (!error) { 418 if (!error) {
416 flush_swap_writer(&handle); 419 flush_swap_writer(&handle);
417 printk("S"); 420 printk("S");
418 error = mark_swapfiles(start); 421 error = mark_swapfiles(start, flags);
419 printk("|\n"); 422 printk("|\n");
420 } 423 }
421 } 424 }
@@ -540,13 +543,20 @@ static int load_image(struct swap_map_handle *handle,
540 return error; 543 return error;
541} 544}
542 545
543int swsusp_read(void) 546/**
547 * swsusp_read - read the hibernation image.
548 * @flags_p: flags passed by the "frozen" kernel in the image header should
549 * be written into this memeory location
550 */
551
552int swsusp_read(unsigned int *flags_p)
544{ 553{
545 int error; 554 int error;
546 struct swap_map_handle handle; 555 struct swap_map_handle handle;
547 struct snapshot_handle snapshot; 556 struct snapshot_handle snapshot;
548 struct swsusp_info *header; 557 struct swsusp_info *header;
549 558
559 *flags_p = swsusp_header->flags;
550 if (IS_ERR(resume_bdev)) { 560 if (IS_ERR(resume_bdev)) {
551 pr_debug("swsusp: block device not initialised\n"); 561 pr_debug("swsusp: block device not initialised\n");
552 return PTR_ERR(resume_bdev); 562 return PTR_ERR(resume_bdev);
diff --git a/kernel/power/user.c b/kernel/power/user.c
index bfed3b924093..1f24f30b951b 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -188,7 +188,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
188 error = -EPERM; 188 error = -EPERM;
189 break; 189 break;
190 } 190 }
191 error = hibernation_restore(); 191 error = hibernation_restore(data->platform_suspend);
192 break; 192 break;
193 193
194 case SNAPSHOT_FREE: 194 case SNAPSHOT_FREE: