Commit fe12c00d authored by Chen Yu's avatar Chen Yu Committed by Rafael J. Wysocki

PM / hibernate: Introduce test_resume mode for hibernation

test_resume mode is to verify if the snapshot data
written to swap device can be successfully restored
to memory. It is useful to ease the debugging process
on hibernation, since this mode can not only bypass
the BIOSes/bootloader, but also the system re-initialization.

To avoid the risk to break the filesystm on persistent storage,
this patch resumes the image with tasks frozen.

For example:
echo test_resume > /sys/power/disk
echo disk > /sys/power/state

[  187.306470] PM: Image saving progress:  70%
[  187.395298] PM: Image saving progress:  80%
[  187.476697] PM: Image saving progress:  90%
[  187.554641] PM: Image saving done.
[  187.558896] PM: Wrote 594600 kbytes in 0.90 seconds (660.66 MB/s)
[  187.566000] PM: S|
[  187.589742] PM: Basic memory bitmaps freed
[  187.594694] PM: Checking hibernation image
[  187.599865] PM: Image signature found, resuming
[  187.605209] PM: Loading hibernation image.
[  187.665753] PM: Basic memory bitmaps created
[  187.691397] PM: Using 3 thread(s) for decompression.
[  187.691397] PM: Loading and decompressing image data (148650 pages)...
[  187.889719] PM: Image loading progress:   0%
[  188.100452] PM: Image loading progress:  10%
[  188.244781] PM: Image loading progress:  20%
[  189.057305] PM: Image loading done.
[  189.068793] PM: Image successfully loaded
Suggested-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarChen Yu <yu.c.chen@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 406f992e
...@@ -52,6 +52,7 @@ enum { ...@@ -52,6 +52,7 @@ enum {
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
HIBERNATION_SUSPEND, HIBERNATION_SUSPEND,
#endif #endif
HIBERNATION_TEST_RESUME,
/* keep last */ /* keep last */
__HIBERNATION_AFTER_LAST __HIBERNATION_AFTER_LAST
}; };
...@@ -647,12 +648,39 @@ static void power_down(void) ...@@ -647,12 +648,39 @@ static void power_down(void)
cpu_relax(); cpu_relax();
} }
static int load_image_and_restore(void)
{
int error;
unsigned int flags;
pr_debug("PM: Loading hibernation image.\n");
lock_device_hotplug();
error = create_basic_memory_bitmaps();
if (error)
goto Unlock;
error = swsusp_read(&flags);
swsusp_close(FMODE_READ);
if (!error)
hibernation_restore(flags & SF_PLATFORM_MODE);
printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
swsusp_free();
free_basic_memory_bitmaps();
Unlock:
unlock_device_hotplug();
return error;
}
/** /**
* hibernate - Carry out system hibernation, including saving the image. * hibernate - Carry out system hibernation, including saving the image.
*/ */
int hibernate(void) int hibernate(void)
{ {
int error, nr_calls = 0; int error, nr_calls = 0;
bool snapshot_test = false;
if (!hibernation_available()) { if (!hibernation_available()) {
pr_debug("PM: Hibernation not available.\n"); pr_debug("PM: Hibernation not available.\n");
...@@ -704,8 +732,12 @@ int hibernate(void) ...@@ -704,8 +732,12 @@ int hibernate(void)
pr_debug("PM: writing image.\n"); pr_debug("PM: writing image.\n");
error = swsusp_write(flags); error = swsusp_write(flags);
swsusp_free(); swsusp_free();
if (!error) if (!error) {
power_down(); if (hibernation_mode == HIBERNATION_TEST_RESUME)
snapshot_test = true;
else
power_down();
}
in_suspend = 0; in_suspend = 0;
pm_restore_gfp_mask(); pm_restore_gfp_mask();
} else { } else {
...@@ -716,6 +748,12 @@ int hibernate(void) ...@@ -716,6 +748,12 @@ int hibernate(void)
free_basic_memory_bitmaps(); free_basic_memory_bitmaps();
Thaw: Thaw:
unlock_device_hotplug(); unlock_device_hotplug();
if (snapshot_test) {
pr_debug("PM: Checking hibernation image\n");
error = swsusp_check();
if (!error)
error = load_image_and_restore();
}
thaw_processes(); thaw_processes();
/* Don't bother checking whether freezer_test_done is true */ /* Don't bother checking whether freezer_test_done is true */
...@@ -748,7 +786,6 @@ int hibernate(void) ...@@ -748,7 +786,6 @@ int hibernate(void)
static int software_resume(void) static int software_resume(void)
{ {
int error, nr_calls = 0; int error, nr_calls = 0;
unsigned int flags;
/* /*
* If the user said "noresume".. bail out early. * If the user said "noresume".. bail out early.
...@@ -844,24 +881,7 @@ static int software_resume(void) ...@@ -844,24 +881,7 @@ static int software_resume(void)
error = freeze_processes(); error = freeze_processes();
if (error) if (error)
goto Close_Finish; goto Close_Finish;
error = load_image_and_restore();
pr_debug("PM: Loading hibernation image.\n");
lock_device_hotplug();
error = create_basic_memory_bitmaps();
if (error)
goto Thaw;
error = swsusp_read(&flags);
swsusp_close(FMODE_READ);
if (!error)
hibernation_restore(flags & SF_PLATFORM_MODE);
printk(KERN_ERR "PM: Failed to load hibernation image, recovering.\n");
swsusp_free();
free_basic_memory_bitmaps();
Thaw:
unlock_device_hotplug();
thaw_processes(); thaw_processes();
Finish: Finish:
__pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
...@@ -887,6 +907,7 @@ static const char * const hibernation_modes[] = { ...@@ -887,6 +907,7 @@ static const char * const hibernation_modes[] = {
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
[HIBERNATION_SUSPEND] = "suspend", [HIBERNATION_SUSPEND] = "suspend",
#endif #endif
[HIBERNATION_TEST_RESUME] = "test_resume",
}; };
/* /*
...@@ -933,6 +954,7 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, ...@@ -933,6 +954,7 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
case HIBERNATION_SUSPEND: case HIBERNATION_SUSPEND:
#endif #endif
case HIBERNATION_TEST_RESUME:
break; break;
case HIBERNATION_PLATFORM: case HIBERNATION_PLATFORM:
if (hibernation_ops) if (hibernation_ops)
...@@ -979,6 +1001,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, ...@@ -979,6 +1001,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
case HIBERNATION_SUSPEND: case HIBERNATION_SUSPEND:
#endif #endif
case HIBERNATION_TEST_RESUME:
hibernation_mode = mode; hibernation_mode = mode;
break; break;
case HIBERNATION_PLATFORM: case HIBERNATION_PLATFORM:
......
...@@ -348,6 +348,12 @@ static int swsusp_swap_check(void) ...@@ -348,6 +348,12 @@ static int swsusp_swap_check(void)
if (res < 0) if (res < 0)
blkdev_put(hib_resume_bdev, FMODE_WRITE); blkdev_put(hib_resume_bdev, FMODE_WRITE);
/*
* Update the resume device to the one actually used,
* so the test_resume mode can use it in case it is
* invoked from hibernate() to test the snapshot.
*/
swsusp_resume_device = hib_resume_bdev->bd_dev;
return res; return res;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment