Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 161345
b: refs/heads/master
c: 4bb3343
h: refs/heads/master
i:
  161343: 15feb69
v: v3
  • Loading branch information
Rafael J. Wysocki committed Sep 14, 2009
1 parent 9b07d84 commit 2c9a3e0
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 48 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e681c9dd62fe8fcc5bba28a3ca3f7dc8be940206
refs/heads/master: 4bb334353ebd821bc8eeabeb019eaac33c7307df
205 changes: 158 additions & 47 deletions trunk/kernel/power/snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,69 +1066,180 @@ void swsusp_free(void)
buffer = NULL;
}

/* Helper functions used for the shrinking of memory. */

#define GFP_IMAGE (GFP_KERNEL | __GFP_NOWARN)

/**
* swsusp_shrink_memory - Try to free as much memory as needed
*
* ... but do not OOM-kill anyone
* preallocate_image_pages - Allocate a number of pages for hibernation image
* @nr_pages: Number of page frames to allocate.
* @mask: GFP flags to use for the allocation.
*
* Notice: all userland should be stopped before it is called, or
* livelock is possible.
* Return value: Number of page frames actually allocated
*/
static unsigned long preallocate_image_pages(unsigned long nr_pages, gfp_t mask)
{
unsigned long nr_alloc = 0;

while (nr_pages > 0) {
if (!alloc_image_page(mask))
break;
nr_pages--;
nr_alloc++;
}

#define SHRINK_BITE 10000
static inline unsigned long __shrink_memory(long tmp)
return nr_alloc;
}

static unsigned long preallocate_image_memory(unsigned long nr_pages)
{
return preallocate_image_pages(nr_pages, GFP_IMAGE);
}

#ifdef CONFIG_HIGHMEM
static unsigned long preallocate_image_highmem(unsigned long nr_pages)
{
if (tmp > SHRINK_BITE)
tmp = SHRINK_BITE;
return shrink_all_memory(tmp);
return preallocate_image_pages(nr_pages, GFP_IMAGE | __GFP_HIGHMEM);
}

/**
* __fraction - Compute (an approximation of) x * (multiplier / base)
*/
static unsigned long __fraction(u64 x, u64 multiplier, u64 base)
{
x *= multiplier;
do_div(x, base);
return (unsigned long)x;
}

static unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
unsigned long highmem,
unsigned long total)
{
unsigned long alloc = __fraction(nr_pages, highmem, total);

return preallocate_image_pages(alloc, GFP_IMAGE | __GFP_HIGHMEM);
}
#else /* CONFIG_HIGHMEM */
static inline unsigned long preallocate_image_highmem(unsigned long nr_pages)
{
return 0;
}

static inline unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
unsigned long highmem,
unsigned long total)
{
return 0;
}
#endif /* CONFIG_HIGHMEM */

/**
* swsusp_shrink_memory - Make the kernel release as much memory as needed
*
* To create a hibernation image it is necessary to make a copy of every page
* 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
*
* ([page frames total] + PAGES_FOR_IO + [metadata pages]) / 2 + 2 * SPARE_PAGES
*
* of them, which corresponds to the maximum size of a hibernation image.
*
* If image_size is set below the number following from the above formula,
* the preallocation of memory is continued until the total number of saveable
* pages in the system is below the requested image size or it is impossible to
* allocate more memory, whichever happens first.
*/
int swsusp_shrink_memory(void)
{
long tmp;
struct zone *zone;
unsigned long pages = 0;
unsigned int i = 0;
char *p = "-\\|/";
unsigned long saveable, size, max_size, count, highmem, pages = 0;
unsigned long alloc, pages_highmem;
struct timeval start, stop;
int error = 0;

printk(KERN_INFO "PM: Shrinking memory... ");
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;
/* Count the number of saveable data pages. */
highmem = count_highmem_pages();
saveable = count_data_pages();

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);
/*
* Compute the total number of page frames we can use (count) and the
* number of pages needed for image metadata (size).
*/
count = saveable;
saveable += highmem;
size = 0;
for_each_populated_zone(zone) {
size += snapshot_additional_pages(zone);
if (is_highmem(zone))
highmem += zone_page_state(zone, NR_FREE_PAGES);
else
count += zone_page_state(zone, NR_FREE_PAGES);
}
count += highmem;
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;
size = DIV_ROUND_UP(image_size, PAGE_SIZE);
if (size > max_size)
size = max_size;
/*
* If the maximum is not less than the current number of saveable pages
* in memory, we don't need to do anything more.
*/
if (size >= saveable)
goto out;

/*
* Let the memory management subsystem know that we're going to need a
* large number of page frames to allocate and make it free some memory.
* NOTE: If this is not done, performance will be hurt badly in some
* test cases.
*/
shrink_all_memory(saveable - size);

/*
* The number of saveable pages in memory was too high, so apply some
* pressure to decrease it. First, make room for the largest possible
* image and fail if that doesn't work. Next, try to decrease the size
* of the image as much as indicated by image_size using allocations
* from highmem and non-highmem zones separately.
*/
pages_highmem = preallocate_image_highmem(highmem / 2);
alloc = (count - max_size) - pages_highmem;
pages = preallocate_image_memory(alloc);
if (pages < alloc) {
error = -ENOMEM;
goto free_out;
}
size = max_size - size;
alloc = size;
size = preallocate_highmem_fraction(size, highmem, count);
pages_highmem += size;
alloc -= size;
pages += preallocate_image_memory(alloc);
pages += pages_highmem;

free_out:
/* Release all of the preallocated page frames. */
swsusp_free();

if (error) {
printk(KERN_CONT "\n");
return error;
}

out:
do_gettimeofday(&stop);
printk("\bdone (%lu pages freed)\n", pages);
printk(KERN_CONT "done (preallocated %lu free pages)\n", pages);
swsusp_show_speed(&start, &stop, pages, "Freed");

return 0;
Expand Down

0 comments on commit 2c9a3e0

Please sign in to comment.