aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2013-07-03 18:02:37 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-03 19:07:31 -0400
commitf15bdfa802bfa5eb6b4b5a241b97ec9fa1204a35 (patch)
treee490deddf30e0c73a663457c54d8961750ec72ee /mm
parent9bde916bc73255dcee3d8aded990443675daa707 (diff)
mm/memory-failure.c: fix memory leak in successful soft offlining
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>
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 ceb0c7f1932f..2c13aa7a0164 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1410,7 +1410,8 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
1410 1410
1411 /* 1411 /*
1412 * Isolate the page, so that it doesn't get reallocated if it 1412 * Isolate the page, so that it doesn't get reallocated if it
1413 * was free. 1413 * was free. This flag should be kept set until the source page
1414 * is freed and PG_hwpoison on it is set.
1414 */ 1415 */
1415 set_migratetype_isolate(p, true); 1416 set_migratetype_isolate(p, true);
1416 /* 1417 /*
@@ -1433,7 +1434,6 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
1433 /* Not a free page */ 1434 /* Not a free page */
1434 ret = 1; 1435 ret = 1;
1435 } 1436 }
1436 unset_migratetype_isolate(p, MIGRATE_MOVABLE);
1437 unlock_memory_hotplug(); 1437 unlock_memory_hotplug();
1438 return ret; 1438 return ret;
1439} 1439}
@@ -1494,7 +1494,6 @@ static int soft_offline_huge_page(struct page *page, int flags)
1494 atomic_long_add(1 << compound_trans_order(hpage), 1494 atomic_long_add(1 << compound_trans_order(hpage),
1495 &num_poisoned_pages); 1495 &num_poisoned_pages);
1496 } 1496 }
1497 /* keep elevated page count for bad page */
1498 return ret; 1497 return ret;
1499} 1498}
1500 1499
@@ -1559,7 +1558,7 @@ int soft_offline_page(struct page *page, int flags)
1559 atomic_long_inc(&num_poisoned_pages); 1558 atomic_long_inc(&num_poisoned_pages);
1560 } 1559 }
1561 } 1560 }
1562 /* keep elevated page count for bad page */ 1561 unset_migratetype_isolate(page, MIGRATE_MOVABLE);
1563 return ret; 1562 return ret;
1564} 1563}
1565 1564
@@ -1625,7 +1624,22 @@ static int __soft_offline_page(struct page *page, int flags)
1625 if (ret > 0) 1624 if (ret > 0)
1626 ret = -EIO; 1625 ret = -EIO;
1627 } else { 1626 } else {
1627 /*
1628 * After page migration succeeds, the source page can
1629 * be trapped in pagevec and actual freeing is delayed.
1630 * Freeing code works differently based on PG_hwpoison,
1631 * so there's a race. We need to make sure that the
1632 * source page should be freed back to buddy before
1633 * setting PG_hwpoison.
1634 */
1635 if (!is_free_buddy_page(page))
1636 lru_add_drain_all();
1637 if (!is_free_buddy_page(page))
1638 drain_all_pages();
1628 SetPageHWPoison(page); 1639 SetPageHWPoison(page);
1640 if (!is_free_buddy_page(page))
1641 pr_info("soft offline: %#lx: page leaked\n",
1642 pfn);
1629 atomic_long_inc(&num_poisoned_pages); 1643 atomic_long_inc(&num_poisoned_pages);
1630 } 1644 }
1631 } else { 1645 } else {