fs/hugetlbfs/inode.c: fix bugs in hugetlb_vmtruncate_list()
[linux-2.6-microblaze.git] / mm / mmap.c
index 2ce04a6..84b1262 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
 #define arch_rebalance_pgtables(addr, len)             (addr)
 #endif
 
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+const int mmap_rnd_bits_min = CONFIG_ARCH_MMAP_RND_BITS_MIN;
+const int mmap_rnd_bits_max = CONFIG_ARCH_MMAP_RND_BITS_MAX;
+int mmap_rnd_bits __read_mostly = CONFIG_ARCH_MMAP_RND_BITS;
+#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
+const int mmap_rnd_compat_bits_min = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN;
+const int mmap_rnd_compat_bits_max = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX;
+int mmap_rnd_compat_bits __read_mostly = CONFIG_ARCH_MMAP_RND_COMPAT_BITS;
+#endif
+
+
 static void unmap_region(struct mm_struct *mm,
                struct vm_area_struct *vma, struct vm_area_struct *prev,
                unsigned long start, unsigned long end);
@@ -1208,24 +1220,6 @@ none:
        return NULL;
 }
 
-#ifdef CONFIG_PROC_FS
-void vm_stat_account(struct mm_struct *mm, unsigned long flags,
-                                               struct file *file, long pages)
-{
-       const unsigned long stack_flags
-               = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);
-
-       mm->total_vm += pages;
-
-       if (file) {
-               mm->shared_vm += pages;
-               if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
-                       mm->exec_vm += pages;
-       } else if (flags & stack_flags)
-               mm->stack_vm += pages;
-}
-#endif /* CONFIG_PROC_FS */
-
 /*
  * If a hint addr is less than mmap_min_addr change hint to be as
  * low as possible but still greater than mmap_min_addr
@@ -1544,19 +1538,17 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
        unsigned long charged = 0;
 
        /* Check against address space limit. */
-       if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
+       if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) {
                unsigned long nr_pages;
 
                /*
                 * MAP_FIXED may remove pages of mappings that intersects with
                 * requested mapping. Account for the pages it would unmap.
                 */
-               if (!(vm_flags & MAP_FIXED))
-                       return -ENOMEM;
-
                nr_pages = count_vma_pages_range(mm, addr, addr + len);
 
-               if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))
+               if (!may_expand_vm(mm, vm_flags,
+                                       (len >> PAGE_SHIFT) - nr_pages))
                        return -ENOMEM;
        }
 
@@ -1655,7 +1647,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
 out:
        perf_event_mmap(vma);
 
-       vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
+       vm_stat_account(mm, vm_flags, len >> PAGE_SHIFT);
        if (vm_flags & VM_LOCKED) {
                if (!((vm_flags & VM_SPECIAL) || is_vm_hugetlb_page(vma) ||
                                        vma == get_gate_vma(current->mm)))
@@ -2102,7 +2094,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
        unsigned long new_start, actual_size;
 
        /* address space limit tests */
-       if (!may_expand_vm(mm, grow))
+       if (!may_expand_vm(mm, vma->vm_flags, grow))
                return -ENOMEM;
 
        /* Stack limit test */
@@ -2199,8 +2191,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
                                spin_lock(&mm->page_table_lock);
                                if (vma->vm_flags & VM_LOCKED)
                                        mm->locked_vm += grow;
-                               vm_stat_account(mm, vma->vm_flags,
-                                               vma->vm_file, grow);
+                               vm_stat_account(mm, vma->vm_flags, grow);
                                anon_vma_interval_tree_pre_update_vma(vma);
                                vma->vm_end = address;
                                anon_vma_interval_tree_post_update_vma(vma);
@@ -2275,8 +2266,7 @@ int expand_downwards(struct vm_area_struct *vma,
                                spin_lock(&mm->page_table_lock);
                                if (vma->vm_flags & VM_LOCKED)
                                        mm->locked_vm += grow;
-                               vm_stat_account(mm, vma->vm_flags,
-                                               vma->vm_file, grow);
+                               vm_stat_account(mm, vma->vm_flags, grow);
                                anon_vma_interval_tree_pre_update_vma(vma);
                                vma->vm_start = address;
                                vma->vm_pgoff -= grow;
@@ -2390,7 +2380,7 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma)
 
                if (vma->vm_flags & VM_ACCOUNT)
                        nr_accounted += nrpages;
-               vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages);
+               vm_stat_account(mm, vma->vm_flags, -nrpages);
                vma = remove_vma(vma);
        } while (vma);
        vm_unacct_memory(nr_accounted);
@@ -2760,7 +2750,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
        }
 
        /* Check against address space limits *after* clearing old maps... */
-       if (!may_expand_vm(mm, len >> PAGE_SHIFT))
+       if (!may_expand_vm(mm, flags, len >> PAGE_SHIFT))
                return -ENOMEM;
 
        if (mm->map_count > sysctl_max_map_count)
@@ -2795,6 +2785,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
 out:
        perf_event_mmap(vma);
        mm->total_vm += len >> PAGE_SHIFT;
+       mm->data_vm += len >> PAGE_SHIFT;
        if (flags & VM_LOCKED)
                mm->locked_vm += (len >> PAGE_SHIFT);
        vma->vm_flags |= VM_SOFTDIRTY;
@@ -2986,16 +2977,28 @@ out:
  * Return true if the calling process may expand its vm space by the passed
  * number of pages
  */
-int may_expand_vm(struct mm_struct *mm, unsigned long npages)
+bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags, unsigned long npages)
 {
-       unsigned long cur = mm->total_vm;       /* pages */
-       unsigned long lim;
+       if (mm->total_vm + npages > rlimit(RLIMIT_AS) >> PAGE_SHIFT)
+               return false;
 
-       lim = rlimit(RLIMIT_AS) >> PAGE_SHIFT;
+       if ((flags & (VM_WRITE | VM_SHARED | (VM_STACK_FLAGS &
+                               (VM_GROWSUP | VM_GROWSDOWN)))) == VM_WRITE)
+               return mm->data_vm + npages <= rlimit(RLIMIT_DATA);
 
-       if (cur + npages > lim)
-               return 0;
-       return 1;
+       return true;
+}
+
+void vm_stat_account(struct mm_struct *mm, vm_flags_t flags, long npages)
+{
+       mm->total_vm += npages;
+
+       if ((flags & (VM_EXEC | VM_WRITE)) == VM_EXEC)
+               mm->exec_vm += npages;
+       else if (flags & (VM_STACK_FLAGS & (VM_GROWSUP | VM_GROWSDOWN)))
+               mm->stack_vm += npages;
+       else if ((flags & (VM_WRITE | VM_SHARED)) == VM_WRITE)
+               mm->data_vm += npages;
 }
 
 static int special_mapping_fault(struct vm_area_struct *vma,
@@ -3077,7 +3080,7 @@ static struct vm_area_struct *__install_special_mapping(
        if (ret)
                goto out;
 
-       mm->total_vm += len >> PAGE_SHIFT;
+       vm_stat_account(mm, vma->vm_flags, len >> PAGE_SHIFT);
 
        perf_event_mmap(vma);
 
@@ -3181,10 +3184,16 @@ static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping)
  * mapping->flags avoid to take the same lock twice, if more than one
  * vma in this mm is backed by the same anon_vma or address_space.
  *
- * We can take all the locks in random order because the VM code
- * taking i_mmap_rwsem or anon_vma->rwsem outside the mmap_sem never
- * takes more than one of them in a row. Secondly we're protected
- * against a concurrent mm_take_all_locks() by the mm_all_locks_mutex.
+ * We take locks in following order, accordingly to comment at beginning
+ * of mm/rmap.c:
+ *   - all hugetlbfs_i_mmap_rwsem_key locks (aka mapping->i_mmap_rwsem for
+ *     hugetlb mapping);
+ *   - all i_mmap_rwsem locks;
+ *   - all anon_vma->rwseml
+ *
+ * We can take all locks within these types randomly because the VM code
+ * doesn't nest them and we protected from parallel mm_take_all_locks() by
+ * mm_all_locks_mutex.
  *
  * mm_take_all_locks() and mm_drop_all_locks are expensive operations
  * that may have to take thousand of locks.
@@ -3203,7 +3212,16 @@ int mm_take_all_locks(struct mm_struct *mm)
        for (vma = mm->mmap; vma; vma = vma->vm_next) {
                if (signal_pending(current))
                        goto out_unlock;
-               if (vma->vm_file && vma->vm_file->f_mapping)
+               if (vma->vm_file && vma->vm_file->f_mapping &&
+                               is_vm_hugetlb_page(vma))
+                       vm_lock_mapping(mm, vma->vm_file->f_mapping);
+       }
+
+       for (vma = mm->mmap; vma; vma = vma->vm_next) {
+               if (signal_pending(current))
+                       goto out_unlock;
+               if (vma->vm_file && vma->vm_file->f_mapping &&
+                               !is_vm_hugetlb_page(vma))
                        vm_lock_mapping(mm, vma->vm_file->f_mapping);
        }