PM / Hibernate: Iterate over set bits instead of PFNs in swsusp_free()
[firefly-linux-kernel-4.4.55.git] / kernel / power / snapshot.c
index 802f2415408e53ca9285bf80bad2aba3157ad7a6..5b71caf43d32a0cda76495f2b8d2afee76cf7729 100644 (file)
@@ -848,6 +848,17 @@ static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn)
        clear_bit(bit, addr);
 }
 
+static void memory_bm_clear_current(struct memory_bitmap *bm)
+{
+       int bit;
+
+       bit = max(bm->cur.node_bit - 1, 0);
+       clear_bit(bit, bm->cur.node->data);
+
+       bit = max(bm->cur.bit - 1, 0);
+       clear_bit(bit, bm->cur.block->data);
+}
+
 static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
@@ -1491,23 +1502,35 @@ static struct memory_bitmap copy_bm;
 
 void swsusp_free(void)
 {
-       struct zone *zone;
-       unsigned long pfn, max_zone_pfn;
+       unsigned long fb_pfn, fr_pfn;
 
-       for_each_populated_zone(zone) {
-               max_zone_pfn = zone_end_pfn(zone);
-               for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
-                       if (pfn_valid(pfn)) {
-                               struct page *page = pfn_to_page(pfn);
-
-                               if (swsusp_page_is_forbidden(page) &&
-                                   swsusp_page_is_free(page)) {
-                                       swsusp_unset_page_forbidden(page);
-                                       swsusp_unset_page_free(page);
-                                       __free_page(page);
-                               }
-                       }
+       memory_bm_position_reset(forbidden_pages_map);
+       memory_bm_position_reset(free_pages_map);
+
+loop:
+       fr_pfn = memory_bm_next_pfn(free_pages_map);
+       fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+
+       /*
+        * Find the next bit set in both bitmaps. This is guaranteed to
+        * terminate when fb_pfn == fr_pfn == BM_END_OF_MAP.
+        */
+       do {
+               if (fb_pfn < fr_pfn)
+                       fb_pfn = memory_bm_next_pfn(forbidden_pages_map);
+               if (fr_pfn < fb_pfn)
+                       fr_pfn = memory_bm_next_pfn(free_pages_map);
+       } while (fb_pfn != fr_pfn);
+
+       if (fr_pfn != BM_END_OF_MAP && pfn_valid(fr_pfn)) {
+               struct page *page = pfn_to_page(fr_pfn);
+
+               memory_bm_clear_current(forbidden_pages_map);
+               memory_bm_clear_current(free_pages_map);
+               __free_page(page);
+               goto loop;
        }
+
        nr_copy_pages = 0;
        nr_meta_pages = 0;
        restore_pblist = NULL;