diff options
Diffstat (limited to 'kernel/power')
| -rw-r--r-- | kernel/power/hibernate.c | 23 | ||||
| -rw-r--r-- | kernel/power/main.c | 1 | ||||
| -rw-r--r-- | kernel/power/power.h | 4 | ||||
| -rw-r--r-- | kernel/power/snapshot.c | 33 | ||||
| -rw-r--r-- | kernel/power/suspend.c | 6 | ||||
| -rw-r--r-- | kernel/power/user.c | 5 | 
6 files changed, 59 insertions, 13 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 554d3b049f3..95a2ac40f48 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -968,10 +968,33 @@ static ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *att  power_attr(image_size); +static ssize_t reserved_size_show(struct kobject *kobj, +				  struct kobj_attribute *attr, char *buf) +{ +	return sprintf(buf, "%lu\n", reserved_size); +} + +static ssize_t reserved_size_store(struct kobject *kobj, +				   struct kobj_attribute *attr, +				   const char *buf, size_t n) +{ +	unsigned long size; + +	if (sscanf(buf, "%lu", &size) == 1) { +		reserved_size = size; +		return n; +	} + +	return -EINVAL; +} + +power_attr(reserved_size); +  static struct attribute * g[] = {  	&disk_attr.attr,  	&resume_attr.attr,  	&image_size_attr.attr, +	&reserved_size_attr.attr,  	NULL,  }; diff --git a/kernel/power/main.c b/kernel/power/main.c index de9aef8742f..2981af4ce7c 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -337,6 +337,7 @@ static int __init pm_init(void)  	if (error)  		return error;  	hibernate_image_size_init(); +	hibernate_reserved_size_init();  	power_kobj = kobject_create_and_add("power", NULL);  	if (!power_kobj)  		return -ENOMEM; diff --git a/kernel/power/power.h b/kernel/power/power.h index 03634be55f6..9a00a0a2628 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -15,6 +15,7 @@ struct swsusp_info {  #ifdef CONFIG_HIBERNATION  /* kernel/power/snapshot.c */ +extern void __init hibernate_reserved_size_init(void);  extern void __init hibernate_image_size_init(void);  #ifdef CONFIG_ARCH_HIBERNATION_HEADER @@ -55,6 +56,7 @@ extern int hibernation_platform_enter(void);  #else /* !CONFIG_HIBERNATION */ +static inline void hibernate_reserved_size_init(void) {}  static inline void hibernate_image_size_init(void) {}  #endif /* !CONFIG_HIBERNATION */ @@ -72,6 +74,8 @@ static struct kobj_attribute _name##_attr = {	\  /* Preferred image size in bytes (default 500 MB) */  extern unsigned long image_size; +/* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */ +extern unsigned long reserved_size;  extern int in_suspend;  extern dev_t swsusp_resume_device;  extern sector_t swsusp_resume_block; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index ca0aacc2487..ace55889f70 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -41,16 +41,28 @@ static void swsusp_set_page_forbidden(struct page *);  static void swsusp_unset_page_forbidden(struct page *);  /* + * Number of bytes to reserve for memory allocations made by device drivers + * from their ->freeze() and ->freeze_noirq() callbacks so that they don't + * cause image creation to fail (tunable via /sys/power/reserved_size). + */ +unsigned long reserved_size; + +void __init hibernate_reserved_size_init(void) +{ +	reserved_size = SPARE_PAGES * PAGE_SIZE; +} + +/*   * Preferred image size in bytes (tunable via /sys/power/image_size). - * When it is set to N, the image creating code will do its best to - * ensure the image size will not exceed N bytes, but if that is - * impossible, it will try to create the smallest image possible. + * When it is set to N, swsusp will do its best to ensure the image + * size will not exceed N bytes, but if that is impossible, it will + * try to create the smallest image possible.   */  unsigned long image_size;  void __init hibernate_image_size_init(void)  { -	image_size = (totalram_pages / 3) * PAGE_SIZE; +	image_size = ((totalram_pages * 2) / 5) * PAGE_SIZE;  }  /* List of PBEs needed for restoring the pages that were allocated before @@ -1263,11 +1275,13 @@ static unsigned long minimum_image_size(unsigned long saveable)   * frame in use.  We also need a number of page frames to be free during   * hibernation for allocations made while saving the image and for device   * drivers, in case they need to allocate memory from their hibernation - * callbacks (these two numbers are given by PAGES_FOR_IO and SPARE_PAGES, - * respectively, both of which are rough estimates).  To make this happen, we - * compute the total number of available page frames and allocate at least + * callbacks (these two numbers are given by PAGES_FOR_IO (which is a rough + * estimate) and reserverd_size divided by PAGE_SIZE (which is tunable through + * /sys/power/reserved_size, respectively).  To make this happen, we compute the + * total number of available page frames and allocate at least   * - * ([page frames total] + PAGES_FOR_IO + [metadata pages]) / 2 + 2 * SPARE_PAGES + * ([page frames total] + PAGES_FOR_IO + [metadata pages]) / 2 + *  + 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE)   *   * of them, which corresponds to the maximum size of a hibernation image.   * @@ -1322,7 +1336,8 @@ int hibernate_preallocate_memory(void)  	count -= totalreserve_pages;  	/* Compute the maximum number of saveable pages to leave in memory. */ -	max_size = (count - (size + PAGES_FOR_IO)) / 2 - 2 * SPARE_PAGES; +	max_size = (count - (size + PAGES_FOR_IO)) / 2 +			- 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE);  	/* Compute the desired number of image pages specified by image_size. */  	size = DIV_ROUND_UP(image_size, PAGE_SIZE);  	if (size > max_size) diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 732d77a957e..1c41ba21541 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -210,7 +210,6 @@ int suspend_devices_and_enter(suspend_state_t state)  			goto Close;  	}  	suspend_console(); -	pm_restrict_gfp_mask();  	suspend_test_start();  	error = dpm_suspend_start(PMSG_SUSPEND);  	if (error) { @@ -221,13 +220,12 @@ int suspend_devices_and_enter(suspend_state_t state)  	if (suspend_test(TEST_DEVICES))  		goto Recover_platform; -	suspend_enter(state); +	error = suspend_enter(state);   Resume_devices:  	suspend_test_start();  	dpm_resume_end(PMSG_RESUME);  	suspend_test_finish("resume devices"); -	pm_restore_gfp_mask();  	resume_console();   Close:  	if (suspend_ops->end) @@ -288,7 +286,9 @@ int enter_state(suspend_state_t state)  		goto Finish;  	pr_debug("PM: Entering %s sleep\n", pm_states[state]); +	pm_restrict_gfp_mask();  	error = suspend_devices_and_enter(state); +	pm_restore_gfp_mask();   Finish:  	pr_debug("PM: Finishing wakeup.\n"); diff --git a/kernel/power/user.c b/kernel/power/user.c index c36c3b9e8a8..7d02d33be69 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -135,8 +135,10 @@ static int snapshot_release(struct inode *inode, struct file *filp)  	free_basic_memory_bitmaps();  	data = filp->private_data;  	free_all_swap_pages(data->swap); -	if (data->frozen) +	if (data->frozen) { +		pm_restore_gfp_mask();  		thaw_processes(); +	}  	pm_notifier_call_chain(data->mode == O_RDONLY ?  			PM_POST_HIBERNATION : PM_POST_RESTORE);  	atomic_inc(&snapshot_device_available); @@ -379,6 +381,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		 * PM_HIBERNATION_PREPARE  		 */  		error = suspend_devices_and_enter(PM_SUSPEND_MEM); +		data->ready = 0;  		break;  	case SNAPSHOT_PLATFORM_SUPPORT:  |