mm: kill validate_anon_vma to avoid mapcount BUG
[sfrench/cifs-2.6.git] / mm / rmap.c
index b82146e6dfc9d0e12f15c7cf292893ab6ede43f3..61e492597a0ba4be07634c60b427031743f533d4 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
 
 struct kmem_cache *anon_vma_cachep;
 
-static inline void validate_anon_vma(struct vm_area_struct *find_vma)
-{
-#ifdef CONFIG_DEBUG_VM
-       struct anon_vma *anon_vma = find_vma->anon_vma;
-       struct vm_area_struct *vma;
-       unsigned int mapcount = 0;
-       int found = 0;
-
-       list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
-               mapcount++;
-               BUG_ON(mapcount > 100000);
-               if (vma == find_vma)
-                       found = 1;
-       }
-       BUG_ON(!found);
-#endif
-}
-
 /* This must be called under the mmap_sem. */
 int anon_vma_prepare(struct vm_area_struct *vma)
 {
@@ -121,10 +103,8 @@ void __anon_vma_link(struct vm_area_struct *vma)
 {
        struct anon_vma *anon_vma = vma->anon_vma;
 
-       if (anon_vma) {
+       if (anon_vma)
                list_add_tail(&vma->anon_vma_node, &anon_vma->head);
-               validate_anon_vma(vma);
-       }
 }
 
 void anon_vma_link(struct vm_area_struct *vma)
@@ -134,7 +114,6 @@ void anon_vma_link(struct vm_area_struct *vma)
        if (anon_vma) {
                spin_lock(&anon_vma->lock);
                list_add_tail(&vma->anon_vma_node, &anon_vma->head);
-               validate_anon_vma(vma);
                spin_unlock(&anon_vma->lock);
        }
 }
@@ -148,7 +127,6 @@ void anon_vma_unlink(struct vm_area_struct *vma)
                return;
 
        spin_lock(&anon_vma->lock);
-       validate_anon_vma(vma);
        list_del(&vma->anon_vma_node);
 
        /* We must garbage collect the anon_vma if it's empty */
@@ -162,13 +140,10 @@ void anon_vma_unlink(struct vm_area_struct *vma)
 static void anon_vma_ctor(void *data, struct kmem_cache *cachep,
                          unsigned long flags)
 {
-       if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-                                               SLAB_CTOR_CONSTRUCTOR) {
-               struct anon_vma *anon_vma = data;
+       struct anon_vma *anon_vma = data;
 
-               spin_lock_init(&anon_vma->lock);
-               INIT_LIST_HEAD(&anon_vma->head);
-       }
+       spin_lock_init(&anon_vma->lock);
+       INIT_LIST_HEAD(&anon_vma->head);
 }
 
 void __init anon_vma_init(void)
@@ -498,12 +473,15 @@ int page_mkclean(struct page *page)
                struct address_space *mapping = page_mapping(page);
                if (mapping)
                        ret = page_mkclean_file(mapping, page);
-               if (page_test_and_clear_dirty(page))
+               if (page_test_dirty(page)) {
+                       page_clear_dirty(page);
                        ret = 1;
+               }
        }
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(page_mkclean);
 
 /**
  * page_set_anon_rmap - setup new anonymous rmap
@@ -529,20 +507,52 @@ static void __page_set_anon_rmap(struct page *page,
        __inc_zone_page_state(page, NR_ANON_PAGES);
 }
 
+/**
+ * page_set_anon_rmap - sanity check anonymous rmap addition
+ * @page:      the page to add the mapping to
+ * @vma:       the vm area in which the mapping is added
+ * @address:   the user virtual address mapped
+ */
+static void __page_check_anon_rmap(struct page *page,
+       struct vm_area_struct *vma, unsigned long address)
+{
+#ifdef CONFIG_DEBUG_VM
+       /*
+        * The page's anon-rmap details (mapping and index) are guaranteed to
+        * be set up correctly at this point.
+        *
+        * We have exclusion against page_add_anon_rmap because the caller
+        * always holds the page locked, except if called from page_dup_rmap,
+        * in which case the page is already known to be setup.
+        *
+        * We have exclusion against page_add_new_anon_rmap because those pages
+        * are initially only visible via the pagetables, and the pte is locked
+        * over the call to page_add_new_anon_rmap.
+        */
+       struct anon_vma *anon_vma = vma->anon_vma;
+       anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
+       BUG_ON(page->mapping != (struct address_space *)anon_vma);
+       BUG_ON(page->index != linear_page_index(vma, address));
+#endif
+}
+
 /**
  * page_add_anon_rmap - add pte mapping to an anonymous page
  * @page:      the page to add the mapping to
  * @vma:       the vm area in which the mapping is added
  * @address:   the user virtual address mapped
  *
- * The caller needs to hold the pte lock.
+ * The caller needs to hold the pte lock and the page must be locked.
  */
 void page_add_anon_rmap(struct page *page,
        struct vm_area_struct *vma, unsigned long address)
 {
+       VM_BUG_ON(!PageLocked(page));
+       VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end);
        if (atomic_inc_and_test(&page->_mapcount))
                __page_set_anon_rmap(page, vma, address);
-       /* else checking page index and mapping is racy */
+       else
+               __page_check_anon_rmap(page, vma, address);
 }
 
 /*
@@ -553,10 +563,12 @@ void page_add_anon_rmap(struct page *page,
  *
  * Same as page_add_anon_rmap but must only be called on *new* pages.
  * This means the inc-and-test can be bypassed.
+ * Page does not have to be locked.
  */
 void page_add_new_anon_rmap(struct page *page,
        struct vm_area_struct *vma, unsigned long address)
 {
+       BUG_ON(address < vma->vm_start || address >= vma->vm_end);
        atomic_set(&page->_mapcount, 0); /* elevate count by 1 (starts at -1) */
        __page_set_anon_rmap(page, vma, address);
 }
@@ -573,6 +585,26 @@ void page_add_file_rmap(struct page *page)
                __inc_zone_page_state(page, NR_FILE_MAPPED);
 }
 
+#ifdef CONFIG_DEBUG_VM
+/**
+ * page_dup_rmap - duplicate pte mapping to a page
+ * @page:      the page to add the mapping to
+ *
+ * For copy_page_range only: minimal extract from page_add_file_rmap /
+ * page_add_anon_rmap, avoiding unnecessary tests (already checked) so it's
+ * quicker.
+ *
+ * The caller needs to hold the pte lock.
+ */
+void page_dup_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address)
+{
+       BUG_ON(page_mapcount(page) == 0);
+       if (PageAnon(page))
+               __page_check_anon_rmap(page, vma, address);
+       atomic_inc(&page->_mapcount);
+}
+#endif
+
 /**
  * page_remove_rmap - take down pte mapping from a page
  * @page: page to remove mapping from
@@ -605,8 +637,10 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma)
                 * Leaving it set also helps swapoff to reinstate ptes
                 * faster for those pages still in swapcache.
                 */
-               if (page_test_and_clear_dirty(page))
+               if (page_test_dirty(page)) {
+                       page_clear_dirty(page);
                        set_page_dirty(page);
+               }
                __dec_zone_page_state(page,
                                PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
        }