--- /dev/null
+From f15bdfa802bfa5eb6b4b5a241b97ec9fa1204a35 Mon Sep 17 00:00:00 2001
+From: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
+Date: Wed, 3 Jul 2013 15:02:37 -0700
+Subject: mm/memory-failure.c: fix memory leak in successful soft offlining
+
+From: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
+
+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>
+
+---
+ mm/memory-failure.c | 22 ++++++++++++++++++----
+ 1 file changed, 18 insertions(+), 4 deletions(-)
+
+--- a/mm/memory-failure.c
++++ b/mm/memory-failure.c
+@@ -1421,7 +1421,8 @@ static int __get_any_page(struct page *p
+
+ /*
+ * 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);
+ /*
+@@ -1444,7 +1445,6 @@ static int __get_any_page(struct page *p
+ /* Not a free page */
+ ret = 1;
+ }
+- unset_migratetype_isolate(p, MIGRATE_MOVABLE);
+ unlock_memory_hotplug();
+ return ret;
+ }
+@@ -1511,7 +1511,6 @@ static int soft_offline_huge_page(struct
+ atomic_long_inc(&num_poisoned_pages);
+ }
+ }
+- /* keep elevated page count for bad page */
+ return ret;
+ }
+
+@@ -1576,7 +1575,7 @@ int soft_offline_page(struct page *page,
+ atomic_long_inc(&num_poisoned_pages);
+ }
+ }
+- /* keep elevated page count for bad page */
++ unset_migratetype_isolate(page, MIGRATE_MOVABLE);
+ return ret;
+ }
+
+@@ -1642,7 +1641,22 @@ static int __soft_offline_page(struct pa
+ 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 {