hugetlbfs: use i_mmap_rwsem for more pmd sharing synchronization
[linux-2.6-microblaze.git] / mm / migrate.c
index b109287..ae50d70 100644 (file)
@@ -1282,6 +1282,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
        int page_was_mapped = 0;
        struct page *new_hpage;
        struct anon_vma *anon_vma = NULL;
+       struct address_space *mapping = NULL;
 
        /*
         * Migratability of hugepages depends on architectures and their size.
@@ -1329,18 +1330,36 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
                goto put_anon;
 
        if (page_mapped(hpage)) {
+               /*
+                * try_to_unmap could potentially call huge_pmd_unshare.
+                * Because of this, take semaphore in write mode here and
+                * set TTU_RMAP_LOCKED to let lower levels know we have
+                * taken the lock.
+                */
+               mapping = hugetlb_page_mapping_lock_write(hpage);
+               if (unlikely(!mapping))
+                       goto unlock_put_anon;
+
                try_to_unmap(hpage,
-                       TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
+                       TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS|
+                       TTU_RMAP_LOCKED);
                page_was_mapped = 1;
+               /*
+                * Leave mapping locked until after subsequent call to
+                * remove_migration_ptes()
+                */
        }
 
        if (!page_mapped(hpage))
                rc = move_to_new_page(new_hpage, hpage, mode);
 
-       if (page_was_mapped)
+       if (page_was_mapped) {
                remove_migration_ptes(hpage,
-                       rc == MIGRATEPAGE_SUCCESS ? new_hpage : hpage, false);
+                       rc == MIGRATEPAGE_SUCCESS ? new_hpage : hpage, true);
+               i_mmap_unlock_write(mapping);
+       }
 
+unlock_put_anon:
        unlock_page(new_hpage);
 
 put_anon: