diff options
Diffstat (limited to 'kernel/power/hibernate.c')
| -rw-r--r-- | kernel/power/hibernate.c | 68 | 
1 files changed, 57 insertions, 11 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 23c0f4e6cb2f..1f1f30cca573 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -90,6 +90,11 @@ void hibernate_release(void)  	atomic_inc(&hibernate_atomic);  } +bool hibernation_in_progress(void) +{ +	return !atomic_read(&hibernate_atomic); +} +  bool hibernation_available(void)  {  	return nohibernate == 0 && @@ -133,10 +138,15 @@ bool system_entering_hibernation(void)  EXPORT_SYMBOL(system_entering_hibernation);  #ifdef CONFIG_PM_DEBUG +static unsigned int pm_test_delay = 5; +module_param(pm_test_delay, uint, 0644); +MODULE_PARM_DESC(pm_test_delay, +		 "Number of seconds to wait before resuming from hibernation test");  static void hibernation_debug_sleep(void)  { -	pr_info("debug: Waiting for 5 seconds.\n"); -	mdelay(5000); +	pr_info("hibernation debug: Waiting for %d second(s).\n", +		pm_test_delay); +	mdelay(pm_test_delay * 1000);  }  static int hibernation_test(int level) @@ -371,6 +381,23 @@ static int create_image(int platform_mode)  	return error;  } +static void shrink_shmem_memory(void) +{ +	struct sysinfo info; +	unsigned long nr_shmem_pages, nr_freed_pages; + +	si_meminfo(&info); +	nr_shmem_pages = info.sharedram; /* current page count used for shmem */ +	/* +	 * The intent is to reclaim all shmem pages. Though shrink_all_memory() can +	 * only reclaim about half of them, it's enough for creating the hibernation +	 * image. +	 */ +	nr_freed_pages = shrink_all_memory(nr_shmem_pages); +	pr_debug("requested to reclaim %lu shmem pages, actually freed %lu pages\n", +			nr_shmem_pages, nr_freed_pages); +} +  /**   * hibernation_snapshot - Quiesce devices and create a hibernation image.   * @platform_mode: If set, use platform driver to prepare for the transition. @@ -412,8 +439,16 @@ int hibernation_snapshot(int platform_mode)  		goto Thaw;  	} +	/* +	 * Device drivers may move lots of data to shmem in dpm_prepare(). The shmem +	 * pages will use lots of system memory, causing hibernation image creation +	 * fail due to insufficient free memory. +	 * This call is to force flush the shmem pages to swap disk and reclaim +	 * the system memory so that image creation can succeed. +	 */ +	shrink_shmem_memory(); +  	console_suspend_all(); -	pm_restrict_gfp_mask();  	error = dpm_suspend(PMSG_FREEZE); @@ -549,7 +584,6 @@ int hibernation_restore(int platform_mode)  	pm_prepare_console();  	console_suspend_all(); -	pm_restrict_gfp_mask();  	error = dpm_suspend_start(PMSG_QUIESCE);  	if (!error) {  		error = resume_target_kernel(platform_mode); @@ -561,7 +595,6 @@ int hibernation_restore(int platform_mode)  		BUG_ON(!error);  	}  	dpm_resume_end(PMSG_RECOVER); -	pm_restore_gfp_mask();  	console_resume_all();  	pm_restore_console();  	return error; @@ -757,7 +790,7 @@ int hibernate(void)  	 * Query for the compression algorithm support if compression is enabled.  	 */  	if (!nocompress) { -		strscpy(hib_comp_algo, hibernate_compressor, sizeof(hib_comp_algo)); +		strscpy(hib_comp_algo, hibernate_compressor);  		if (!crypto_has_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC)) {  			pr_err("%s compression is not available\n", hib_comp_algo);  			return -EOPNOTSUPP; @@ -778,6 +811,8 @@ int hibernate(void)  		goto Restore;  	ksys_sync_helper(); +	if (filesystem_freeze_enabled) +		filesystems_freeze();  	error = freeze_processes();  	if (error) @@ -846,6 +881,7 @@ int hibernate(void)  	/* Don't bother checking whether freezer_test_done is true */  	freezer_test_done = false;   Exit: +	filesystems_thaw();  	pm_notifier_call_chain(PM_POST_HIBERNATION);   Restore:  	pm_restore_console(); @@ -882,6 +918,9 @@ int hibernate_quiet_exec(int (*func)(void *data), void *data)  	if (error)  		goto restore; +	if (filesystem_freeze_enabled) +		filesystems_freeze(); +  	error = freeze_processes();  	if (error)  		goto exit; @@ -941,6 +980,7 @@ thaw:  	thaw_processes();  exit: +	filesystems_thaw();  	pm_notifier_call_chain(PM_POST_HIBERNATION);  restore: @@ -1006,9 +1046,9 @@ static int software_resume(void)  	 */  	if (!(swsusp_header_flags & SF_NOCOMPRESS_MODE)) {  		if (swsusp_header_flags & SF_COMPRESSION_ALG_LZ4) -			strscpy(hib_comp_algo, COMPRESSION_ALGO_LZ4, sizeof(hib_comp_algo)); +			strscpy(hib_comp_algo, COMPRESSION_ALGO_LZ4);  		else -			strscpy(hib_comp_algo, COMPRESSION_ALGO_LZO, sizeof(hib_comp_algo)); +			strscpy(hib_comp_algo, COMPRESSION_ALGO_LZO);  		if (!crypto_has_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC)) {  			pr_err("%s compression is not available\n", hib_comp_algo);  			error = -EOPNOTSUPP; @@ -1029,19 +1069,26 @@ static int software_resume(void)  	if (error)  		goto Restore; +	if (filesystem_freeze_enabled) +		filesystems_freeze(); +  	pm_pr_dbg("Preparing processes for hibernation restore.\n");  	error = freeze_processes(); -	if (error) +	if (error) { +		filesystems_thaw();  		goto Close_Finish; +	}  	error = freeze_kernel_threads();  	if (error) {  		thaw_processes(); +		filesystems_thaw();  		goto Close_Finish;  	}  	error = load_image_and_restore();  	thaw_processes(); +	filesystems_thaw();   Finish:  	pm_notifier_call_chain(PM_POST_RESTORE);   Restore: @@ -1456,8 +1503,7 @@ static int hibernate_compressor_param_set(const char *compressor,  	if (index >= 0) {  		ret = param_set_copystring(comp_alg_enabled[index], kp);  		if (!ret) -			strscpy(hib_comp_algo, comp_alg_enabled[index], -				sizeof(hib_comp_algo)); +			strscpy(hib_comp_algo, comp_alg_enabled[index]);  	} else {  		ret = index;  	}  | 
