|
@@ -1066,69 +1066,180 @@ void swsusp_free(void)
|
|
buffer = NULL;
|
|
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)
|
|
int swsusp_shrink_memory(void)
|
|
{
|
|
{
|
|
- long tmp;
|
|
|
|
struct zone *zone;
|
|
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;
|
|
struct timeval start, stop;
|
|
|
|
+ int error = 0;
|
|
|
|
|
|
- printk(KERN_INFO "PM: Shrinking memory... ");
|
|
|
|
|
|
+ printk(KERN_INFO "PM: Shrinking memory... ");
|
|
do_gettimeofday(&start);
|
|
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);
|
|
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");
|
|
swsusp_show_speed(&start, &stop, pages, "Freed");
|
|
|
|
|
|
return 0;
|
|
return 0;
|