mm: merge populate and nopage into fault (fixes nonlinear)
[safe/jmp/linux-2.6] / mm / mmap.c
index 5ecc2cf..724f342 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -13,6 +13,7 @@
 #include <linux/pagemap.h>
 #include <linux/swap.h>
 #include <linux/syscalls.h>
+#include <linux/capability.h>
 #include <linux/init.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <asm/tlb.h>
+#include <asm/mmu_context.h>
+
+#ifndef arch_mmap_check
+#define arch_mmap_check(addr, len, flags)      (0)
+#endif
 
 static void unmap_region(struct mm_struct *mm,
                struct vm_area_struct *vma, struct vm_area_struct *prev,
@@ -59,6 +65,13 @@ pgprot_t protection_map[16] = {
        __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
 };
 
+pgprot_t vm_get_page_prot(unsigned long vm_flags)
+{
+       return protection_map[vm_flags &
+                               (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
+}
+EXPORT_SYMBOL(vm_get_page_prot);
+
 int sysctl_overcommit_memory = OVERCOMMIT_GUESS;  /* heuristic overcommit */
 int sysctl_overcommit_ratio = 50;      /* default is 50% */
 int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
@@ -95,7 +108,7 @@ int __vm_enough_memory(long pages, int cap_sys_admin)
        if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
                unsigned long n;
 
-               free = get_page_cache_size();
+               free = global_page_state(NR_FILE_PAGES);
                free += nr_swap_pages;
 
                /*
@@ -104,7 +117,7 @@ int __vm_enough_memory(long pages, int cap_sys_admin)
                 * which are reclaimable, under pressure.  The dentry
                 * cache and most inode caches should fall into this
                 */
-               free += atomic_read(&slab_reclaim_pages);
+               free += global_page_state(NR_SLAB_RECLAIMABLE);
 
                /*
                 * Leave the last 3% for root
@@ -120,14 +133,26 @@ int __vm_enough_memory(long pages, int cap_sys_admin)
                 * only call if we're about to fail.
                 */
                n = nr_free_pages();
+
+               /*
+                * Leave reserved pages. The pages are not for anonymous pages.
+                */
+               if (n <= totalreserve_pages)
+                       goto error;
+               else
+                       n -= totalreserve_pages;
+
+               /*
+                * Leave the last 3% for root
+                */
                if (!cap_sys_admin)
                        n -= n / 32;
                free += n;
 
                if (free > pages)
                        return 0;
-               vm_unacct_memory(pages);
-               return -ENOMEM;
+
+               goto error;
        }
 
        allowed = (totalram_pages - hugetlb_total_pages())
@@ -149,16 +174,12 @@ int __vm_enough_memory(long pages, int cap_sys_admin)
         */
        if (atomic_read(&vm_committed_space) < (long)allowed)
                return 0;
-
+error:
        vm_unacct_memory(pages);
 
        return -ENOMEM;
 }
 
-EXPORT_SYMBOL(sysctl_overcommit_memory);
-EXPORT_SYMBOL(sysctl_overcommit_ratio);
-EXPORT_SYMBOL(sysctl_max_map_count);
-EXPORT_SYMBOL(vm_committed_space);
 EXPORT_SYMBOL(__vm_enough_memory);
 
 /*
@@ -168,7 +189,7 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma,
                struct file *file, struct address_space *mapping)
 {
        if (vma->vm_flags & VM_DENYWRITE)
-               atomic_inc(&file->f_dentry->d_inode->i_writecount);
+               atomic_inc(&file->f_path.dentry->d_inode->i_writecount);
        if (vma->vm_flags & VM_SHARED)
                mapping->i_mmap_writable--;
 
@@ -223,6 +244,17 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
 
        if (brk < mm->end_code)
                goto out;
+
+       /*
+        * Check against rlimit here. If this check is done later after the test
+        * of oldbrk with newbrk then it can escape the test and let the data
+        * segment grow beyond its set limit the in case where the limit is
+        * not page aligned -Ram Gupta
+        */
+       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+       if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
+               goto out;
+
        newbrk = PAGE_ALIGN(brk);
        oldbrk = PAGE_ALIGN(mm->brk);
        if (oldbrk == newbrk)
@@ -235,11 +267,6 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
                goto out;
        }
 
-       /* Check against rlimit.. */
-       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
-       if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
-               goto out;
-
        /* Check against existing mmap mappings. */
        if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
                goto out;
@@ -273,6 +300,8 @@ static int browse_rb(struct rb_root *root)
                        printk("vm_end %lx < vm_start %lx\n", vma->vm_end, vma->vm_start);
                i++;
                pn = nd;
+               prev = vma->vm_start;
+               pend = vma->vm_end;
        }
        j = 0;
        for (nd = pn; nd; nd = rb_prev(nd)) {
@@ -297,8 +326,7 @@ void validate_mm(struct mm_struct *mm)
        i = browse_rb(&mm->mm_rb);
        if (i != mm->map_count)
                printk("map_count %d rb %d\n", mm->map_count, i), bug = 1;
-       if (bug)
-               BUG();
+       BUG_ON(bug);
 }
 #else
 #define validate_mm(mm) do { } while (0)
@@ -374,7 +402,7 @@ static inline void __vma_link_file(struct vm_area_struct *vma)
                struct address_space *mapping = file->f_mapping;
 
                if (vma->vm_flags & VM_DENYWRITE)
-                       atomic_dec(&file->f_dentry->d_inode->i_writecount);
+                       atomic_dec(&file->f_path.dentry->d_inode->i_writecount);
                if (vma->vm_flags & VM_SHARED)
                        mapping->i_mmap_writable++;
 
@@ -435,8 +463,7 @@ __insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
        struct rb_node ** rb_link, * rb_parent;
 
        __vma = find_vma_prepare(mm, vma->vm_start,&prev, &rb_link, &rb_parent);
-       if (__vma && __vma->vm_start < vma->vm_end)
-               BUG();
+       BUG_ON(__vma && __vma->vm_start < vma->vm_end);
        __vma_link(mm, vma, prev, rb_link, rb_parent);
        mm->map_count++;
 }
@@ -615,7 +642,7 @@ again:                      remove_next = 1 + (end > next->vm_end);
  * If the vma has a ->close operation then the driver probably needs to release
  * per-vma resources, so we don't attempt to merge those.
  */
-#define VM_SPECIAL (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED)
+#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP)
 
 static inline int is_mergeable_vma(struct vm_area_struct *vma,
                        struct file *file, unsigned long vm_flags)
@@ -816,8 +843,7 @@ try_prev:
         * (e.g. stash info in next's anon_vma_node when assigning
         * an anon_vma, or when trying vma_merge).  Another time.
         */
-       if (find_vma_prev(vma->vm_mm, vma->vm_start, &near) != vma)
-               BUG();
+       BUG_ON(find_vma_prev(vma->vm_mm, vma->vm_start, &near) != vma);
        if (!near)
                goto none;
 
@@ -848,14 +874,6 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags,
        const unsigned long stack_flags
                = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);
 
-#ifdef CONFIG_HUGETLB
-       if (flags & VM_HUGETLB) {
-               if (!(flags & VM_DONTCOPY))
-                       mm->shared_vm += pages;
-               return;
-       }
-#endif /* CONFIG_HUGETLB */
-
        if (file) {
                mm->shared_vm += pages;
                if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
@@ -876,26 +894,12 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
                        unsigned long flags, unsigned long pgoff)
 {
        struct mm_struct * mm = current->mm;
-       struct vm_area_struct * vma, * prev;
        struct inode *inode;
        unsigned int vm_flags;
-       int correct_wcount = 0;
        int error;
-       struct rb_node ** rb_link, * rb_parent;
        int accountable = 1;
-       unsigned long charged = 0, reqprot = prot;
+       unsigned long reqprot = prot;
 
-       if (file) {
-               if (is_file_hugepages(file))
-                       accountable = 0;
-
-               if (!file->f_op || !file->f_op->mmap)
-                       return -ENODEV;
-
-               if ((prot & PROT_EXEC) &&
-                   (file->f_vfsmnt->mnt_flags & MNT_NOEXEC))
-                       return -EPERM;
-       }
        /*
         * Does the application expect PROT_READ to imply PROT_EXEC?
         *
@@ -903,12 +907,16 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
         *  mounted, in which case we dont add PROT_EXEC.)
         */
        if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
-               if (!(file && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC)))
+               if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC)))
                        prot |= PROT_EXEC;
 
        if (!len)
                return -EINVAL;
 
+       error = arch_mmap_check(addr, len, flags);
+       if (error)
+               return error;
+
        /* Careful about overflows.. */
        len = PAGE_ALIGN(len);
        if (!len || len > TASK_SIZE)
@@ -952,7 +960,7 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
                        return -EAGAIN;
        }
 
-       inode = file ? file->f_dentry->d_inode : NULL;
+       inode = file ? file->f_path.dentry->d_inode : NULL;
 
        if (file) {
                switch (flags & MAP_TYPE) {
@@ -981,6 +989,16 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
                case MAP_PRIVATE:
                        if (!(file->f_mode & FMODE_READ))
                                return -EACCES;
+                       if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
+                               if (vm_flags & VM_EXEC)
+                                       return -EPERM;
+                               vm_flags &= ~VM_MAYEXEC;
+                       }
+                       if (is_file_hugepages(file))
+                               accountable = 0;
+
+                       if (!file->f_op || !file->f_op->mmap)
+                               return -ENODEV;
                        break;
 
                default:
@@ -1002,10 +1020,28 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
                }
        }
 
-       error = security_file_mmap(file, reqprot, prot, flags);
+       error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
        if (error)
                return error;
-               
+
+       return mmap_region(file, addr, len, flags, vm_flags, pgoff,
+                          accountable);
+}
+EXPORT_SYMBOL(do_mmap_pgoff);
+
+unsigned long mmap_region(struct file *file, unsigned long addr,
+                         unsigned long len, unsigned long flags,
+                         unsigned int vm_flags, unsigned long pgoff,
+                         int accountable)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma, *prev;
+       int correct_wcount = 0;
+       int error;
+       struct rb_node **rb_link, *rb_parent;
+       unsigned long charged = 0;
+       struct inode *inode =  file ? file->f_path.dentry->d_inode : NULL;
+
        /* Clear old maps */
        error = -ENOMEM;
 munmap_back:
@@ -1051,18 +1087,18 @@ munmap_back:
         * specific mapper. the address has already been validated, but
         * not unmapped, but the maps are removed from the list.
         */
-       vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
        if (!vma) {
                error = -ENOMEM;
                goto unacct_error;
        }
-       memset(vma, 0, sizeof(*vma));
 
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
        vma->vm_flags = vm_flags;
-       vma->vm_page_prot = protection_map[vm_flags & 0x0f];
+       vma->vm_page_prot = protection_map[vm_flags &
+                               (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
        vma->vm_pgoff = pgoff;
 
        if (file) {
@@ -1080,17 +1116,6 @@ munmap_back:
                error = file->f_op->mmap(file, vma);
                if (error)
                        goto unmap_and_free_vma;
-               if ((vma->vm_flags & (VM_SHARED | VM_WRITE | VM_RESERVED))
-                                               == (VM_WRITE | VM_RESERVED)) {
-                       printk(KERN_WARNING "program %s is using MAP_PRIVATE, "
-                               "PROT_WRITE mmap of VM_RESERVED memory, which "
-                               "is deprecated. Please report this to "
-                               "linux-kernel@vger.kernel.org\n",current->comm);
-                       if (vma->vm_ops && vma->vm_ops->close)
-                               vma->vm_ops->close(vma);
-                       error = -EACCES;
-                       goto unmap_and_free_vma;
-               }
        } else if (vm_flags & VM_SHARED) {
                error = shmem_zero_setup(vma);
                if (error)
@@ -1114,6 +1139,10 @@ munmap_back:
        pgoff = vma->vm_pgoff;
        vm_flags = vma->vm_flags;
 
+       if (vma_wants_writenotify(vma))
+               vma->vm_page_prot =
+                       protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC)];
+
        if (!file || !vma_merge(mm, prev, addr, vma->vm_end,
                        vma->vm_flags, NULL, file, pgoff, vma_policy(vma))) {
                file = vma->vm_file;
@@ -1136,12 +1165,8 @@ out:
                mm->locked_vm += len >> PAGE_SHIFT;
                make_pages_present(addr, addr + len);
        }
-       if (flags & MAP_POPULATE) {
-               up_write(&mm->mmap_sem);
-               sys_remap_file_pages(addr, len, 0,
-                                       pgoff, flags & MAP_NONBLOCK);
-               down_write(&mm->mmap_sem);
-       }
+       if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
+               make_pages_present(addr, addr + len);
        return addr;
 
 unmap_and_free_vma:
@@ -1161,8 +1186,6 @@ unacct_error:
        return error;
 }
 
-EXPORT_SYMBOL(do_mmap_pgoff);
-
 /* Get an address range which is currently unmapped.
  * For shmat() with addr=0.
  *
@@ -1186,6 +1209,9 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
        if (len > TASK_SIZE)
                return -ENOMEM;
 
+       if (flags & MAP_FIXED)
+               return addr;
+
        if (addr) {
                addr = PAGE_ALIGN(addr);
                vma = find_vma(mm, addr);
@@ -1259,6 +1285,9 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
        if (len > TASK_SIZE)
                return -ENOMEM;
 
+       if (flags & MAP_FIXED)
+               return addr;
+
        /* requesting a specific address */
        if (addr) {
                addr = PAGE_ALIGN(addr);
@@ -1346,39 +1375,21 @@ unsigned long
 get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags)
 {
-       unsigned long ret;
-
-       if (!(flags & MAP_FIXED)) {
-               unsigned long (*get_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-
-               get_area = current->mm->get_unmapped_area;
-               if (file && file->f_op && file->f_op->get_unmapped_area)
-                       get_area = file->f_op->get_unmapped_area;
-               addr = get_area(file, addr, len, pgoff, flags);
-               if (IS_ERR_VALUE(addr))
-                       return addr;
-       }
+       unsigned long (*get_area)(struct file *, unsigned long,
+                                 unsigned long, unsigned long, unsigned long);
+
+       get_area = current->mm->get_unmapped_area;
+       if (file && file->f_op && file->f_op->get_unmapped_area)
+               get_area = file->f_op->get_unmapped_area;
+       addr = get_area(file, addr, len, pgoff, flags);
+       if (IS_ERR_VALUE(addr))
+               return addr;
 
        if (addr > TASK_SIZE - len)
                return -ENOMEM;
        if (addr & ~PAGE_MASK)
                return -EINVAL;
-       if (file && is_file_hugepages(file))  {
-               /*
-                * Check if the given range is hugepage aligned, and
-                * can be made suitable for hugepages.
-                */
-               ret = prepare_hugepage_range(addr, len);
-       } else {
-               /*
-                * Ensure that a normal request is not falling in a
-                * reserved hugepage range.  For some archs like IA-64,
-                * there is a separate region for hugepages.
-                */
-               ret = is_hugepage_only_range(current->mm, addr, len);
-       }
-       if (ret)
-               return -EINVAL;
+
        return addr;
 }
 
@@ -1466,6 +1477,7 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
 {
        struct mm_struct *mm = vma->vm_mm;
        struct rlimit *rlim = current->signal->rlim;
+       unsigned long new_start;
 
        /* address space limit tests */
        if (!may_expand_vm(mm, grow))
@@ -1485,6 +1497,12 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
                        return -ENOMEM;
        }
 
+       /* Check to ensure the stack will not grow into a hugetlb-only region */
+       new_start = (vma->vm_flags & VM_GROWSUP) ? vma->vm_start :
+                       vma->vm_end - size;
+       if (is_hugepage_only_range(vma->vm_mm, new_start, size))
+               return -EFAULT;
+
        /*
         * Overcommit..  This must be the final test, as it will
         * update security statistics.
@@ -1505,7 +1523,7 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
  * PA-RISC uses this for its stack; IA64 for its Register Backing Store.
  * vma is the last one with address > vma->vm_end.  Have to extend vma.
  */
-#ifdef CONFIG_STACK_GROWSUP
+#ifndef CONFIG_IA64
 static inline
 #endif
 int expand_upwards(struct vm_area_struct *vma, unsigned long address)
@@ -1527,9 +1545,14 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
         * vma->vm_start/vm_end cannot change under us because the caller
         * is required to hold the mmap_sem in read mode.  We need the
         * anon_vma lock to serialize against concurrent expand_stacks.
+        * Also guard against wrapping around to address 0.
         */
-       address += 4 + PAGE_SIZE - 1;
-       address &= PAGE_MASK;
+       if (address < PAGE_ALIGN(address+4))
+               address = PAGE_ALIGN(address+4);
+       else {
+               anon_vma_unlock(vma);
+               return -ENOMEM;
+       }
        error = 0;
 
        /* Somebody else might have raced and expanded it already */
@@ -1711,7 +1734,7 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma,
 
 /*
  * Split a vma into two pieces at address 'addr', a new vma is allocated
- * either for the first part or the the tail.
+ * either for the first part or the tail.
  */
 int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
              unsigned long addr, int new_below)
@@ -1725,7 +1748,7 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
        if (mm->map_count >= sysctl_max_map_count)
                return -ENOMEM;
 
-       new = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
        if (!new)
                return -ENOMEM;
 
@@ -1840,7 +1863,7 @@ asmlinkage long sys_munmap(unsigned long addr, size_t len)
 
 static inline void verify_mm_writelocked(struct mm_struct *mm)
 {
-#ifdef CONFIG_DEBUG_KERNEL
+#ifdef CONFIG_DEBUG_VM
        if (unlikely(down_read_trylock(&mm->mmap_sem))) {
                WARN_ON(1);
                up_read(&mm->mmap_sem);
@@ -1860,6 +1883,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        unsigned long flags;
        struct rb_node ** rb_link, * rb_parent;
        pgoff_t pgoff = addr >> PAGE_SHIFT;
+       int error;
 
        len = PAGE_ALIGN(len);
        if (!len)
@@ -1868,6 +1892,15 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        if ((addr + len) > TASK_SIZE || (addr + len) < addr)
                return -EINVAL;
 
+       if (is_hugepage_only_range(mm, addr, len))
+               return -EINVAL;
+
+       flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
+
+       error = arch_mmap_check(addr, len, flags);
+       if (error)
+               return error;
+
        /*
         * mlock MCL_FUTURE?
         */
@@ -1908,8 +1941,6 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        if (security_vm_enough_memory(len >> PAGE_SHIFT))
                return -ENOMEM;
 
-       flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
-
        /* Can we just expand an old private anonymous mapping? */
        if (vma_merge(mm, prev, addr, addr + len, flags,
                                        NULL, NULL, pgoff, NULL))
@@ -1918,19 +1949,19 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        /*
         * create a vma struct for an anonymous mapping
         */
-       vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
        if (!vma) {
                vm_unacct_memory(len >> PAGE_SHIFT);
                return -ENOMEM;
        }
-       memset(vma, 0, sizeof(*vma));
 
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
        vma->vm_pgoff = pgoff;
        vma->vm_flags = flags;
-       vma->vm_page_prot = protection_map[flags & 0x0f];
+       vma->vm_page_prot = protection_map[flags &
+                               (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
        vma_link(mm, vma, prev, rb_link, rb_parent);
 out:
        mm->total_vm += len >> PAGE_SHIFT;
@@ -1951,6 +1982,9 @@ void exit_mmap(struct mm_struct *mm)
        unsigned long nr_accounted = 0;
        unsigned long end;
 
+       /* mm's last user has gone, and its about to be pulled down */
+       arch_exit_mmap(mm);
+
        lru_add_drain();
        flush_cache_mm(mm);
        tlb = tlb_gather_mmu(mm, 1);
@@ -2038,7 +2072,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                    vma_start < new_vma->vm_end)
                        *vmap = new_vma;
        } else {
-               new_vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+               new_vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
                if (new_vma) {
                        *new_vma = *vma;
                        pol = mpol_copy(vma_policy(vma));
@@ -2075,3 +2109,75 @@ int may_expand_vm(struct mm_struct *mm, unsigned long npages)
                return 0;
        return 1;
 }
+
+
+static struct page *special_mapping_nopage(struct vm_area_struct *vma,
+                                          unsigned long address, int *type)
+{
+       struct page **pages;
+
+       BUG_ON(address < vma->vm_start || address >= vma->vm_end);
+
+       address -= vma->vm_start;
+       for (pages = vma->vm_private_data; address > 0 && *pages; ++pages)
+               address -= PAGE_SIZE;
+
+       if (*pages) {
+               struct page *page = *pages;
+               get_page(page);
+               return page;
+       }
+
+       return NOPAGE_SIGBUS;
+}
+
+/*
+ * Having a close hook prevents vma merging regardless of flags.
+ */
+static void special_mapping_close(struct vm_area_struct *vma)
+{
+}
+
+static struct vm_operations_struct special_mapping_vmops = {
+       .close = special_mapping_close,
+       .nopage = special_mapping_nopage,
+};
+
+/*
+ * Called with mm->mmap_sem held for writing.
+ * Insert a new vma covering the given region, with the given flags.
+ * Its pages are supplied by the given array of struct page *.
+ * The array can be shorter than len >> PAGE_SHIFT if it's null-terminated.
+ * The region past the last page supplied will always produce SIGBUS.
+ * The array pointer and the pages it points to are assumed to stay alive
+ * for as long as this mapping might exist.
+ */
+int install_special_mapping(struct mm_struct *mm,
+                           unsigned long addr, unsigned long len,
+                           unsigned long vm_flags, struct page **pages)
+{
+       struct vm_area_struct *vma;
+
+       vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
+       if (unlikely(vma == NULL))
+               return -ENOMEM;
+
+       vma->vm_mm = mm;
+       vma->vm_start = addr;
+       vma->vm_end = addr + len;
+
+       vma->vm_flags = vm_flags | mm->def_flags;
+       vma->vm_page_prot = protection_map[vma->vm_flags & 7];
+
+       vma->vm_ops = &special_mapping_vmops;
+       vma->vm_private_data = pages;
+
+       if (unlikely(insert_vm_struct(mm, vma))) {
+               kmem_cache_free(vm_area_cachep, vma);
+               return -ENOMEM;
+       }
+
+       mm->total_vm += len >> PAGE_SHIFT;
+
+       return 0;
+}