mm/dmapool.c: take lock only once in dma_pool_free()
[linux-2.6.git] / mm / nommu.c
index d6dd656..f59e142 100644 (file)
@@ -10,7 +10,7 @@
  *  Copyright (c) 2000-2003 David McCullough <davidm@snapgear.com>
  *  Copyright (c) 2000-2001 D Jeff Dionne <jeff@uClinux.org>
  *  Copyright (c) 2002      Greg Ungerer <gerg@snapgear.com>
- *  Copyright (c) 2007-2009 Paul Mundt <lethal@linux-sh.org>
+ *  Copyright (c) 2007-2010 Paul Mundt <lethal@linux-sh.org>
  */
 
 #include <linux/module.h>
@@ -29,6 +29,7 @@
 #include <linux/personality.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
+#include <linux/audit.h>
 
 #include <asm/uaccess.h>
 #include <asm/tlb.h>
 #include <asm/mmu_context.h>
 #include "internal.h"
 
-static inline __attribute__((format(printf, 1, 2)))
-void no_printk(const char *fmt, ...)
-{
-}
-
 #if 0
 #define kenter(FMT, ...) \
        printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
@@ -131,7 +127,8 @@ unsigned int kobjsize(const void *objp)
 
 int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                     unsigned long start, int nr_pages, unsigned int foll_flags,
-                    struct page **pages, struct vm_area_struct **vmas)
+                    struct page **pages, struct vm_area_struct **vmas,
+                    int *retry)
 {
        struct vm_area_struct *vma;
        unsigned long vm_flags;
@@ -162,7 +159,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                }
                if (vmas)
                        vmas[i] = vma;
-               start += PAGE_SIZE;
+               start = (start + PAGE_SIZE) & PAGE_MASK;
        }
 
        return i;
@@ -189,7 +186,8 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
        if (force)
                flags |= FOLL_FORCE;
 
-       return __get_user_pages(tsk, mm, start, nr_pages, flags, pages, vmas);
+       return __get_user_pages(tsk, mm, start, nr_pages, flags, pages, vmas,
+                               NULL);
 }
 EXPORT_SYMBOL(get_user_pages);
 
@@ -298,12 +296,60 @@ void *vmalloc(unsigned long size)
 }
 EXPORT_SYMBOL(vmalloc);
 
+/*
+ *     vzalloc - allocate virtually continguos memory with zero fill
+ *
+ *     @size:          allocation size
+ *
+ *     Allocate enough pages to cover @size from the page level
+ *     allocator and map them into continguos kernel virtual space.
+ *     The memory allocated is set to zero.
+ *
+ *     For tight control over page level allocator and protection flags
+ *     use __vmalloc() instead.
+ */
+void *vzalloc(unsigned long size)
+{
+       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
+                       PAGE_KERNEL);
+}
+EXPORT_SYMBOL(vzalloc);
+
+/**
+ * vmalloc_node - allocate memory on a specific node
+ * @size:      allocation size
+ * @node:      numa node
+ *
+ * Allocate enough pages to cover @size from the page level
+ * allocator and map them into contiguous kernel virtual space.
+ *
+ * For tight control over page level allocator and protection flags
+ * use __vmalloc() instead.
+ */
 void *vmalloc_node(unsigned long size, int node)
 {
        return vmalloc(size);
 }
 EXPORT_SYMBOL(vmalloc_node);
 
+/**
+ * vzalloc_node - allocate memory on a specific node with zero fill
+ * @size:      allocation size
+ * @node:      numa node
+ *
+ * Allocate enough pages to cover @size from the page level
+ * allocator and map them into contiguous kernel virtual space.
+ * The memory allocated is set to zero.
+ *
+ * For tight control over page level allocator and protection flags
+ * use __vmalloc() instead.
+ */
+void *vzalloc_node(unsigned long size, int node)
+{
+       return vzalloc(size);
+}
+EXPORT_SYMBOL(vzalloc_node);
+
 #ifndef PAGE_KERNEL_EXEC
 # define PAGE_KERNEL_EXEC PAGE_KERNEL
 #endif
@@ -397,6 +443,31 @@ void  __attribute__((weak)) vmalloc_sync_all(void)
 {
 }
 
+/**
+ *     alloc_vm_area - allocate a range of kernel address space
+ *     @size:          size of the area
+ *
+ *     Returns:        NULL on failure, vm_struct on success
+ *
+ *     This function reserves a range of kernel address space, and
+ *     allocates pagetables to map that range.  No actual mappings
+ *     are created.  If the kernel address space is not shared
+ *     between processes, it syncs the pagetable across all
+ *     processes.
+ */
+struct vm_struct *alloc_vm_area(size_t size)
+{
+       BUG();
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(alloc_vm_area);
+
+void free_vm_area(struct vm_struct *area)
+{
+       BUG();
+}
+EXPORT_SYMBOL_GPL(free_vm_area);
+
 int vm_insert_page(struct vm_area_struct *vma, unsigned long addr,
                   struct page *page)
 {
@@ -609,7 +680,7 @@ static void protect_vma(struct vm_area_struct *vma, unsigned long flags)
  */
 static void add_vma_to_mm(struct mm_struct *mm, struct vm_area_struct *vma)
 {
-       struct vm_area_struct *pvma, **pp;
+       struct vm_area_struct *pvma, **pp, *next;
        struct address_space *mapping;
        struct rb_node **p, *parent;
 
@@ -669,8 +740,11 @@ static void add_vma_to_mm(struct mm_struct *mm, struct vm_area_struct *vma)
                        break;
        }
 
-       vma->vm_next = *pp;
+       next = *pp;
        *pp = vma;
+       vma->vm_next = next;
+       if (next)
+               next->vm_prev = vma;
 }
 
 /*
@@ -918,14 +992,6 @@ static int validate_mmap_request(struct file *file,
                        if (!(capabilities & BDI_CAP_MAP_DIRECT))
                                return -ENODEV;
 
-                       if (((prot & PROT_READ)  && !(capabilities & BDI_CAP_READ_MAP))  ||
-                           ((prot & PROT_WRITE) && !(capabilities & BDI_CAP_WRITE_MAP)) ||
-                           ((prot & PROT_EXEC)  && !(capabilities & BDI_CAP_EXEC_MAP))
-                           ) {
-                               printk("MAP_SHARED not completely supported on !MMU\n");
-                               return -EINVAL;
-                       }
-
                        /* we mustn't privatise shared mappings */
                        capabilities &= ~BDI_CAP_MAP_COPY;
                }
@@ -941,6 +1007,20 @@ static int validate_mmap_request(struct file *file,
                                capabilities &= ~BDI_CAP_MAP_DIRECT;
                }
 
+               if (capabilities & BDI_CAP_MAP_DIRECT) {
+                       if (((prot & PROT_READ)  && !(capabilities & BDI_CAP_READ_MAP))  ||
+                           ((prot & PROT_WRITE) && !(capabilities & BDI_CAP_WRITE_MAP)) ||
+                           ((prot & PROT_EXEC)  && !(capabilities & BDI_CAP_EXEC_MAP))
+                           ) {
+                               capabilities &= ~BDI_CAP_MAP_DIRECT;
+                               if (flags & MAP_SHARED) {
+                                       printk(KERN_WARNING
+                                              "MAP_SHARED not completely supported on !MMU\n");
+                                       return -EINVAL;
+                               }
+                       }
+               }
+
                /* handle executable mappings and implied executable
                 * mappings */
                if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
@@ -996,22 +1076,20 @@ static unsigned long determine_vm_flags(struct file *file,
        unsigned long vm_flags;
 
        vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags);
-       vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
        /* vm_flags |= mm->def_flags; */
 
        if (!(capabilities & BDI_CAP_MAP_DIRECT)) {
                /* attempt to share read-only copies of mapped file chunks */
+               vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
                if (file && !(prot & PROT_WRITE))
                        vm_flags |= VM_MAYSHARE;
-       }
-       else {
+       } else {
                /* overlay a shareable mapping on the backing device or inode
                 * if possible - used for chardevs, ramfs/tmpfs/shmfs and
                 * romfs/cramfs */
+               vm_flags |= VM_MAYSHARE | (capabilities & BDI_CAP_VMFLAGS);
                if (flags & MAP_SHARED)
-                       vm_flags |= VM_MAYSHARE | VM_SHARED;
-               else if ((((vm_flags & capabilities) ^ vm_flags) & BDI_CAP_VMFLAGS) == 0)
-                       vm_flags |= VM_MAYSHARE;
+                       vm_flags |= VM_SHARED;
        }
 
        /* refuse to let anyone share private mappings with this process if
@@ -1040,10 +1118,9 @@ static int do_mmap_shared_file(struct vm_area_struct *vma)
        if (ret != -ENOSYS)
                return ret;
 
-       /* getting an ENOSYS error indicates that direct mmap isn't
-        * possible (as opposed to tried but failed) so we'll fall
-        * through to making a private copy of the data and mapping
-        * that if we can */
+       /* getting -ENOSYS indicates that direct mmap isn't possible (as
+        * opposed to tried but failed) so we can only give a suitable error as
+        * it's not possible to make a private copy if MAP_SHARED was given */
        return -ENODEV;
 }
 
@@ -1209,7 +1286,7 @@ unsigned long do_mmap_pgoff(struct file *file,
        region->vm_flags = vm_flags;
        region->vm_pgoff = pgoff;
 
-       INIT_LIST_HEAD(&vma->anon_vma_node);
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
        vma->vm_flags = vm_flags;
        vma->vm_pgoff = pgoff;
 
@@ -1410,6 +1487,7 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
        struct file *file = NULL;
        unsigned long retval = -EBADF;
 
+       audit_mmap_fd(fd, flags);
        if (!(flags & MAP_ANONYMOUS)) {
                file = fget(fd);
                if (!file)
@@ -1428,6 +1506,30 @@ out:
        return retval;
 }
 
+#ifdef __ARCH_WANT_SYS_OLD_MMAP
+struct mmap_arg_struct {
+       unsigned long addr;
+       unsigned long len;
+       unsigned long prot;
+       unsigned long flags;
+       unsigned long fd;
+       unsigned long offset;
+};
+
+SYSCALL_DEFINE1(old_mmap, struct mmap_arg_struct __user *, arg)
+{
+       struct mmap_arg_struct a;
+
+       if (copy_from_user(&a, arg, sizeof(a)))
+               return -EFAULT;
+       if (a.offset & ~PAGE_MASK)
+               return -EINVAL;
+
+       return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+                             a.offset >> PAGE_SHIFT);
+}
+#endif /* __ARCH_WANT_SYS_OLD_MMAP */
+
 /*
  * split a vma into two pieces at address 'addr', a new vma is allocated either
  * for the first part or the tail.
@@ -1643,6 +1745,7 @@ void exit_mmap(struct mm_struct *mm)
                mm->mmap = vma->vm_next;
                delete_vma_from_mm(vma);
                delete_vma(mm, vma);
+               cond_resched();
        }
 
        kleave("");
@@ -1761,27 +1864,6 @@ void unmap_mapping_range(struct address_space *mapping,
 EXPORT_SYMBOL(unmap_mapping_range);
 
 /*
- * ask for an unmapped area at which to create a mapping on a file
- */
-unsigned long get_unmapped_area(struct file *file, unsigned long addr,
-                               unsigned long len, unsigned long pgoff,
-                               unsigned long flags)
-{
-       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;
-
-       if (!get_area)
-               return -ENOSYS;
-
-       return get_area(file, addr, len, pgoff, flags);
-}
-EXPORT_SYMBOL(get_unmapped_area);
-
-/*
  * Check that a process has enough memory to allocate a new virtual
  * mapping. 0 means there is enough memory for the allocation to
  * succeed and -ENOMEM implies there is not.
@@ -1935,3 +2017,65 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
        mmput(mm);
        return len;
 }
+
+/**
+ * nommu_shrink_inode_mappings - Shrink the shared mappings on an inode
+ * @inode: The inode to check
+ * @size: The current filesize of the inode
+ * @newsize: The proposed filesize of the inode
+ *
+ * Check the shared mappings on an inode on behalf of a shrinking truncate to
+ * make sure that that any outstanding VMAs aren't broken and then shrink the
+ * vm_regions that extend that beyond so that do_mmap_pgoff() doesn't
+ * automatically grant mappings that are too large.
+ */
+int nommu_shrink_inode_mappings(struct inode *inode, size_t size,
+                               size_t newsize)
+{
+       struct vm_area_struct *vma;
+       struct prio_tree_iter iter;
+       struct vm_region *region;
+       pgoff_t low, high;
+       size_t r_size, r_top;
+
+       low = newsize >> PAGE_SHIFT;
+       high = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+       down_write(&nommu_region_sem);
+
+       /* search for VMAs that fall within the dead zone */
+       vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap,
+                             low, high) {
+               /* found one - only interested if it's shared out of the page
+                * cache */
+               if (vma->vm_flags & VM_SHARED) {
+                       up_write(&nommu_region_sem);
+                       return -ETXTBSY; /* not quite true, but near enough */
+               }
+       }
+
+       /* reduce any regions that overlap the dead zone - if in existence,
+        * these will be pointed to by VMAs that don't overlap the dead zone
+        *
+        * we don't check for any regions that start beyond the EOF as there
+        * shouldn't be any
+        */
+       vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap,
+                             0, ULONG_MAX) {
+               if (!(vma->vm_flags & VM_SHARED))
+                       continue;
+
+               region = vma->vm_region;
+               r_size = region->vm_top - region->vm_start;
+               r_top = (region->vm_pgoff << PAGE_SHIFT) + r_size;
+
+               if (r_top > newsize) {
+                       region->vm_top -= r_top - newsize;
+                       if (region->vm_end > region->vm_top)
+                               region->vm_end = region->vm_top;
+               }
+       }
+
+       up_write(&nommu_region_sem);
+       return 0;
+}