]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - mm/vmalloc.c
revert "mm: vmalloc use mutex for purge"
[linux-2.6-omap-h63xx.git] / mm / vmalloc.c
index 7465f22fec0cb800187ce0a98e8a80d616ce9fc8..75f49d312e8c1d47648f3e96b8a1eb6d14076405 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/rbtree.h>
 #include <linux/radix-tree.h>
 #include <linux/rcupdate.h>
+#include <linux/bootmem.h>
 
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
@@ -381,8 +382,9 @@ found:
                        goto retry;
                }
                if (printk_ratelimit())
-                       printk(KERN_WARNING "vmap allocation failed: "
-                                "use vmalloc=<size> to increase size.\n");
+                       printk(KERN_WARNING
+                               "vmap allocation for size %lu failed: "
+                               "use vmalloc=<size> to increase size.\n", size);
                return ERR_PTR(-EBUSY);
        }
 
@@ -432,6 +434,27 @@ static void unmap_vmap_area(struct vmap_area *va)
        vunmap_page_range(va->va_start, va->va_end);
 }
 
+static void vmap_debug_free_range(unsigned long start, unsigned long end)
+{
+       /*
+        * Unmap page tables and force a TLB flush immediately if
+        * CONFIG_DEBUG_PAGEALLOC is set. This catches use after free
+        * bugs similarly to those in linear kernel virtual address
+        * space after a page has been freed.
+        *
+        * All the lazy freeing logic is still retained, in order to
+        * minimise intrusiveness of this debugging feature.
+        *
+        * This is going to be *slow* (linear kernel virtual address
+        * debugging doesn't do a broadcast TLB flush so it is a lot
+        * faster).
+        */
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       vunmap_page_range(start, end);
+       flush_tlb_kernel_range(start, end);
+#endif
+}
+
 /*
  * lazy_max_pages is the maximum amount of virtual address space we gather up
  * before attempting to purge with a TLB flush.
@@ -912,6 +935,7 @@ void vm_unmap_ram(const void *mem, unsigned int count)
        BUG_ON(addr & (PAGE_SIZE-1));
 
        debug_check_no_locks_freed(mem, size);
+       vmap_debug_free_range(addr, addr+size);
 
        if (likely(count <= VMAP_MAX_ALLOC))
                vb_free(mem, size);
@@ -960,6 +984,8 @@ EXPORT_SYMBOL(vm_map_ram);
 
 void __init vmalloc_init(void)
 {
+       struct vmap_area *va;
+       struct vm_struct *tmp;
        int i;
 
        for_each_possible_cpu(i) {
@@ -972,6 +998,14 @@ void __init vmalloc_init(void)
                vbq->nr_dirty = 0;
        }
 
+       /* Import existing vmlist entries. */
+       for (tmp = vmlist; tmp; tmp = tmp->next) {
+               va = alloc_bootmem(sizeof(struct vmap_area));
+               va->flags = tmp->flags | VM_VM_AREA;
+               va->va_start = (unsigned long)tmp->addr;
+               va->va_end = va->va_start + tmp->size;
+               __insert_vmap_area(va);
+       }
        vmap_initialized = true;
 }
 
@@ -1128,6 +1162,8 @@ 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;
 
@@ -1375,7 +1411,8 @@ void *vmalloc_user(unsigned long size)
        struct vm_struct *area;
        void *ret;
 
-       ret = __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
+       ret = __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
+                            PAGE_KERNEL, -1, __builtin_return_address(0));
        if (ret) {
                area = find_vm_area(ret);
                area->flags |= VM_USERMAP;
@@ -1420,7 +1457,8 @@ EXPORT_SYMBOL(vmalloc_node);
 
 void *vmalloc_exec(unsigned long size)
 {
-       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC);
+       return __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
+                             -1, __builtin_return_address(0));
 }
 
 #if defined(CONFIG_64BIT) && defined(CONFIG_ZONE_DMA32)
@@ -1440,7 +1478,8 @@ void *vmalloc_exec(unsigned long size)
  */
 void *vmalloc_32(unsigned long size)
 {
-       return __vmalloc(size, GFP_VMALLOC32, PAGE_KERNEL);
+       return __vmalloc_node(size, GFP_VMALLOC32, PAGE_KERNEL,
+                             -1, __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vmalloc_32);
 
@@ -1456,7 +1495,8 @@ void *vmalloc_32_user(unsigned long size)
        struct vm_struct *area;
        void *ret;
 
-       ret = __vmalloc(size, GFP_VMALLOC32 | __GFP_ZERO, PAGE_KERNEL);
+       ret = __vmalloc_node(size, GFP_VMALLOC32 | __GFP_ZERO, PAGE_KERNEL,
+                            -1, __builtin_return_address(0));
        if (ret) {
                area = find_vm_area(ret);
                area->flags |= VM_USERMAP;