OSDN Git Service

Merge tag 'kconfig-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[uclinux-h8/linux.git] / mm / migrate.c
index d4fd680..ac6f493 100644 (file)
@@ -100,7 +100,7 @@ int isolate_movable_page(struct page *page, isolate_mode_t mode)
        /*
         * Check PageMovable before holding a PG_lock because page's owner
         * assumes anybody doesn't touch PG_lock of newly allocated page
-        * so unconditionally grapping the lock ruins page's owner side.
+        * so unconditionally grabbing the lock ruins page's owner side.
         */
        if (unlikely(!__PageMovable(page)))
                goto out_putpage;
@@ -374,7 +374,7 @@ unlock:
 }
 #endif
 
-static int expected_page_refs(struct page *page)
+static int expected_page_refs(struct address_space *mapping, struct page *page)
 {
        int expected_count = 1;
 
@@ -384,7 +384,7 @@ static int expected_page_refs(struct page *page)
         */
        expected_count += is_device_private_page(page);
        expected_count += is_device_public_page(page);
-       if (page_mapping(page))
+       if (mapping)
                expected_count += hpage_nr_pages(page) + page_has_private(page);
 
        return expected_count;
@@ -405,7 +405,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
        XA_STATE(xas, &mapping->i_pages, page_index(page));
        struct zone *oldzone, *newzone;
        int dirty;
-       int expected_count = expected_page_refs(page) + extra_count;
+       int expected_count = expected_page_refs(mapping, page) + extra_count;
 
        if (!mapping) {
                /* Anonymous page without mapping */
@@ -750,7 +750,7 @@ static int __buffer_migrate_page(struct address_space *mapping,
                return migrate_page(mapping, newpage, page, mode);
 
        /* Check whether page does not have extra refs before we do more work */
-       expected_count = expected_page_refs(page);
+       expected_count = expected_page_refs(mapping, page);
        if (page_count(page) != expected_count)
                return -EAGAIN;
 
@@ -911,7 +911,7 @@ static int fallback_migrate_page(struct address_space *mapping,
         */
        if (page_has_private(page) &&
            !try_to_release_page(page, GFP_KERNEL))
-               return -EAGAIN;
+               return mode == MIGRATE_SYNC ? -EAGAIN : -EBUSY;
 
        return migrate_page(mapping, newpage, page, mode);
 }
@@ -1287,7 +1287,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
        struct anon_vma *anon_vma = NULL;
 
        /*
-        * Movability of hugepages depends on architectures and hugepage size.
+        * Migratability of hugepages depends on architectures and their size.
         * This check is necessary because some callers of hugepage migration
         * like soft offline and memory hotremove don't walk through page
         * tables or check whether the hugepage is pmd-based or not before
@@ -1315,6 +1315,16 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
                lock_page(hpage);
        }
 
+       /*
+        * Check for pages which are in the process of being freed.  Without
+        * page_mapping() set, hugetlbfs specific move page routine will not
+        * be called and we could leak usage counts for subpools.
+        */
+       if (page_private(hpage) && !page_mapping(hpage)) {
+               rc = -EBUSY;
+               goto out_unlock;
+       }
+
        if (PageAnon(hpage))
                anon_vma = page_get_anon_vma(hpage);
 
@@ -1345,6 +1355,7 @@ put_anon:
                put_new_page = NULL;
        }
 
+out_unlock:
        unlock_page(hpage);
 out:
        if (rc != -EAGAIN)