1 From 0bf380bc70ecba68cb4d74dc656cc2fa8c4d801a Mon Sep 17 00:00:00 2001
2 From: Mel Gorman <mgorman@suse.de>
3 Date: Fri, 3 Feb 2012 15:37:18 -0800
4 Subject: mm: compaction: check pfn_valid when entering a new MAX_ORDER_NR_PAGES block during isolation for migration
6 From: Mel Gorman <mgorman@suse.de>
8 commit 0bf380bc70ecba68cb4d74dc656cc2fa8c4d801a upstream.
10 When isolating for migration, migration starts at the start of a zone
11 which is not necessarily pageblock aligned. Further, it stops isolating
12 when COMPACT_CLUSTER_MAX pages are isolated so migrate_pfn is generally
13 not aligned. This allows isolate_migratepages() to call pfn_to_page() on
14 an invalid PFN which can result in a crash. This was originally reported
15 against a 3.0-based kernel with the following trace in a crash dump.
17 PID: 9902 TASK: d47aecd0 CPU: 0 COMMAND: "memcg_process_s"
18 #0 [d72d3ad0] crash_kexec at c028cfdb
19 #1 [d72d3b24] oops_end at c05c5322
20 #2 [d72d3b38] __bad_area_nosemaphore at c0227e60
21 #3 [d72d3bec] bad_area at c0227fb6
22 #4 [d72d3c00] do_page_fault at c05c72ec
23 #5 [d72d3c80] error_code (via page_fault) at c05c47a4
24 EAX: 00000000 EBX: 000c0000 ECX: 00000001 EDX: 00000807 EBP: 000c0000
25 DS: 007b ESI: 00000001 ES: 007b EDI: f3000a80 GS: 6f50
26 CS: 0060 EIP: c030b15a ERR: ffffffff EFLAGS: 00010002
27 #6 [d72d3cb4] isolate_migratepages at c030b15a
28 #7 [d72d3d14] zone_watermark_ok at c02d26cb
29 #8 [d72d3d2c] compact_zone at c030b8de
30 #9 [d72d3d68] compact_zone_order at c030bba1
31 #10 [d72d3db4] try_to_compact_pages at c030bc84
32 #11 [d72d3ddc] __alloc_pages_direct_compact at c02d61e7
33 #12 [d72d3e08] __alloc_pages_slowpath at c02d66c7
34 #13 [d72d3e78] __alloc_pages_nodemask at c02d6a97
35 #14 [d72d3eb8] alloc_pages_vma at c030a845
36 #15 [d72d3ed4] do_huge_pmd_anonymous_page at c03178eb
37 #16 [d72d3f00] handle_mm_fault at c02f36c6
38 #17 [d72d3f30] do_page_fault at c05c70ed
39 #18 [d72d3fb0] error_code (via page_fault) at c05c47a4
40 EAX: b71ff000 EBX: 00000001 ECX: 00001600 EDX: 00000431
41 DS: 007b ESI: 08048950 ES: 007b EDI: bfaa3788
42 SS: 007b ESP: bfaa36e0 EBP: bfaa3828 GS: 6f50
43 CS: 0073 EIP: 080487c8 ERR: ffffffff EFLAGS: 00010202
45 It was also reported by Herbert van den Bergh against 3.1-based kernel
46 with the following snippet from the console log.
48 BUG: unable to handle kernel paging request at 01c00008
49 IP: [<c0522399>] isolate_migratepages+0x119/0x390
50 *pdpt = 000000002f7ce001 *pde = 0000000000000000
52 It is expected that it also affects 3.2.x and current mainline.
54 The problem is that pfn_valid is only called on the first PFN being
55 checked and that PFN is not necessarily aligned. Lets say we have a case
58 H = MAX_ORDER_NR_PAGES boundary
59 | = pageblock boundary
64 H------|------H------|----m-Hoooooo|ooooooH-f----|------H
66 The migrate_pfn is just below a memory hole and the free scanner is beyond
67 the hole. When isolate_migratepages started, it scans from migrate_pfn to
68 migrate_pfn+pageblock_nr_pages which is now in a memory hole. It checks
69 pfn_valid() on the first PFN but then scans into the hole where there are
70 not necessarily valid struct pages.
72 This patch ensures that isolate_migratepages calls pfn_valid when
75 Reported-by: Herbert van den Bergh <herbert.van.den.bergh@oracle.com>
76 Tested-by: Herbert van den Bergh <herbert.van.den.bergh@oracle.com>
77 Signed-off-by: Mel Gorman <mgorman@suse.de>
78 Acked-by: Michal Nazarewicz <mina86@mina86.com>
79 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
80 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
81 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
84 mm/compaction.c | 13 +++++++++++++
85 1 file changed, 13 insertions(+)
89 @@ -320,6 +320,19 @@ static isolate_migrate_t isolate_migrate
91 spin_lock_irq(&zone->lru_lock);
94 + * migrate_pfn does not necessarily start aligned to a
95 + * pageblock. Ensure that pfn_valid is called when moving
96 + * into a new MAX_ORDER_NR_PAGES range in case of large
97 + * memory holes within the zone
99 + if ((low_pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) {
100 + if (!pfn_valid(low_pfn)) {
101 + low_pfn += MAX_ORDER_NR_PAGES - 1;
106 if (!pfn_valid_within(low_pfn))