|
@@ -1410,7 +1410,8 @@ 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
|
|
|
- * was free.
|
|
|
+ * was free. This flag should be kept set until the source page
|
|
|
+ * is freed and PG_hwpoison on it is set.
|
|
|
*/
|
|
|
set_migratetype_isolate(p, true);
|
|
|
/*
|
|
@@ -1433,7 +1434,6 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
|
|
|
/* Not a free page */
|
|
|
ret = 1;
|
|
|
}
|
|
|
- unset_migratetype_isolate(p, MIGRATE_MOVABLE);
|
|
|
unlock_memory_hotplug();
|
|
|
return ret;
|
|
|
}
|
|
@@ -1494,7 +1494,6 @@ static int soft_offline_huge_page(struct page *page, int flags)
|
|
|
atomic_long_add(1 << compound_trans_order(hpage),
|
|
|
&num_poisoned_pages);
|
|
|
}
|
|
|
- /* keep elevated page count for bad page */
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1559,7 +1558,7 @@ int soft_offline_page(struct page *page, int flags)
|
|
|
atomic_long_inc(&num_poisoned_pages);
|
|
|
}
|
|
|
}
|
|
|
- /* keep elevated page count for bad page */
|
|
|
+ unset_migratetype_isolate(page, MIGRATE_MOVABLE);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1625,7 +1624,22 @@ static int __soft_offline_page(struct page *page, int flags)
|
|
|
if (ret > 0)
|
|
|
ret = -EIO;
|
|
|
} else {
|
|
|
+ /*
|
|
|
+ * After page migration succeeds, the source page can
|
|
|
+ * be trapped in pagevec and actual freeing is delayed.
|
|
|
+ * Freeing code works differently based on PG_hwpoison,
|
|
|
+ * so there's a race. We need to make sure that the
|
|
|
+ * source page should be freed back to buddy before
|
|
|
+ * setting PG_hwpoison.
|
|
|
+ */
|
|
|
+ if (!is_free_buddy_page(page))
|
|
|
+ lru_add_drain_all();
|
|
|
+ if (!is_free_buddy_page(page))
|
|
|
+ drain_all_pages();
|
|
|
SetPageHWPoison(page);
|
|
|
+ if (!is_free_buddy_page(page))
|
|
|
+ pr_info("soft offline: %#lx: page leaked\n",
|
|
|
+ pfn);
|
|
|
atomic_long_inc(&num_poisoned_pages);
|
|
|
}
|
|
|
} else {
|