Commit b023f468 authored by Wen Congyang's avatar Wen Congyang Committed by Linus Torvalds

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: default avatarWen 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fa7194eb
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
#define __LINUX_PAGEISOLATION_H #define __LINUX_PAGEISOLATION_H
bool has_unmovable_pages(struct zone *zone, struct page *page, int count); bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
bool skip_hwpoisoned_pages);
void set_pageblock_migratetype(struct page *page, int migratetype); void set_pageblock_migratetype(struct page *page, int migratetype);
int move_freepages_block(struct zone *zone, struct page *page, int move_freepages_block(struct zone *zone, struct page *page,
int migratetype); int migratetype);
...@@ -21,7 +22,7 @@ int move_freepages(struct zone *zone, ...@@ -21,7 +22,7 @@ int move_freepages(struct zone *zone,
*/ */
int int
start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
unsigned migratetype); unsigned migratetype, bool skip_hwpoisoned_pages);
/* /*
* Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE. * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE.
...@@ -34,12 +35,13 @@ undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, ...@@ -34,12 +35,13 @@ undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
/* /*
* Test all pages in [start_pfn, end_pfn) are isolated or not. * Test all pages in [start_pfn, end_pfn) are isolated or not.
*/ */
int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn); int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
bool skip_hwpoisoned_pages);
/* /*
* Internal functions. Changes pageblock's migrate type. * Internal functions. Changes pageblock's migrate type.
*/ */
int set_migratetype_isolate(struct page *page); int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages);
void unset_migratetype_isolate(struct page *page, unsigned migratetype); void unset_migratetype_isolate(struct page *page, unsigned migratetype);
struct page *alloc_migrate_target(struct page *page, unsigned long private, struct page *alloc_migrate_target(struct page *page, unsigned long private,
int **resultp); int **resultp);
......
...@@ -1385,7 +1385,7 @@ static int get_any_page(struct page *p, unsigned long pfn, int flags) ...@@ -1385,7 +1385,7 @@ static int get_any_page(struct page *p, unsigned long pfn, int flags)
* Isolate the page, so that it doesn't get reallocated if it * Isolate the page, so that it doesn't get reallocated if it
* was free. * was free.
*/ */
set_migratetype_isolate(p); set_migratetype_isolate(p, true);
/* /*
* When the target page is a free hugepage, just remove it * When the target page is a free hugepage, just remove it
* from free hugepage list. * from free hugepage list.
......
...@@ -847,7 +847,7 @@ check_pages_isolated_cb(unsigned long start_pfn, unsigned long nr_pages, ...@@ -847,7 +847,7 @@ check_pages_isolated_cb(unsigned long start_pfn, unsigned long nr_pages,
{ {
int ret; int ret;
long offlined = *(long *)data; long offlined = *(long *)data;
ret = test_pages_isolated(start_pfn, start_pfn + nr_pages); ret = test_pages_isolated(start_pfn, start_pfn + nr_pages, true);
offlined = nr_pages; offlined = nr_pages;
if (!ret) if (!ret)
*(long *)data += offlined; *(long *)data += offlined;
...@@ -894,7 +894,8 @@ static int __ref __offline_pages(unsigned long start_pfn, ...@@ -894,7 +894,8 @@ static int __ref __offline_pages(unsigned long start_pfn,
nr_pages = end_pfn - start_pfn; nr_pages = end_pfn - start_pfn;
/* set above range as isolated */ /* set above range as isolated */
ret = start_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE); ret = start_isolate_page_range(start_pfn, end_pfn,
MIGRATE_MOVABLE, true);
if (ret) if (ret)
goto out; goto out;
......
...@@ -5616,7 +5616,8 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags, ...@@ -5616,7 +5616,8 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags,
* MIGRATE_MOVABLE block might include unmovable pages. It means you can't * MIGRATE_MOVABLE block might include unmovable pages. It means you can't
* expect this function should be exact. * expect this function should be exact.
*/ */
bool has_unmovable_pages(struct zone *zone, struct page *page, int count) bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
bool skip_hwpoisoned_pages)
{ {
unsigned long pfn, iter, found; unsigned long pfn, iter, found;
int mt; int mt;
...@@ -5651,6 +5652,13 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count) ...@@ -5651,6 +5652,13 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count)
continue; continue;
} }
/*
* The HWPoisoned page may be not in buddy system, and
* page_count() is not 0.
*/
if (skip_hwpoisoned_pages && PageHWPoison(page))
continue;
if (!PageLRU(page)) if (!PageLRU(page))
found++; found++;
/* /*
...@@ -5693,7 +5701,7 @@ bool is_pageblock_removable_nolock(struct page *page) ...@@ -5693,7 +5701,7 @@ bool is_pageblock_removable_nolock(struct page *page)
zone->zone_start_pfn + zone->spanned_pages <= pfn) zone->zone_start_pfn + zone->spanned_pages <= pfn)
return false; return false;
return !has_unmovable_pages(zone, page, 0); return !has_unmovable_pages(zone, page, 0, true);
} }
#ifdef CONFIG_CMA #ifdef CONFIG_CMA
...@@ -5864,7 +5872,8 @@ int alloc_contig_range(unsigned long start, unsigned long end, ...@@ -5864,7 +5872,8 @@ int alloc_contig_range(unsigned long start, unsigned long end,
*/ */
ret = start_isolate_page_range(pfn_max_align_down(start), ret = start_isolate_page_range(pfn_max_align_down(start),
pfn_max_align_up(end), migratetype); pfn_max_align_up(end), migratetype,
false);
if (ret) if (ret)
return ret; return ret;
...@@ -5903,7 +5912,7 @@ int alloc_contig_range(unsigned long start, unsigned long end, ...@@ -5903,7 +5912,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
} }
/* Make sure the range is really isolated. */ /* Make sure the range is really isolated. */
if (test_pages_isolated(outer_start, end)) { if (test_pages_isolated(outer_start, end, false)) {
pr_warn("alloc_contig_range test_pages_isolated(%lx, %lx) failed\n", pr_warn("alloc_contig_range test_pages_isolated(%lx, %lx) failed\n",
outer_start, end); outer_start, end);
ret = -EBUSY; ret = -EBUSY;
...@@ -6018,6 +6027,16 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn) ...@@ -6018,6 +6027,16 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn)
continue; continue;
} }
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
/*
* The HWPoisoned page may be not in buddy system, and
* page_count() is not 0.
*/
if (unlikely(!PageBuddy(page) && PageHWPoison(page))) {
pfn++;
SetPageReserved(page);
continue;
}
BUG_ON(page_count(page)); BUG_ON(page_count(page));
BUG_ON(!PageBuddy(page)); BUG_ON(!PageBuddy(page));
order = page_order(page); order = page_order(page);
......
...@@ -30,7 +30,7 @@ static void restore_pageblock_isolate(struct page *page, int migratetype) ...@@ -30,7 +30,7 @@ static void restore_pageblock_isolate(struct page *page, int migratetype)
zone->nr_pageblock_isolate--; zone->nr_pageblock_isolate--;
} }
int set_migratetype_isolate(struct page *page) int set_migratetype_isolate(struct page *page, bool skip_hwpoisoned_pages)
{ {
struct zone *zone; struct zone *zone;
unsigned long flags, pfn; unsigned long flags, pfn;
...@@ -66,7 +66,8 @@ int set_migratetype_isolate(struct page *page) ...@@ -66,7 +66,8 @@ int set_migratetype_isolate(struct page *page)
* FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
* We just check MOVABLE pages. * We just check MOVABLE pages.
*/ */
if (!has_unmovable_pages(zone, page, arg.pages_found)) if (!has_unmovable_pages(zone, page, arg.pages_found,
skip_hwpoisoned_pages))
ret = 0; ret = 0;
/* /*
...@@ -134,7 +135,7 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages) ...@@ -134,7 +135,7 @@ __first_valid_page(unsigned long pfn, unsigned long nr_pages)
* Returns 0 on success and -EBUSY if any part of range cannot be isolated. * Returns 0 on success and -EBUSY if any part of range cannot be isolated.
*/ */
int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
unsigned migratetype) unsigned migratetype, bool skip_hwpoisoned_pages)
{ {
unsigned long pfn; unsigned long pfn;
unsigned long undo_pfn; unsigned long undo_pfn;
...@@ -147,7 +148,8 @@ int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, ...@@ -147,7 +148,8 @@ int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
pfn < end_pfn; pfn < end_pfn;
pfn += pageblock_nr_pages) { pfn += pageblock_nr_pages) {
page = __first_valid_page(pfn, pageblock_nr_pages); page = __first_valid_page(pfn, pageblock_nr_pages);
if (page && set_migratetype_isolate(page)) { if (page &&
set_migratetype_isolate(page, skip_hwpoisoned_pages)) {
undo_pfn = pfn; undo_pfn = pfn;
goto undo; goto undo;
} }
...@@ -190,7 +192,8 @@ int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, ...@@ -190,7 +192,8 @@ int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
* Returns 1 if all pages in the range are isolated. * Returns 1 if all pages in the range are isolated.
*/ */
static int static int
__test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn) __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
bool skip_hwpoisoned_pages)
{ {
struct page *page; struct page *page;
...@@ -220,6 +223,14 @@ __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn) ...@@ -220,6 +223,14 @@ __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn)
else if (page_count(page) == 0 && else if (page_count(page) == 0 &&
get_freepage_migratetype(page) == MIGRATE_ISOLATE) get_freepage_migratetype(page) == MIGRATE_ISOLATE)
pfn += 1; pfn += 1;
else if (skip_hwpoisoned_pages && PageHWPoison(page)) {
/*
* The HWPoisoned page may be not in buddy
* system, and page_count() is not 0.
*/
pfn++;
continue;
}
else else
break; break;
} }
...@@ -228,7 +239,8 @@ __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn) ...@@ -228,7 +239,8 @@ __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn)
return 1; return 1;
} }
int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn) int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
bool skip_hwpoisoned_pages)
{ {
unsigned long pfn, flags; unsigned long pfn, flags;
struct page *page; struct page *page;
...@@ -251,7 +263,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn) ...@@ -251,7 +263,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn)
/* Check all pages are free or Marked as ISOLATED */ /* Check all pages are free or Marked as ISOLATED */
zone = page_zone(page); zone = page_zone(page);
spin_lock_irqsave(&zone->lock, flags); spin_lock_irqsave(&zone->lock, flags);
ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn); ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn,
skip_hwpoisoned_pages);
spin_unlock_irqrestore(&zone->lock, flags); spin_unlock_irqrestore(&zone->lock, flags);
return ret ? 0 : -EBUSY; return ret ? 0 : -EBUSY;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment