aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2013-07-03 18:02:37 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-20 14:06:12 -0500
commitb0d4c0f8122abd6069312ccc20089d5c1c19c772 (patch)
tree9f9b9854d4c4afb2fafa5fc6ee17fc39c15550fc /mm
parent1fc42b84b45d4a58641821c3c0b601565523ad7d (diff)
mm/memory-failure.c: fix memory leak in successful soft offlining
commit f15bdfa802bfa5eb6b4b5a241b97ec9fa1204a35 upstream. After a successful page migration by soft offlining, the source page is not properly freed and it's never reusable even if we unpoison it afterward. This is caused by the race between freeing page and setting PG_hwpoison. In successful soft offlining, the source page is put (and the refcount becomes 0) by putback_lru_page() in unmap_and_move(), where it's linked to pagevec and actual freeing back to buddy is delayed. So if PG_hwpoison is set for the page before freeing, the freeing does not functions as expected (in such case freeing aborts in free_pages_prepare() check.) This patch tries to make sure to free the source page before setting PG_hwpoison on it. To avoid reallocating, the page keeps MIGRATE_ISOLATE until after setting PG_hwpoison. This patch also removes obsolete comments about "keeping elevated refcount" because what they say is not true. Unlike memory_failure(), soft_offline_page() uses no special page isolation code, and the soft-offlined pages have no elevated. Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.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> Cc: Xishi Qiu <qiuxishi@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/memory-failure.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 3b4120e38d48..f2a591d87d00 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1421,7 +1421,8 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
1421 1421
1422 /* 1422 /*
1423 * Isolate the page, so that it doesn't get reallocated if it 1423 * Isolate the page, so that it doesn't get reallocated if it
1424 * was free. 1424 * was free. This flag should be kept set until the source page
1425 * is freed and PG_hwpoison on it is set.
1425 */ 1426 */
1426 set_migratetype_isolate(p, true); 1427 set_migratetype_isolate(p, true);
1427 /* 1428 /*
@@ -1444,7 +1445,6 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
1444 /* Not a free page */ 1445 /* Not a free page */
1445 ret = 1; 1446 ret = 1;
1446 } 1447 }
1447 unset_migratetype_isolate(p, MIGRATE_MOVABLE);
1448 unlock_memory_hotplug(); 1448 unlock_memory_hotplug();
1449 return ret; 1449 return ret;
1450} 1450}
@@ -1511,7 +1511,6 @@ static int soft_offline_huge_page(struct page *page, int flags)
1511 atomic_long_inc(&num_poisoned_pages); 1511 atomic_long_inc(&num_poisoned_pages);
1512 } 1512 }
1513 } 1513 }
1514 /* keep elevated page count for bad page */
1515 return ret; 1514 return ret;
1516} 1515}
1517 1516
@@ -1576,7 +1575,7 @@ int soft_offline_page(struct page *page, int flags)
1576 atomic_long_inc(&num_poisoned_pages); 1575 atomic_long_inc(&num_poisoned_pages);
1577 } 1576 }
1578 } 1577 }
1579 /* keep elevated page count for bad page */ 1578 unset_migratetype_isolate(page, MIGRATE_MOVABLE);
1580 return ret; 1579 return ret;
1581} 1580}
1582 1581
@@ -1642,7 +1641,22 @@ static int __soft_offline_page(struct page *page, int flags)
1642 if (ret > 0) 1641 if (ret > 0)
1643 ret = -EIO; 1642 ret = -EIO;
1644 } else { 1643 } else {
1644 /*
1645 * After page migration succeeds, the source page can
1646 * be trapped in pagevec and actual freeing is delayed.
1647 * Freeing code works differently based on PG_hwpoison,
1648 * so there's a race. We need to make sure that the
1649 * source page should be freed back to buddy before
1650 * setting PG_hwpoison.
1651 */
1652 if (!is_free_buddy_page(page))
1653 lru_add_drain_all();
1654 if (!is_free_buddy_page(page))
1655 drain_all_pages();
1645 SetPageHWPoison(page); 1656 SetPageHWPoison(page);
1657 if (!is_free_buddy_page(page))
1658 pr_info("soft offline: %#lx: page leaked\n",
1659 pfn);
1646 atomic_long_inc(&num_poisoned_pages); 1660 atomic_long_inc(&num_poisoned_pages);
1647 } 1661 }
1648 } else { 1662 } else {