diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memory-failure.c | 22 |
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 { |