diff options
Diffstat (limited to 'kernel/power/hibernate.c')
| -rw-r--r-- | kernel/power/hibernate.c | 98 | 
1 files changed, 74 insertions, 24 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index c9c759d5a15..fcc2611d3f1 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -28,14 +28,16 @@  #include <linux/syscore_ops.h>  #include <linux/ctype.h>  #include <linux/genhd.h> +#include <trace/events/power.h>  #include "power.h"  static int nocompress;  static int noresume; +static int nohibernate;  static int resume_wait; -static int resume_delay; +static unsigned int resume_delay;  static char resume_file[256] = CONFIG_PM_STD_PARTITION;  dev_t swsusp_resume_device;  sector_t swsusp_resume_block; @@ -61,6 +63,11 @@ bool freezer_test_done;  static const struct platform_hibernation_ops *hibernation_ops; +bool hibernation_available(void) +{ +	return (nohibernate == 0); +} +  /**   * hibernation_set_ops - Set the global hibernate operations.   * @ops: Hibernation operations to use in subsequent hibernation transitions. @@ -82,6 +89,7 @@ void hibernation_set_ops(const struct platform_hibernation_ops *ops)  	unlock_system_sleep();  } +EXPORT_SYMBOL_GPL(hibernation_set_ops);  static bool entering_platform_hibernation; @@ -227,19 +235,23 @@ static void platform_recover(int platform_mode)  void swsusp_show_speed(struct timeval *start, struct timeval *stop,  			unsigned nr_pages, char *msg)  { -	s64 elapsed_centisecs64; -	int centisecs; -	int k; -	int kps; +	u64 elapsed_centisecs64; +	unsigned int centisecs; +	unsigned int k; +	unsigned int kps;  	elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start); +	/* +	 * If "(s64)elapsed_centisecs64 < 0", it will print long elapsed time, +	 * it is obvious enough for what went wrong. +	 */  	do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);  	centisecs = elapsed_centisecs64;  	if (centisecs == 0)  		centisecs = 1;	/* avoid div-by-zero */  	k = nr_pages * (PAGE_SIZE / 1024);  	kps = (k * 100) / centisecs; -	printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", +	printk(KERN_INFO "PM: %s %u kbytes in %u.%02u seconds (%u.%02u MB/s)\n",  			msg, k,  			centisecs / 100, centisecs % 100,  			kps / 1000, (kps % 1000) / 10); @@ -287,16 +299,18 @@ static int create_image(int platform_mode)  	in_suspend = 1;  	save_processor_state(); +	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);  	error = swsusp_arch_suspend(); +	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);  	if (error)  		printk(KERN_ERR "PM: Error %d creating hibernation image\n",  			error);  	/* Restore control flow magically appears here */  	restore_processor_state(); -	if (!in_suspend) { +	if (!in_suspend)  		events_check_enabled = false; -		platform_leave(platform_mode); -	} + +	platform_leave(platform_mode);   Power_up:  	syscore_resume(); @@ -594,7 +608,8 @@ static void power_down(void)  	case HIBERNATION_PLATFORM:  		hibernation_platform_enter();  	case HIBERNATION_SHUTDOWN: -		kernel_power_off(); +		if (pm_power_off) +			kernel_power_off();  		break;  #ifdef CONFIG_SUSPEND  	case HIBERNATION_SUSPEND: @@ -622,7 +637,8 @@ static void power_down(void)  	 * corruption after resume.  	 */  	printk(KERN_CRIT "PM: Please power down manually\n"); -	while(1); +	while (1) +		cpu_relax();  }  /** @@ -632,6 +648,11 @@ int hibernate(void)  {  	int error; +	if (!hibernation_available()) { +		pr_debug("PM: Hibernation not available.\n"); +		return -EPERM; +	} +  	lock_system_sleep();  	/* The snapshot device should not be opened while we're running */  	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { @@ -724,7 +745,7 @@ static int software_resume(void)  	/*  	 * If the user said "noresume".. bail out early.  	 */ -	if (noresume) +	if (noresume || !hibernation_available())  		return 0;  	/* @@ -846,7 +867,7 @@ static int software_resume(void)  	goto Finish;  } -late_initcall(software_resume); +late_initcall_sync(software_resume);  static const char * const hibernation_modes[] = { @@ -890,6 +911,9 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,  	int i;  	char *start = buf; +	if (!hibernation_available()) +		return sprintf(buf, "[disabled]\n"); +  	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {  		if (!hibernation_modes[i])  			continue; @@ -924,6 +948,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,  	char *p;  	int mode = HIBERNATION_INVALID; +	if (!hibernation_available()) +		return -EPERM; +  	p = memchr(buf, '\n', n);  	len = p ? p - buf : n; @@ -972,16 +999,20 @@ static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr,  static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,  			    const char *buf, size_t n)  { -	unsigned int maj, min;  	dev_t res; -	int ret = -EINVAL; +	int len = n; +	char *name; -	if (sscanf(buf, "%u:%u", &maj, &min) != 2) -		goto out; +	if (len && buf[len-1] == '\n') +		len--; +	name = kstrndup(buf, len, GFP_KERNEL); +	if (!name) +		return -ENOMEM; -	res = MKDEV(maj,min); -	if (maj != MAJOR(res) || min != MINOR(res)) -		goto out; +	res = name_to_dev_t(name); +	kfree(name); +	if (!res) +		return -EINVAL;  	lock_system_sleep();  	swsusp_resume_device = res; @@ -989,9 +1020,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,  	printk(KERN_INFO "PM: Starting manual resume from disk\n");  	noresume = 0;  	software_resume(); -	ret = n; - out: -	return ret; +	return n;  }  power_attr(resume); @@ -1089,6 +1118,10 @@ static int __init hibernate_setup(char *str)  		noresume = 1;  	else if (!strncmp(str, "nocompress", 10))  		nocompress = 1; +	else if (!strncmp(str, "no", 2)) { +		noresume = 1; +		nohibernate = 1; +	}  	return 1;  } @@ -1106,13 +1139,30 @@ static int __init resumewait_setup(char *str)  static int __init resumedelay_setup(char *str)  { -	resume_delay = simple_strtoul(str, NULL, 0); +	int rc = kstrtouint(str, 0, &resume_delay); + +	if (rc) +		return rc;  	return 1;  } +static int __init nohibernate_setup(char *str) +{ +	noresume = 1; +	nohibernate = 1; +	return 1; +} + +static int __init kaslr_nohibernate_setup(char *str) +{ +	return nohibernate_setup(str); +} +  __setup("noresume", noresume_setup);  __setup("resume_offset=", resume_offset_setup);  __setup("resume=", resume_setup);  __setup("hibernate=", hibernate_setup);  __setup("resumewait", resumewait_setup);  __setup("resumedelay=", resumedelay_setup); +__setup("nohibernate", nohibernate_setup); +__setup("kaslr", kaslr_nohibernate_setup);  | 
