]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - mm/rmap.c
thp: add tlb_remove_pmd_tlb_entry
[linux-2.6.git] / mm / rmap.c
index f286697c61dcdf53fe7c82648089e7b08e58952a..c8454e06b6c84b424de3952c567fbbcc8264ff75 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -21,7 +21,6 @@
  * Lock ordering in mm:
  *
  * inode->i_mutex      (while writing or truncating, not reading or faulting)
- *   inode->i_alloc_sem (vmtruncate_range)
  *   mm->mmap_sem
  *     page->flags PG_locked (lock_page)
  *       mapping->i_mmap_mutex
  *               mmlist_lock (in mmput, drain_mmlist and others)
  *               mapping->private_lock (in __set_page_dirty_buffers)
  *               inode->i_lock (in set_page_dirty's __mark_inode_dirty)
- *               inode_wb_list_lock (in set_page_dirty's __mark_inode_dirty)
+ *               bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty)
  *                 sb_lock (within inode_lock in fs/fs-writeback.c)
  *                 mapping->tree_lock (widely used, in set_page_dirty,
  *                           in arch-dependent flush_dcache_mmap_lock,
- *                           within inode_wb_list_lock in __sync_single_inode)
+ *                           within bdi.wb->list_lock in __sync_single_inode)
  *
- * (code doesn't rely on that order so it could be switched around)
- * ->tasklist_lock
- *   anon_vma->mutex      (memory_failure, collect_procs_anon)
+ * anon_vma->mutex,mapping->i_mutex      (memory_failure, collect_procs_anon)
+ *   ->tasklist_lock
  *     pte map lock
  */
 
@@ -53,7 +51,7 @@
 #include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/rcupdate.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/memcontrol.h>
 #include <linux/mmu_notifier.h>
 #include <linux/migrate.h>
@@ -112,9 +110,9 @@ static inline void anon_vma_free(struct anon_vma *anon_vma)
        kmem_cache_free(anon_vma_cachep, anon_vma);
 }
 
-static inline struct anon_vma_chain *anon_vma_chain_alloc(void)
+static inline struct anon_vma_chain *anon_vma_chain_alloc(gfp_t gfp)
 {
-       return kmem_cache_alloc(anon_vma_chain_cachep, GFP_KERNEL);
+       return kmem_cache_alloc(anon_vma_chain_cachep, gfp);
 }
 
 static void anon_vma_chain_free(struct anon_vma_chain *anon_vma_chain)
@@ -159,7 +157,7 @@ int anon_vma_prepare(struct vm_area_struct *vma)
                struct mm_struct *mm = vma->vm_mm;
                struct anon_vma *allocated;
 
-               avc = anon_vma_chain_alloc();
+               avc = anon_vma_chain_alloc(GFP_KERNEL);
                if (!avc)
                        goto out_enomem;
 
@@ -253,9 +251,14 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
        list_for_each_entry_reverse(pavc, &src->anon_vma_chain, same_vma) {
                struct anon_vma *anon_vma;
 
-               avc = anon_vma_chain_alloc();
-               if (!avc)
-                       goto enomem_failure;
+               avc = anon_vma_chain_alloc(GFP_NOWAIT | __GFP_NOWARN);
+               if (unlikely(!avc)) {
+                       unlock_anon_vma_root(root);
+                       root = NULL;
+                       avc = anon_vma_chain_alloc(GFP_KERNEL);
+                       if (!avc)
+                               goto enomem_failure;
+               }
                anon_vma = pavc->anon_vma;
                root = lock_anon_vma_root(root, anon_vma);
                anon_vma_chain_link(dst, avc, anon_vma);
@@ -264,11 +267,55 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
        return 0;
 
  enomem_failure:
-       unlock_anon_vma_root(root);
        unlink_anon_vmas(dst);
        return -ENOMEM;
 }
 
+/*
+ * Some rmap walk that needs to find all ptes/hugepmds without false
+ * negatives (like migrate and split_huge_page) running concurrent
+ * with operations that copy or move pagetables (like mremap() and
+ * fork()) to be safe. They depend on the anon_vma "same_anon_vma"
+ * list to be in a certain order: the dst_vma must be placed after the
+ * src_vma in the list. This is always guaranteed by fork() but
+ * mremap() needs to call this function to enforce it in case the
+ * dst_vma isn't newly allocated and chained with the anon_vma_clone()
+ * function but just an extension of a pre-existing vma through
+ * vma_merge.
+ *
+ * NOTE: the same_anon_vma list can still be changed by other
+ * processes while mremap runs because mremap doesn't hold the
+ * anon_vma mutex to prevent modifications to the list while it
+ * runs. All we need to enforce is that the relative order of this
+ * process vmas isn't changing (we don't care about other vmas
+ * order). Each vma corresponds to an anon_vma_chain structure so
+ * there's no risk that other processes calling anon_vma_moveto_tail()
+ * and changing the same_anon_vma list under mremap() will screw with
+ * the relative order of this process vmas in the list, because we
+ * they can't alter the order of any vma that belongs to this
+ * process. And there can't be another anon_vma_moveto_tail() running
+ * concurrently with mremap() coming from this process because we hold
+ * the mmap_sem for the whole mremap(). fork() ordering dependency
+ * also shouldn't be affected because fork() only cares that the
+ * parent vmas are placed in the list before the child vmas and
+ * anon_vma_moveto_tail() won't reorder vmas from either the fork()
+ * parent or child.
+ */
+void anon_vma_moveto_tail(struct vm_area_struct *dst)
+{
+       struct anon_vma_chain *pavc;
+       struct anon_vma *root = NULL;
+
+       list_for_each_entry_reverse(pavc, &dst->anon_vma_chain, same_vma) {
+               struct anon_vma *anon_vma = pavc->anon_vma;
+               VM_BUG_ON(pavc->vma != dst);
+               root = lock_anon_vma_root(root, anon_vma);
+               list_del(&pavc->same_anon_vma);
+               list_add_tail(&pavc->same_anon_vma, &anon_vma->head);
+       }
+       unlock_anon_vma_root(root);
+}
+
 /*
  * Attach vma to its own anon_vma, as well as to the anon_vmas that
  * the corresponding VMA in the parent process is attached to.
@@ -294,7 +341,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
        anon_vma = anon_vma_alloc();
        if (!anon_vma)
                goto out_error;
-       avc = anon_vma_chain_alloc();
+       avc = anon_vma_chain_alloc(GFP_KERNEL);
        if (!avc)
                goto out_error_free_anon_vma;
 
@@ -324,36 +371,43 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
        return -ENOMEM;
 }
 
-static void anon_vma_unlink(struct anon_vma_chain *anon_vma_chain)
-{
-       struct anon_vma *anon_vma = anon_vma_chain->anon_vma;
-       int empty;
-
-       /* If anon_vma_fork fails, we can get an empty anon_vma_chain. */
-       if (!anon_vma)
-               return;
-
-       anon_vma_lock(anon_vma);
-       list_del(&anon_vma_chain->same_anon_vma);
-
-       /* We must garbage collect the anon_vma if it's empty */
-       empty = list_empty(&anon_vma->head);
-       anon_vma_unlock(anon_vma);
-
-       if (empty)
-               put_anon_vma(anon_vma);
-}
-
 void unlink_anon_vmas(struct vm_area_struct *vma)
 {
        struct anon_vma_chain *avc, *next;
+       struct anon_vma *root = NULL;
 
        /*
         * Unlink each anon_vma chained to the VMA.  This list is ordered
         * from newest to oldest, ensuring the root anon_vma gets freed last.
         */
        list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
-               anon_vma_unlink(avc);
+               struct anon_vma *anon_vma = avc->anon_vma;
+
+               root = lock_anon_vma_root(root, anon_vma);
+               list_del(&avc->same_anon_vma);
+
+               /*
+                * Leave empty anon_vmas on the list - we'll need
+                * to free them outside the lock.
+                */
+               if (list_empty(&anon_vma->head))
+                       continue;
+
+               list_del(&avc->same_vma);
+               anon_vma_chain_free(avc);
+       }
+       unlock_anon_vma_root(root);
+
+       /*
+        * Iterate the list once more, it now only contains empty and unlinked
+        * anon_vmas, destroy them. Could not do before due to __put_anon_vma()
+        * needing to acquire the anon_vma->root->mutex.
+        */
+       list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
+               struct anon_vma *anon_vma = avc->anon_vma;
+
+               put_anon_vma(anon_vma);
+
                list_del(&avc->same_vma);
                anon_vma_chain_free(avc);
        }
@@ -719,7 +773,7 @@ out:
 }
 
 static int page_referenced_anon(struct page *page,
-                               struct mem_cgroup *mem_cont,
+                               struct mem_cgroup *memcg,
                                unsigned long *vm_flags)
 {
        unsigned int mapcount;
@@ -742,7 +796,7 @@ static int page_referenced_anon(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
+               if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
                        continue;
                referenced += page_referenced_one(page, vma, address,
                                                  &mapcount, vm_flags);
@@ -757,7 +811,7 @@ static int page_referenced_anon(struct page *page,
 /**
  * page_referenced_file - referenced check for object-based rmap
  * @page: the page we're checking references on.
- * @mem_cont: target memory controller
+ * @memcg: target memory control group
  * @vm_flags: collect encountered vma->vm_flags who actually referenced the page
  *
  * For an object-based mapped page, find all the places it is mapped and
@@ -768,7 +822,7 @@ static int page_referenced_anon(struct page *page,
  * This function is only called from page_referenced for object-based pages.
  */
 static int page_referenced_file(struct page *page,
-                               struct mem_cgroup *mem_cont,
+                               struct mem_cgroup *memcg,
                                unsigned long *vm_flags)
 {
        unsigned int mapcount;
@@ -810,7 +864,7 @@ static int page_referenced_file(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
+               if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
                        continue;
                referenced += page_referenced_one(page, vma, address,
                                                  &mapcount, vm_flags);
@@ -826,7 +880,7 @@ static int page_referenced_file(struct page *page,
  * page_referenced - test if the page was referenced
  * @page: the page to test
  * @is_locked: caller holds lock on the page
- * @mem_cont: target memory controller
+ * @memcg: target memory cgroup
  * @vm_flags: collect encountered vma->vm_flags who actually referenced the page
  *
  * Quick test_and_clear_referenced for all mappings to a page,
@@ -834,7 +888,7 @@ static int page_referenced_file(struct page *page,
  */
 int page_referenced(struct page *page,
                    int is_locked,
-                   struct mem_cgroup *mem_cont,
+                   struct mem_cgroup *memcg,
                    unsigned long *vm_flags)
 {
        int referenced = 0;
@@ -850,21 +904,21 @@ int page_referenced(struct page *page,
                        }
                }
                if (unlikely(PageKsm(page)))
-                       referenced += page_referenced_ksm(page, mem_cont,
+                       referenced += page_referenced_ksm(page, memcg,
                                                                vm_flags);
                else if (PageAnon(page))
-                       referenced += page_referenced_anon(page, mem_cont,
+                       referenced += page_referenced_anon(page, memcg,
                                                                vm_flags);
                else if (page->mapping)
-                       referenced += page_referenced_file(page, mem_cont,
+                       referenced += page_referenced_file(page, memcg,
                                                                vm_flags);
                if (we_locked)
                        unlock_page(page);
+
+               if (page_test_and_clear_young(page_to_pfn(page)))
+                       referenced++;
        }
 out:
-       if (page_test_and_clear_young(page_to_pfn(page)))
-               referenced++;
-
        return referenced;
 }
 
@@ -1155,7 +1209,7 @@ void page_remove_rmap(struct page *page)
 
 /*
  * Subfunctions of try_to_unmap: try_to_unmap_one called
- * repeatedly from either try_to_unmap_anon or try_to_unmap_file.
+ * repeatedly from try_to_unmap_ksm, try_to_unmap_anon or try_to_unmap_file.
  */
 int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                     unsigned long address, enum ttu_flags flags)