hugetlbfs: use i_mmap_rwsem for more pmd sharing synchronization
[linux-2.6-microblaze.git] / mm / rmap.c
index 85b7f94..21a26cf 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -25,6 +25,7 @@
  *     page->flags PG_locked (lock_page)
  *       hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share)
  *         mapping->i_mmap_rwsem
+ *           hugetlb_fault_mutex (hugetlbfs specific page fault mutex)
  *           anon_vma->rwsem
  *             mm->page_table_lock or pte_lock
  *               zone_lru_lock (in mark_page_accessed, isolate_lru_page)
@@ -889,15 +890,17 @@ static bool page_mkclean_one(struct page *page, struct vm_area_struct *vma,
                .address = address,
                .flags = PVMW_SYNC,
        };
-       unsigned long start = address, end;
+       struct mmu_notifier_range range;
        int *cleaned = arg;
 
        /*
         * We have to assume the worse case ie pmd for invalidation. Note that
         * the page can not be free from this function.
         */
-       end = min(vma->vm_end, start + (PAGE_SIZE << compound_order(page)));
-       mmu_notifier_invalidate_range_start(vma->vm_mm, start, end);
+       mmu_notifier_range_init(&range, vma->vm_mm, address,
+                               min(vma->vm_end, address +
+                                   (PAGE_SIZE << compound_order(page))));
+       mmu_notifier_invalidate_range_start(&range);
 
        while (page_vma_mapped_walk(&pvmw)) {
                unsigned long cstart;
@@ -949,7 +952,7 @@ static bool page_mkclean_one(struct page *page, struct vm_area_struct *vma,
                        (*cleaned)++;
        }
 
-       mmu_notifier_invalidate_range_end(vma->vm_mm, start, end);
+       mmu_notifier_invalidate_range_end(&range);
 
        return true;
 }
@@ -1017,7 +1020,7 @@ void page_move_anon_rmap(struct page *page, struct vm_area_struct *vma)
 
 /**
  * __page_set_anon_rmap - set up new anonymous rmap
- * @page:      Page to add to rmap     
+ * @page:      Page or Hugepage to add to rmap
  * @vma:       VM area to add page to.
  * @address:   User virtual address of the mapping     
  * @exclusive: the page is exclusively owned by the current process
@@ -1345,7 +1348,7 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
        pte_t pteval;
        struct page *subpage;
        bool ret = true;
-       unsigned long start = address, end;
+       struct mmu_notifier_range range;
        enum ttu_flags flags = (enum ttu_flags)arg;
 
        /* munlock has nothing to gain from examining un-locked vmas */
@@ -1369,15 +1372,21 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
         * Note that the page can not be free in this function as call of
         * try_to_unmap() must hold a reference on the page.
         */
-       end = min(vma->vm_end, start + (PAGE_SIZE << compound_order(page)));
+       mmu_notifier_range_init(&range, vma->vm_mm, vma->vm_start,
+                               min(vma->vm_end, vma->vm_start +
+                                   (PAGE_SIZE << compound_order(page))));
        if (PageHuge(page)) {
                /*
                 * If sharing is possible, start and end will be adjusted
                 * accordingly.
+                *
+                * If called for a huge page, caller must hold i_mmap_rwsem
+                * in write mode as it is possible to call huge_pmd_unshare.
                 */
-               adjust_range_if_pmd_sharing_possible(vma, &start, &end);
+               adjust_range_if_pmd_sharing_possible(vma, &range.start,
+                                                    &range.end);
        }
-       mmu_notifier_invalidate_range_start(vma->vm_mm, start, end);
+       mmu_notifier_invalidate_range_start(&range);
 
        while (page_vma_mapped_walk(&pvmw)) {
 #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION
@@ -1428,9 +1437,10 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                                 * we must flush them all.  start/end were
                                 * already adjusted above to cover this range.
                                 */
-                               flush_cache_range(vma, start, end);
-                               flush_tlb_range(vma, start, end);
-                               mmu_notifier_invalidate_range(mm, start, end);
+                               flush_cache_range(vma, range.start, range.end);
+                               flush_tlb_range(vma, range.start, range.end);
+                               mmu_notifier_invalidate_range(mm, range.start,
+                                                             range.end);
 
                                /*
                                 * The ref count of the PMD page was dropped
@@ -1650,7 +1660,7 @@ discard:
                put_page(page);
        }
 
-       mmu_notifier_invalidate_range_end(vma->vm_mm, start, end);
+       mmu_notifier_invalidate_range_end(&range);
 
        return ret;
 }
@@ -1910,27 +1920,10 @@ void rmap_walk_locked(struct page *page, struct rmap_walk_control *rwc)
 
 #ifdef CONFIG_HUGETLB_PAGE
 /*
- * The following three functions are for anonymous (private mapped) hugepages.
+ * The following two functions are for anonymous (private mapped) hugepages.
  * Unlike common anonymous pages, anonymous hugepages have no accounting code
  * and no lru code, because we handle hugepages differently from common pages.
  */
-static void __hugepage_set_anon_rmap(struct page *page,
-       struct vm_area_struct *vma, unsigned long address, int exclusive)
-{
-       struct anon_vma *anon_vma = vma->anon_vma;
-
-       BUG_ON(!anon_vma);
-
-       if (PageAnon(page))
-               return;
-       if (!exclusive)
-               anon_vma = anon_vma->root;
-
-       anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
-       page->mapping = (struct address_space *) anon_vma;
-       page->index = linear_page_index(vma, address);
-}
-
 void hugepage_add_anon_rmap(struct page *page,
                            struct vm_area_struct *vma, unsigned long address)
 {
@@ -1942,7 +1935,7 @@ void hugepage_add_anon_rmap(struct page *page,
        /* address might be in next vma when migration races vma_adjust */
        first = atomic_inc_and_test(compound_mapcount_ptr(page));
        if (first)
-               __hugepage_set_anon_rmap(page, vma, address, 0);
+               __page_set_anon_rmap(page, vma, address, 0);
 }
 
 void hugepage_add_new_anon_rmap(struct page *page,
@@ -1950,6 +1943,6 @@ void hugepage_add_new_anon_rmap(struct page *page,
 {
        BUG_ON(address < vma->vm_start || address >= vma->vm_end);
        atomic_set(compound_mapcount_ptr(page), 0);
-       __hugepage_set_anon_rmap(page, vma, address, 1);
+       __page_set_anon_rmap(page, vma, address, 1);
 }
 #endif /* CONFIG_HUGETLB_PAGE */