Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / mm / migrate.c
index 352de55..b0406d7 100644 (file)
@@ -564,7 +564,7 @@ static int fallback_migrate_page(struct address_space *mapping,
  *  == 0 - success
  */
 static int move_to_new_page(struct page *newpage, struct page *page,
-                                               int remap_swapcache)
+                                       int remap_swapcache, bool sync)
 {
        struct address_space *mapping;
        int rc;
@@ -586,18 +586,28 @@ static int move_to_new_page(struct page *newpage, struct page *page,
        mapping = page_mapping(page);
        if (!mapping)
                rc = migrate_page(mapping, newpage, page);
-       else if (mapping->a_ops->migratepage)
+       else {
                /*
-                * Most pages have a mapping and most filesystems
-                * should provide a migration function. Anonymous
-                * pages are part of swap space which also has its
-                * own migration function. This is the most common
-                * path for page migration.
+                * Do not writeback pages if !sync and migratepage is
+                * not pointing to migrate_page() which is nonblocking
+                * (swapcache/tmpfs uses migratepage = migrate_page).
                 */
-               rc = mapping->a_ops->migratepage(mapping,
-                                               newpage, page);
-       else
-               rc = fallback_migrate_page(mapping, newpage, page);
+               if (PageDirty(page) && !sync &&
+                   mapping->a_ops->migratepage != migrate_page)
+                       rc = -EBUSY;
+               else if (mapping->a_ops->migratepage)
+                       /*
+                        * Most pages have a mapping and most filesystems
+                        * should provide a migration function. Anonymous
+                        * pages are part of swap space which also has its
+                        * own migration function. This is the most common
+                        * path for page migration.
+                        */
+                       rc = mapping->a_ops->migratepage(mapping,
+                                                       newpage, page);
+               else
+                       rc = fallback_migrate_page(mapping, newpage, page);
+       }
 
        if (rc) {
                newpage->mapping = NULL;
@@ -623,7 +633,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
        struct page *newpage = get_new_page(page, private, &result);
        int remap_swapcache = 1;
        int charge = 0;
-       struct mem_cgroup *mem = NULL;
+       struct mem_cgroup *mem;
        struct anon_vma *anon_vma = NULL;
 
        if (!newpage)
@@ -641,7 +651,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
        rc = -EAGAIN;
 
        if (!trylock_page(page)) {
-               if (!force)
+               if (!force || !sync)
                        goto move_newpage;
 
                /*
@@ -678,7 +688,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
        }
 
        /* charge against new page */
-       charge = mem_cgroup_prepare_migration(page, newpage, &mem);
+       charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL);
        if (charge == -ENOMEM) {
                rc = -ENOMEM;
                goto unlock;
@@ -686,7 +696,15 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
        BUG_ON(charge);
 
        if (PageWriteback(page)) {
-               if (!force || !sync)
+               /*
+                * For !sync, there is no point retrying as the retry loop
+                * is expected to be too short for PageWriteback to be cleared
+                */
+               if (!sync) {
+                       rc = -EBUSY;
+                       goto uncharge;
+               }
+               if (!force)
                        goto uncharge;
                wait_on_page_writeback(page);
        }
@@ -757,14 +775,14 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
 
 skip_unmap:
        if (!page_mapped(page))
-               rc = move_to_new_page(newpage, page, remap_swapcache);
+               rc = move_to_new_page(newpage, page, remap_swapcache, sync);
 
        if (rc && remap_swapcache)
                remove_migration_ptes(page, page);
 
        /* Drop an anon_vma reference if we took one */
        if (anon_vma)
-               drop_anon_vma(anon_vma);
+               put_anon_vma(anon_vma);
 
 uncharge:
        if (!charge)
@@ -850,13 +868,13 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
        try_to_unmap(hpage, TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
 
        if (!page_mapped(hpage))
-               rc = move_to_new_page(new_hpage, hpage, 1);
+               rc = move_to_new_page(new_hpage, hpage, 1, sync);
 
        if (rc)
                remove_migration_ptes(hpage, hpage);
 
        if (anon_vma)
-               drop_anon_vma(anon_vma);
+               put_anon_vma(anon_vma);
 out:
        unlock_page(hpage);