diff options
Diffstat (limited to 'kernel/power/snapshot.c')
| -rw-r--r-- | kernel/power/snapshot.c | 80 | 
1 files changed, 78 insertions, 2 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 33e2e4a819f..523a451b45d 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -39,6 +39,14 @@ static int swsusp_page_is_free(struct page *);  static void swsusp_set_page_forbidden(struct page *);  static void swsusp_unset_page_forbidden(struct page *); +/* + * Preferred image size in bytes (tunable via /sys/power/image_size). + * 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 = 500 * 1024 * 1024; +  /* List of PBEs needed for restoring the pages that were allocated before   * the suspend and included in the suspend image, but have also been   * allocated by the "resume" kernel, so their contents cannot be written @@ -840,7 +848,7 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)   *	pages.   */ -unsigned int count_highmem_pages(void) +static unsigned int count_highmem_pages(void)  {  	struct zone *zone;  	unsigned int n = 0; @@ -902,7 +910,7 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn)   *	pages.   */ -unsigned int count_data_pages(void) +static unsigned int count_data_pages(void)  {  	struct zone *zone;  	unsigned long pfn, max_zone_pfn; @@ -1058,6 +1066,74 @@ void swsusp_free(void)  	buffer = NULL;  } +/** + *	swsusp_shrink_memory -  Try to free as much memory as needed + * + *	... but do not OOM-kill anyone + * + *	Notice: all userland should be stopped before it is called, or + *	livelock is possible. + */ + +#define SHRINK_BITE	10000 +static inline unsigned long __shrink_memory(long tmp) +{ +	if (tmp > SHRINK_BITE) +		tmp = SHRINK_BITE; +	return shrink_all_memory(tmp); +} + +int swsusp_shrink_memory(void) +{ +	long tmp; +	struct zone *zone; +	unsigned long pages = 0; +	unsigned int i = 0; +	char *p = "-\\|/"; +	struct timeval start, stop; + +	printk(KERN_INFO "PM: Shrinking memory...  "); +	do_gettimeofday(&start); +	do { +		long size, highmem_size; + +		highmem_size = count_highmem_pages(); +		size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES; +		tmp = size; +		size += highmem_size; +		for_each_populated_zone(zone) { +			tmp += snapshot_additional_pages(zone); +			if (is_highmem(zone)) { +				highmem_size -= +					zone_page_state(zone, NR_FREE_PAGES); +			} else { +				tmp -= zone_page_state(zone, NR_FREE_PAGES); +				tmp += zone->lowmem_reserve[ZONE_NORMAL]; +			} +		} + +		if (highmem_size < 0) +			highmem_size = 0; + +		tmp += highmem_size; +		if (tmp > 0) { +			tmp = __shrink_memory(tmp); +			if (!tmp) +				return -ENOMEM; +			pages += tmp; +		} else if (size > image_size / PAGE_SIZE) { +			tmp = __shrink_memory(size - (image_size / PAGE_SIZE)); +			pages += tmp; +		} +		printk("\b%c", p[i++%4]); +	} while (tmp > 0); +	do_gettimeofday(&stop); +	printk("\bdone (%lu pages freed)\n", pages); +	swsusp_show_speed(&start, &stop, pages, "Freed"); + +	return 0; +} +  #ifdef CONFIG_HIGHMEM  /**    *	count_pages_for_highmem - compute the number of non-highmem pages  |