Commit b023f46813cde6e3b8a8c24f432ff9c1fd8e9a64
Committed by
Linus Torvalds
1 parent
fa7194eb99
Exists in
master
and in
20 other branches
memory-hotplug: skip HWPoisoned page when offlining pages
hwpoisoned may be set when we offline a page by the sysfs interface /sys/devices/system/memory/soft_offline_page or /sys/devices/system/memory/hard_offline_page. If we don't clear this flag when onlining pages, this page can't be freed, and will not in free list. So we can't offline these pages again. So we should skip such page when offlining pages. Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> Cc: David Rientjes <rientjes@google.com> Cc: Jiang Liu <liuj97@gmail.com> Cc: Len Brown <len.brown@intel.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Christoph Lameter <cl@linux.com> Cc: Minchan Kim <minchan.kim@gmail.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: Mel Gorman <mel@csn.ul.ie> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 5 changed files with 53 additions and 18 deletions Side-by-side Diff
include/linux/page-isolation.h
... | ... | @@ -2,7 +2,8 @@ |
2 | 2 | #define __LINUX_PAGEISOLATION_H |
3 | 3 | |
4 | 4 | |
5 | -bool has_unmovable_pages(struct zone *zone, struct page *page, int count); | |
5 | +bool has_unmovable_pages(struct zone *zone, struct page *page, int count, | |
6 | + bool skip_hwpoisoned_pages); | |
6 | 7 | void set_pageblock_migratetype(struct page *page, int migratetype); |
7 | 8 | int move_freepages_block(struct zone *zone, struct page *page, |
8 | 9 | int migratetype); |
... | ... | @@ -21,7 +22,7 @@ |
21 | 22 | */ |
22 | 23 | int |
23 | 24 | start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, |
24 | - unsigned migratetype); | |
25 | + unsigned migratetype, bool skip_hwpoisoned_pages); | |
25 | 26 | |
26 | 27 | /* |
27 | 28 | * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE. |
28 | 29 | |
... | ... | @@ -34,12 +35,13 @@ |
34 | 35 | /* |
35 | 36 | * Test all pages in [start_pfn, end_pfn) are isolated or not. |
36 | 37 | */ |
37 | -int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn); | |
38 | +int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn, | |
39 | + bool skip_hwpoisoned_pages); | |
38 | 40 | |
39 | 41 | /* |
40 | 42 | * Internal functions. Changes pageblock's migrate type. |
41 | 43 | */ |
42 | -int set_migratetype_isolate(struct page *page); | |
44 | +int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages); | |
43 | 45 | void unset_migratetype_isolate(struct page *page, unsigned migratetype); |
44 | 46 | struct page *alloc_migrate_target(struct page *page, unsigned long private, |
45 | 47 | int **resultp); |
mm/memory-failure.c
... | ... | @@ -1385,7 +1385,7 @@ |
1385 | 1385 | * Isolate the page, so that it doesn't get reallocated if it |
1386 | 1386 | * was free. |
1387 | 1387 | */ |
1388 | - set_migratetype_isolate(p); | |
1388 | + set_migratetype_isolate(p, true); | |
1389 | 1389 | /* |
1390 | 1390 | * When the target page is a free hugepage, just remove it |
1391 | 1391 | * from free hugepage list. |
mm/memory_hotplug.c
... | ... | @@ -847,7 +847,7 @@ |
847 | 847 | { |
848 | 848 | int ret; |
849 | 849 | long offlined = *(long *)data; |
850 | - ret = test_pages_isolated(start_pfn, start_pfn + nr_pages); | |
850 | + ret = test_pages_isolated(start_pfn, start_pfn + nr_pages, true); | |
851 | 851 | offlined = nr_pages; |
852 | 852 | if (!ret) |
853 | 853 | *(long *)data += offlined; |
... | ... | @@ -894,7 +894,8 @@ |
894 | 894 | nr_pages = end_pfn - start_pfn; |
895 | 895 | |
896 | 896 | /* set above range as isolated */ |
897 | - ret = start_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE); | |
897 | + ret = start_isolate_page_range(start_pfn, end_pfn, | |
898 | + MIGRATE_MOVABLE, true); | |
898 | 899 | if (ret) |
899 | 900 | goto out; |
900 | 901 |
mm/page_alloc.c
... | ... | @@ -5616,7 +5616,8 @@ |
5616 | 5616 | * MIGRATE_MOVABLE block might include unmovable pages. It means you can't |
5617 | 5617 | * expect this function should be exact. |
5618 | 5618 | */ |
5619 | -bool has_unmovable_pages(struct zone *zone, struct page *page, int count) | |
5619 | +bool has_unmovable_pages(struct zone *zone, struct page *page, int count, | |
5620 | + bool skip_hwpoisoned_pages) | |
5620 | 5621 | { |
5621 | 5622 | unsigned long pfn, iter, found; |
5622 | 5623 | int mt; |
... | ... | @@ -5651,6 +5652,13 @@ |
5651 | 5652 | continue; |
5652 | 5653 | } |
5653 | 5654 | |
5655 | + /* | |
5656 | + * The HWPoisoned page may be not in buddy system, and | |
5657 | + * page_count() is not 0. | |
5658 | + */ | |
5659 | + if (skip_hwpoisoned_pages && PageHWPoison(page)) | |
5660 | + continue; | |
5661 | + | |
5654 | 5662 | if (!PageLRU(page)) |
5655 | 5663 | found++; |
5656 | 5664 | /* |
... | ... | @@ -5693,7 +5701,7 @@ |
5693 | 5701 | zone->zone_start_pfn + zone->spanned_pages <= pfn) |
5694 | 5702 | return false; |
5695 | 5703 | |
5696 | - return !has_unmovable_pages(zone, page, 0); | |
5704 | + return !has_unmovable_pages(zone, page, 0, true); | |
5697 | 5705 | } |
5698 | 5706 | |
5699 | 5707 | #ifdef CONFIG_CMA |
... | ... | @@ -5864,7 +5872,8 @@ |
5864 | 5872 | */ |
5865 | 5873 | |
5866 | 5874 | ret = start_isolate_page_range(pfn_max_align_down(start), |
5867 | - pfn_max_align_up(end), migratetype); | |
5875 | + pfn_max_align_up(end), migratetype, | |
5876 | + false); | |
5868 | 5877 | if (ret) |
5869 | 5878 | return ret; |
5870 | 5879 | |
... | ... | @@ -5903,7 +5912,7 @@ |
5903 | 5912 | } |
5904 | 5913 | |
5905 | 5914 | /* Make sure the range is really isolated. */ |
5906 | - if (test_pages_isolated(outer_start, end)) { | |
5915 | + if (test_pages_isolated(outer_start, end, false)) { | |
5907 | 5916 | pr_warn("alloc_contig_range test_pages_isolated(%lx, %lx) failed\n", |
5908 | 5917 | outer_start, end); |
5909 | 5918 | ret = -EBUSY; |
... | ... | @@ -6018,6 +6027,16 @@ |
6018 | 6027 | continue; |
6019 | 6028 | } |
6020 | 6029 | page = pfn_to_page(pfn); |
6030 | + /* | |
6031 | + * The HWPoisoned page may be not in buddy system, and | |
6032 | + * page_count() is not 0. | |
6033 | + */ | |
6034 | + if (unlikely(!PageBuddy(page) && PageHWPoison(page))) { | |
6035 | + pfn++; | |
6036 | + SetPageReserved(page); | |
6037 | + continue; | |
6038 | + } | |
6039 | + | |
6021 | 6040 | BUG_ON(page_count(page)); |
6022 | 6041 | BUG_ON(!PageBuddy(page)); |
6023 | 6042 | order = page_order(page); |
mm/page_isolation.c
... | ... | @@ -30,7 +30,7 @@ |
30 | 30 | zone->nr_pageblock_isolate--; |
31 | 31 | } |
32 | 32 | |
33 | -int set_migratetype_isolate(struct page *page) | |
33 | +int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages) | |
34 | 34 | { |
35 | 35 | struct zone *zone; |
36 | 36 | unsigned long flags, pfn; |
... | ... | @@ -66,7 +66,8 @@ |
66 | 66 | * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. |
67 | 67 | * We just check MOVABLE pages. |
68 | 68 | */ |
69 | - if (!has_unmovable_pages(zone, page, arg.pages_found)) | |
69 | + if (!has_unmovable_pages(zone, page, arg.pages_found, | |
70 | + skip_hwpoisoned_pages)) | |
70 | 71 | ret = 0; |
71 | 72 | |
72 | 73 | /* |
... | ... | @@ -134,7 +135,7 @@ |
134 | 135 | * Returns 0 on success and -EBUSY if any part of range cannot be isolated. |
135 | 136 | */ |
136 | 137 | int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, |
137 | - unsigned migratetype) | |
138 | + unsigned migratetype, bool skip_hwpoisoned_pages) | |
138 | 139 | { |
139 | 140 | unsigned long pfn; |
140 | 141 | unsigned long undo_pfn; |
... | ... | @@ -147,7 +148,8 @@ |
147 | 148 | pfn < end_pfn; |
148 | 149 | pfn += pageblock_nr_pages) { |
149 | 150 | page = __first_valid_page(pfn, pageblock_nr_pages); |
150 | - if (page && set_migratetype_isolate(page)) { | |
151 | + if (page && | |
152 | + set_migratetype_isolate(page, skip_hwpoisoned_pages)) { | |
151 | 153 | undo_pfn = pfn; |
152 | 154 | goto undo; |
153 | 155 | } |
... | ... | @@ -190,7 +192,8 @@ |
190 | 192 | * Returns 1 if all pages in the range are isolated. |
191 | 193 | */ |
192 | 194 | static int |
193 | -__test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn) | |
195 | +__test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn, | |
196 | + bool skip_hwpoisoned_pages) | |
194 | 197 | { |
195 | 198 | struct page *page; |
196 | 199 | |
... | ... | @@ -220,6 +223,14 @@ |
220 | 223 | else if (page_count(page) == 0 && |
221 | 224 | get_freepage_migratetype(page) == MIGRATE_ISOLATE) |
222 | 225 | pfn += 1; |
226 | + else if (skip_hwpoisoned_pages && PageHWPoison(page)) { | |
227 | + /* | |
228 | + * The HWPoisoned page may be not in buddy | |
229 | + * system, and page_count() is not 0. | |
230 | + */ | |
231 | + pfn++; | |
232 | + continue; | |
233 | + } | |
223 | 234 | else |
224 | 235 | break; |
225 | 236 | } |
... | ... | @@ -228,7 +239,8 @@ |
228 | 239 | return 1; |
229 | 240 | } |
230 | 241 | |
231 | -int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn) | |
242 | +int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn, | |
243 | + bool skip_hwpoisoned_pages) | |
232 | 244 | { |
233 | 245 | unsigned long pfn, flags; |
234 | 246 | struct page *page; |
... | ... | @@ -251,7 +263,8 @@ |
251 | 263 | /* Check all pages are free or Marked as ISOLATED */ |
252 | 264 | zone = page_zone(page); |
253 | 265 | spin_lock_irqsave(&zone->lock, flags); |
254 | - ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn); | |
266 | + ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn, | |
267 | + skip_hwpoisoned_pages); | |
255 | 268 | spin_unlock_irqrestore(&zone->lock, flags); |
256 | 269 | return ret ? 0 : -EBUSY; |
257 | 270 | } |