diff options
Diffstat (limited to 'kernel/power/hibernate.c')
| -rw-r--r-- | kernel/power/hibernate.c | 179 | 
1 files changed, 92 insertions, 87 deletions
| diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 30d1274f03f6..f62e89d0d906 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -11,6 +11,7 @@  #define pr_fmt(fmt) "PM: hibernation: " fmt +#include <linux/blkdev.h>  #include <linux/export.h>  #include <linux/suspend.h>  #include <linux/reboot.h> @@ -64,7 +65,6 @@ enum {  static int hibernation_mode = HIBERNATION_SHUTDOWN;  bool freezer_test_done; -bool snapshot_test;  static const struct platform_hibernation_ops *hibernation_ops; @@ -684,26 +684,22 @@ static void power_down(void)  		cpu_relax();  } -static int load_image_and_restore(void) +static int load_image_and_restore(bool snapshot_test)  {  	int error;  	unsigned int flags; -	fmode_t mode = FMODE_READ; - -	if (snapshot_test) -		mode |= FMODE_EXCL;  	pm_pr_dbg("Loading hibernation image.\n");  	lock_device_hotplug();  	error = create_basic_memory_bitmaps();  	if (error) { -		swsusp_close(mode); +		swsusp_close(snapshot_test);  		goto Unlock;  	}  	error = swsusp_read(&flags); -	swsusp_close(mode); +	swsusp_close(snapshot_test);  	if (!error)  		error = hibernation_restore(flags & SF_PLATFORM_MODE); @@ -721,6 +717,7 @@ static int load_image_and_restore(void)   */  int hibernate(void)  { +	bool snapshot_test = false;  	unsigned int sleep_flags;  	int error; @@ -748,9 +745,6 @@ int hibernate(void)  	if (error)  		goto Exit; -	/* protected by system_transition_mutex */ -	snapshot_test = false; -  	lock_device_hotplug();  	/* Allocate memory management structures */  	error = create_basic_memory_bitmaps(); @@ -792,9 +786,9 @@ int hibernate(void)  	unlock_device_hotplug();  	if (snapshot_test) {  		pm_pr_dbg("Checking hibernation image\n"); -		error = swsusp_check(); +		error = swsusp_check(snapshot_test);  		if (!error) -			error = load_image_and_restore(); +			error = load_image_and_restore(snapshot_test);  	}  	thaw_processes(); @@ -910,52 +904,10 @@ unlock:  }  EXPORT_SYMBOL_GPL(hibernate_quiet_exec); -/** - * software_resume - Resume from a saved hibernation image. - * - * This routine is called as a late initcall, when all devices have been - * discovered and initialized already. - * - * The image reading code is called to see if there is a hibernation image - * available for reading.  If that is the case, devices are quiesced and the - * contents of memory is restored from the saved image. - * - * If this is successful, control reappears in the restored target kernel in - * hibernation_snapshot() which returns to hibernate().  Otherwise, the routine - * attempts to recover gracefully and make the kernel return to the normal mode - * of operation. - */ -static int software_resume(void) +static int __init find_resume_device(void)  { -	int error; - -	/* -	 * If the user said "noresume".. bail out early. -	 */ -	if (noresume || !hibernation_available()) -		return 0; - -	/* -	 * name_to_dev_t() below takes a sysfs buffer mutex when sysfs -	 * is configured into the kernel. Since the regular hibernate -	 * trigger path is via sysfs which takes a buffer mutex before -	 * calling hibernate functions (which take system_transition_mutex) -	 * this can cause lockdep to complain about a possible ABBA deadlock -	 * which cannot happen since we're in the boot code here and -	 * sysfs can't be invoked yet. Therefore, we use a subclass -	 * here to avoid lockdep complaining. -	 */ -	mutex_lock_nested(&system_transition_mutex, SINGLE_DEPTH_NESTING); - -	snapshot_test = false; - -	if (swsusp_resume_device) -		goto Check_image; - -	if (!strlen(resume_file)) { -		error = -ENOENT; -		goto Unlock; -	} +	if (!strlen(resume_file)) +		return -ENOENT;  	pm_pr_dbg("Checking hibernation image partition %s\n", resume_file); @@ -966,40 +918,41 @@ static int software_resume(void)  	}  	/* Check if the device is there */ -	swsusp_resume_device = name_to_dev_t(resume_file); -	if (!swsusp_resume_device) { -		/* -		 * Some device discovery might still be in progress; we need -		 * to wait for this to finish. -		 */ -		wait_for_device_probe(); - -		if (resume_wait) { -			while ((swsusp_resume_device = name_to_dev_t(resume_file)) == 0) -				msleep(10); -			async_synchronize_full(); -		} +	if (!early_lookup_bdev(resume_file, &swsusp_resume_device)) +		return 0; -		swsusp_resume_device = name_to_dev_t(resume_file); -		if (!swsusp_resume_device) { -			error = -ENODEV; -			goto Unlock; -		} +	/* +	 * Some device discovery might still be in progress; we need to wait for +	 * this to finish. +	 */ +	wait_for_device_probe(); +	if (resume_wait) { +		while (early_lookup_bdev(resume_file, &swsusp_resume_device)) +			msleep(10); +		async_synchronize_full();  	} - Check_image: +	return early_lookup_bdev(resume_file, &swsusp_resume_device); +} + +static int software_resume(void) +{ +	int error; +  	pm_pr_dbg("Hibernation image partition %d:%d present\n",  		MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));  	pm_pr_dbg("Looking for hibernation image.\n"); -	error = swsusp_check(); + +	mutex_lock(&system_transition_mutex); +	error = swsusp_check(false);  	if (error)  		goto Unlock;  	/* The snapshot device should not be opened while we're running */  	if (!hibernate_acquire()) {  		error = -EBUSY; -		swsusp_close(FMODE_READ | FMODE_EXCL); +		swsusp_close(false);  		goto Unlock;  	} @@ -1020,7 +973,7 @@ static int software_resume(void)  		goto Close_Finish;  	} -	error = load_image_and_restore(); +	error = load_image_and_restore(false);  	thaw_processes();   Finish:  	pm_notifier_call_chain(PM_POST_RESTORE); @@ -1034,11 +987,43 @@ static int software_resume(void)  	pm_pr_dbg("Hibernation image not present or could not be loaded.\n");  	return error;   Close_Finish: -	swsusp_close(FMODE_READ | FMODE_EXCL); +	swsusp_close(false);  	goto Finish;  } -late_initcall_sync(software_resume); +/** + * software_resume_initcall - Resume from a saved hibernation image. + * + * This routine is called as a late initcall, when all devices have been + * discovered and initialized already. + * + * The image reading code is called to see if there is a hibernation image + * available for reading.  If that is the case, devices are quiesced and the + * contents of memory is restored from the saved image. + * + * If this is successful, control reappears in the restored target kernel in + * hibernation_snapshot() which returns to hibernate().  Otherwise, the routine + * attempts to recover gracefully and make the kernel return to the normal mode + * of operation. + */ +static int __init software_resume_initcall(void) +{ +	/* +	 * If the user said "noresume".. bail out early. +	 */ +	if (noresume || !hibernation_available()) +		return 0; + +	if (!swsusp_resume_device) { +		int error = find_resume_device(); + +		if (error) +			return error; +	} + +	return software_resume(); +} +late_initcall_sync(software_resume_initcall);  static const char * const hibernation_modes[] = { @@ -1177,7 +1162,11 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,  	unsigned int sleep_flags;  	int len = n;  	char *name; -	dev_t res; +	dev_t dev; +	int error; + +	if (!hibernation_available()) +		return 0;  	if (len && buf[len-1] == '\n')  		len--; @@ -1185,13 +1174,29 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,  	if (!name)  		return -ENOMEM; -	res = name_to_dev_t(name); +	error = lookup_bdev(name, &dev); +	if (error) { +		unsigned maj, min, offset; +		char *p, dummy; + +		if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 || +		    sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, +				&dummy) == 3) { +			dev = MKDEV(maj, min); +			if (maj != MAJOR(dev) || min != MINOR(dev)) +				error = -EINVAL; +		} else { +			dev = new_decode_dev(simple_strtoul(name, &p, 16)); +			if (*p) +				error = -EINVAL; +		} +	}  	kfree(name); -	if (!res) -		return -EINVAL; +	if (error) +		return error;  	sleep_flags = lock_system_sleep(); -	swsusp_resume_device = res; +	swsusp_resume_device = dev;  	unlock_system_sleep(sleep_flags);  	pm_pr_dbg("Configured hibernation resume from disk to %u\n", |