Merge branch 'x86/mce' into x86/ras
[cascardo/linux.git] / mm / memory-failure.c
index 0d6717e..55d7c80 100644 (file)
@@ -1413,7 +1413,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);
        /*
@@ -1436,7 +1437,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;
 }
@@ -1497,7 +1497,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;
 }
 
@@ -1562,7 +1561,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;
 }
 
@@ -1628,7 +1627,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 {