]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/power/snapshot.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt...
[mirror_ubuntu-bionic-kernel.git] / kernel / power / snapshot.c
index 33e2e4a819f97014a6e70e5f06d52e246ba783b3..523a451b45d37404f3ca1c8e0b59d9c31f45b49a 100644 (file)
@@ -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