Merge branch 'drm-fixes-3.11' of git://people.freedesktop.org/~agd5f/linux
[pandora-kernel.git] / mm / memory-failure.c
index ceb0c7f..2c13aa7 100644 (file)
@@ -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 {