Merge branch 'x86/ptrace-syscall-exit' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / mm / vmalloc.c
index 204b8243d8abb11cb9abd2f37dd8864ad0a346aa..69511e663234482228909f373e247d21082e6ada 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pfn.h>
 #include <linux/kmemleak.h>
-
+#include <linux/highmem.h>
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
@@ -168,11 +168,9 @@ static int vmap_page_range_noflush(unsigned long start, unsigned long end,
                next = pgd_addr_end(addr, end);
                err = vmap_pud_range(pgd, addr, next, prot, pages, &nr);
                if (err)
-                       break;
+                       return err;
        } while (pgd++, addr = next, addr != end);
 
-       if (unlikely(err))
-               return err;
        return nr;
 }
 
@@ -186,7 +184,7 @@ static int vmap_page_range(unsigned long start, unsigned long end,
        return ret;
 }
 
-static inline int is_vmalloc_or_module_addr(const void *x)
+int is_vmalloc_or_module_addr(const void *x)
 {
        /*
         * ARM, x86-64 and sparc64 put modules in a special place,
@@ -1272,17 +1270,21 @@ struct vm_struct *remove_vm_area(const void *addr)
        if (va && va->flags & VM_VM_AREA) {
                struct vm_struct *vm = va->private;
                struct vm_struct *tmp, **p;
-
-               vmap_debug_free_range(va->va_start, va->va_end);
-               free_unmap_vmap_area(va);
-               vm->size -= PAGE_SIZE;
-
+               /*
+                * remove from list and disallow access to this vm_struct
+                * before unmap. (address range confliction is maintained by
+                * vmap.)
+                */
                write_lock(&vmlist_lock);
                for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next)
                        ;
                *p = tmp->next;
                write_unlock(&vmlist_lock);
 
+               vmap_debug_free_range(va->va_start, va->va_end);
+               free_unmap_vmap_area(va);
+               vm->size -= PAGE_SIZE;
+
                return vm;
        }
        return NULL;
@@ -1384,7 +1386,7 @@ void *vmap(struct page **pages, unsigned int count,
 
        might_sleep();
 
-       if (count > num_physpages)
+       if (count > totalram_pages)
                return NULL;
 
        area = get_vm_area_caller((count << PAGE_SHIFT), flags,
@@ -1491,7 +1493,7 @@ static void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot,
        unsigned long real_size = size;
 
        size = PAGE_ALIGN(size);
-       if (!size || (size >> PAGE_SHIFT) > num_physpages)
+       if (!size || (size >> PAGE_SHIFT) > totalram_pages)
                return NULL;
 
        area = __get_vm_area_node(size, VM_ALLOC, VMALLOC_START, VMALLOC_END,
@@ -1641,10 +1643,120 @@ void *vmalloc_32_user(unsigned long size)
 }
 EXPORT_SYMBOL(vmalloc_32_user);
 
+/*
+ * small helper routine , copy contents to buf from addr.
+ * If the page is not present, fill zero.
+ */
+
+static int aligned_vread(char *buf, char *addr, unsigned long count)
+{
+       struct page *p;
+       int copied = 0;
+
+       while (count) {
+               unsigned long offset, length;
+
+               offset = (unsigned long)addr & ~PAGE_MASK;
+               length = PAGE_SIZE - offset;
+               if (length > count)
+                       length = count;
+               p = vmalloc_to_page(addr);
+               /*
+                * To do safe access to this _mapped_ area, we need
+                * lock. But adding lock here means that we need to add
+                * overhead of vmalloc()/vfree() calles for this _debug_
+                * interface, rarely used. Instead of that, we'll use
+                * kmap() and get small overhead in this access function.
+                */
+               if (p) {
+                       /*
+                        * we can expect USER0 is not used (see vread/vwrite's
+                        * function description)
+                        */
+                       void *map = kmap_atomic(p, KM_USER0);
+                       memcpy(buf, map + offset, length);
+                       kunmap_atomic(map, KM_USER0);
+               } else
+                       memset(buf, 0, length);
+
+               addr += length;
+               buf += length;
+               copied += length;
+               count -= length;
+       }
+       return copied;
+}
+
+static int aligned_vwrite(char *buf, char *addr, unsigned long count)
+{
+       struct page *p;
+       int copied = 0;
+
+       while (count) {
+               unsigned long offset, length;
+
+               offset = (unsigned long)addr & ~PAGE_MASK;
+               length = PAGE_SIZE - offset;
+               if (length > count)
+                       length = count;
+               p = vmalloc_to_page(addr);
+               /*
+                * To do safe access to this _mapped_ area, we need
+                * lock. But adding lock here means that we need to add
+                * overhead of vmalloc()/vfree() calles for this _debug_
+                * interface, rarely used. Instead of that, we'll use
+                * kmap() and get small overhead in this access function.
+                */
+               if (p) {
+                       /*
+                        * we can expect USER0 is not used (see vread/vwrite's
+                        * function description)
+                        */
+                       void *map = kmap_atomic(p, KM_USER0);
+                       memcpy(map + offset, buf, length);
+                       kunmap_atomic(map, KM_USER0);
+               }
+               addr += length;
+               buf += length;
+               copied += length;
+               count -= length;
+       }
+       return copied;
+}
+
+/**
+ *     vread() -  read vmalloc area in a safe way.
+ *     @buf:           buffer for reading data
+ *     @addr:          vm address.
+ *     @count:         number of bytes to be read.
+ *
+ *     Returns # of bytes which addr and buf should be increased.
+ *     (same number to @count). Returns 0 if [addr...addr+count) doesn't
+ *     includes any intersect with alive vmalloc area.
+ *
+ *     This function checks that addr is a valid vmalloc'ed area, and
+ *     copy data from that area to a given buffer. If the given memory range
+ *     of [addr...addr+count) includes some valid address, data is copied to
+ *     proper area of @buf. If there are memory holes, they'll be zero-filled.
+ *     IOREMAP area is treated as memory hole and no copy is done.
+ *
+ *     If [addr...addr+count) doesn't includes any intersects with alive
+ *     vm_struct area, returns 0.
+ *     @buf should be kernel's buffer. Because this function uses KM_USER0,
+ *     the caller should guarantee KM_USER0 is not used.
+ *
+ *     Note: In usual ops, vread() is never necessary because the caller
+ *     should know vmalloc() area is valid and can use memcpy().
+ *     This is for routines which have to access vmalloc area without
+ *     any informaion, as /dev/kmem.
+ *
+ */
+
 long vread(char *buf, char *addr, unsigned long count)
 {
        struct vm_struct *tmp;
        char *vaddr, *buf_start = buf;
+       unsigned long buflen = count;
        unsigned long n;
 
        /* Don't allow overflow */
@@ -1652,7 +1764,7 @@ long vread(char *buf, char *addr, unsigned long count)
                count = -(unsigned long) addr;
 
        read_lock(&vmlist_lock);
-       for (tmp = vmlist; tmp; tmp = tmp->next) {
+       for (tmp = vmlist; count && tmp; tmp = tmp->next) {
                vaddr = (char *) tmp->addr;
                if (addr >= vaddr + tmp->size - PAGE_SIZE)
                        continue;
@@ -1665,32 +1777,72 @@ long vread(char *buf, char *addr, unsigned long count)
                        count--;
                }
                n = vaddr + tmp->size - PAGE_SIZE - addr;
-               do {
-                       if (count == 0)
-                               goto finished;
-                       *buf = *addr;
-                       buf++;
-                       addr++;
-                       count--;
-               } while (--n > 0);
+               if (n > count)
+                       n = count;
+               if (!(tmp->flags & VM_IOREMAP))
+                       aligned_vread(buf, addr, n);
+               else /* IOREMAP area is treated as memory hole */
+                       memset(buf, 0, n);
+               buf += n;
+               addr += n;
+               count -= n;
        }
 finished:
        read_unlock(&vmlist_lock);
-       return buf - buf_start;
+
+       if (buf == buf_start)
+               return 0;
+       /* zero-fill memory holes */
+       if (buf != buf_start + buflen)
+               memset(buf, 0, buflen - (buf - buf_start));
+
+       return buflen;
 }
 
+/**
+ *     vwrite() -  write vmalloc area in a safe way.
+ *     @buf:           buffer for source data
+ *     @addr:          vm address.
+ *     @count:         number of bytes to be read.
+ *
+ *     Returns # of bytes which addr and buf should be incresed.
+ *     (same number to @count).
+ *     If [addr...addr+count) doesn't includes any intersect with valid
+ *     vmalloc area, returns 0.
+ *
+ *     This function checks that addr is a valid vmalloc'ed area, and
+ *     copy data from a buffer to the given addr. If specified range of
+ *     [addr...addr+count) includes some valid address, data is copied from
+ *     proper area of @buf. If there are memory holes, no copy to hole.
+ *     IOREMAP area is treated as memory hole and no copy is done.
+ *
+ *     If [addr...addr+count) doesn't includes any intersects with alive
+ *     vm_struct area, returns 0.
+ *     @buf should be kernel's buffer. Because this function uses KM_USER0,
+ *     the caller should guarantee KM_USER0 is not used.
+ *
+ *     Note: In usual ops, vwrite() is never necessary because the caller
+ *     should know vmalloc() area is valid and can use memcpy().
+ *     This is for routines which have to access vmalloc area without
+ *     any informaion, as /dev/kmem.
+ *
+ *     The caller should guarantee KM_USER1 is not used.
+ */
+
 long vwrite(char *buf, char *addr, unsigned long count)
 {
        struct vm_struct *tmp;
-       char *vaddr, *buf_start = buf;
-       unsigned long n;
+       char *vaddr;
+       unsigned long n, buflen;
+       int copied = 0;
 
        /* Don't allow overflow */
        if ((unsigned long) addr + count < count)
                count = -(unsigned long) addr;
+       buflen = count;
 
        read_lock(&vmlist_lock);
-       for (tmp = vmlist; tmp; tmp = tmp->next) {
+       for (tmp = vmlist; count && tmp; tmp = tmp->next) {
                vaddr = (char *) tmp->addr;
                if (addr >= vaddr + tmp->size - PAGE_SIZE)
                        continue;
@@ -1702,18 +1854,21 @@ long vwrite(char *buf, char *addr, unsigned long count)
                        count--;
                }
                n = vaddr + tmp->size - PAGE_SIZE - addr;
-               do {
-                       if (count == 0)
-                               goto finished;
-                       *addr = *buf;
-                       buf++;
-                       addr++;
-                       count--;
-               } while (--n > 0);
+               if (n > count)
+                       n = count;
+               if (!(tmp->flags & VM_IOREMAP)) {
+                       aligned_vwrite(buf, addr, n);
+                       copied++;
+               }
+               buf += n;
+               addr += n;
+               count -= n;
        }
 finished:
        read_unlock(&vmlist_lock);
-       return buf - buf_start;
+       if (!copied)
+               return 0;
+       return buflen;
 }
 
 /**