Revert "PM / Hibernate: Iterate over set bits instead of PFNs in swsusp_free()"
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 24 Sep 2014 22:53:44 +0000 (00:53 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 24 Sep 2014 22:59:54 +0000 (00:59 +0200)
Revert commit 6efde38f0769 (PM / Hibernate: Iterate over set bits
instead of PFNs in swsusp_free()) that introduced a NULL pointer
dereference during system resume from hibernation:

BUG: unable to handle kernel NULL pointer dereference at (null)
IP: [<ffffffff810a8cc1>] swsusp_free+0x21/0x190
PGD b39c2067 PUD b39c1067 PMD 0
Oops: 0000 [#1] SMP
Modules linked in: <irrelevant list of modules>
CPU: 1 PID: 4898 Comm: s2disk Tainted: G         C     3.17-rc5-amd64 #1 Debian 3.17~rc5-1~exp1
Hardware name: LENOVO 2776LEG/2776LEG, BIOS 6EET55WW (3.15 ) 12/19/2011
task: ffff88023155ea40 ti: ffff8800b3b14000 task.ti: ffff8800b3b14000
RIP: 0010:[<ffffffff810a8cc1>]  [<ffffffff810a8cc1>]
swsusp_free+0x21/0x190
RSP: 0018:ffff8800b3b17ea8  EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff8800b39bab00 RCX: 0000000000000001
RDX: ffff8800b39bab10 RSI: ffff8800b39bab00 RDI: 0000000000000000
RBP: 0000000000000010 R08: 0000000000000000 R09: 0000000000000000
R10: ffff8800b39bab10 R11: 0000000000000246 R12: ffffea0000000000
R13: ffff880232f485a0 R14: ffff88023ac27cd8 R15: ffff880232927590
FS:  00007f406d83b700(0000) GS:ffff88023bc80000(0000)
knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000000 CR3: 00000000b3a62000 CR4: 00000000000007e0
Stack:
 ffff8800b39bab00 0000000000000010 ffff880232927590 ffffffff810acb4a
 ffff8800b39bab00 ffffffff811a955a ffff8800b39bab10 0000000000000000
 ffff88023155f098 ffffffff81a6b8c0 ffff88023155ea40 0000000000000007
Call Trace:
 [<ffffffff810acb4a>] ? snapshot_release+0x2a/0xb0
 [<ffffffff811a955a>] ? __fput+0xca/0x1d0
 [<ffffffff81080627>] ? task_work_run+0x97/0xd0
 [<ffffffff81012d89>] ? do_notify_resume+0x69/0xa0
 [<ffffffff8151452a>] ? int_signal+0x12/0x17
Code: 66 2e 0f 1f 84 00 00 00 00 00 66 66 66 66 90 41 54 48 8b 05 ba 62 9c 00 49 bc 00 00 00 00 00 ea ff ff 48 8b 3d a1 62 9c 00 55 53 <48> 8b 10 48 89 50 18 48 8b 52 20 48 c7 40 28 00 00 00 00 c7 40
RIP  [<ffffffff810a8cc1>] swsusp_free+0x21/0x190
 RSP <ffff8800b3b17ea8>
CR2: 0000000000000000
---[ end trace f02be86a1ec0cccb ]---

due to forbidden_pages_map being NULL in swsusp_free().

Fixes: 6efde38f0769 "PM / Hibernate: Iterate over set bits instead of PFNs in swsusp_free()"
Reported-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
kernel/power/snapshot.c

index c4b8093c80b3bad9b6f9cd56a086df5a4c159508..f1604d8cf489a2e0cc689ab82ce6c3adfb82f693 100644 (file)
@@ -725,14 +725,6 @@ 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);
-}
-
 static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn)
 {
        void *addr;
@@ -1341,35 +1333,23 @@ static struct memory_bitmap copy_bm;
 
 void swsusp_free(void)
 {
-       unsigned long fb_pfn, fr_pfn;
-
-       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);
+       struct zone *zone;
+       unsigned long pfn, max_zone_pfn;
 
-               memory_bm_clear_current(forbidden_pages_map);
-               memory_bm_clear_current(free_pages_map);
-               __free_page(page);
-               goto loop;
+       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);
+                               }
+                       }
        }
-
        nr_copy_pages = 0;
        nr_meta_pages = 0;
        restore_pblist = NULL;