diff options
Diffstat (limited to 'mm/vmalloc.c')
| -rw-r--r-- | mm/vmalloc.c | 71 | 
1 files changed, 41 insertions, 30 deletions
diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 107454312d5..f64632b6719 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -27,7 +27,9 @@  #include <linux/pfn.h>  #include <linux/kmemleak.h>  #include <linux/atomic.h> +#include <linux/compiler.h>  #include <linux/llist.h> +  #include <asm/uaccess.h>  #include <asm/tlbflush.h>  #include <asm/shmparam.h> @@ -359,6 +361,12 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,  	if (unlikely(!va))  		return ERR_PTR(-ENOMEM); +	/* +	 * Only scan the relevant parts containing pointers to other objects +	 * to avoid false negatives. +	 */ +	kmemleak_scan_area(&va->rb_node, SIZE_MAX, gfp_mask & GFP_RECLAIM_MASK); +  retry:  	spin_lock(&vmap_area_lock);  	/* @@ -1077,6 +1085,12 @@ EXPORT_SYMBOL(vm_unmap_ram);   * @node: prefer to allocate data structures on this node   * @prot: memory protection to use. PAGE_KERNEL for regular RAM   * + * If you use this function for less than VMAP_MAX_ALLOC pages, it could be + * faster than vmap so it's good.  But if you mix long-life and short-life + * objects with vm_map_ram(), it could consume lots of address space through + * fragmentation (especially on a 32bit machine).  You could see failures in + * the end.  Please use this function for short-lived objects. + *   * Returns: a pointer to the address that has been mapped, or %NULL on failure   */  void *vm_map_ram(struct page **pages, unsigned int count, int node, pgprot_t prot) @@ -1254,6 +1268,7 @@ void unmap_kernel_range(unsigned long addr, unsigned long size)  	vunmap_page_range(addr, end);  	flush_tlb_kernel_range(addr, end);  } +EXPORT_SYMBOL_GPL(unmap_kernel_range);  int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)  { @@ -1482,7 +1497,7 @@ void vfree(const void *addr)  	if (!addr)  		return;  	if (unlikely(in_interrupt())) { -		struct vfree_deferred *p = &__get_cpu_var(vfree_deferred); +		struct vfree_deferred *p = this_cpu_ptr(&vfree_deferred);  		if (llist_add((struct llist_node *)addr, &p->list))  			schedule_work(&p->wq);  	} else @@ -1546,7 +1561,7 @@ static void *__vmalloc_node(unsigned long size, unsigned long align,  			    gfp_t gfp_mask, pgprot_t prot,  			    int node, const void *caller);  static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, -				 pgprot_t prot, int node, const void *caller) +				 pgprot_t prot, int node)  {  	const int order = 0;  	struct page **pages; @@ -1560,13 +1575,12 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  	/* Please note that the recursion is strictly bounded. */  	if (array_size > PAGE_SIZE) {  		pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM, -				PAGE_KERNEL, node, caller); +				PAGE_KERNEL, node, area->caller);  		area->flags |= VM_VPAGES;  	} else {  		pages = kmalloc_node(array_size, nested_gfp, node);  	}  	area->pages = pages; -	area->caller = caller;  	if (!area->pages) {  		remove_vm_area(area->addr);  		kfree(area); @@ -1577,7 +1591,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  		struct page *page;  		gfp_t tmp_mask = gfp_mask | __GFP_NOWARN; -		if (node < 0) +		if (node == NUMA_NO_NODE)  			page = alloc_page(tmp_mask);  		else  			page = alloc_pages_node(node, tmp_mask, order); @@ -1634,9 +1648,9 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,  	if (!area)  		goto fail; -	addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller); +	addr = __vmalloc_area_node(area, gfp_mask, prot, node);  	if (!addr) -		goto fail; +		return NULL;  	/*  	 * In this function, newly allocated vm_struct has VM_UNINITIALIZED @@ -1646,11 +1660,11 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,  	clear_vm_uninitialized_flag(area);  	/* -	 * A ref_count = 3 is needed because the vm_struct and vmap_area -	 * structures allocated in the __get_vm_area_node() function contain -	 * references to the virtual address of the vmalloc'ed block. +	 * A ref_count = 2 is needed because vm_struct allocated in +	 * __get_vm_area_node() contains a reference to the virtual address of +	 * the vmalloc'ed block.  	 */ -	kmemleak_alloc(addr, real_size, 3, gfp_mask); +	kmemleak_alloc(addr, real_size, 2, gfp_mask);  	return addr; @@ -2176,7 +2190,7 @@ EXPORT_SYMBOL(remap_vmalloc_range);   * Implement a stub for vmalloc_sync_all() if the architecture chose not to   * have one.   */ -void  __attribute__((weak)) vmalloc_sync_all(void) +void __weak vmalloc_sync_all(void)  {  } @@ -2563,6 +2577,11 @@ static void show_numa_info(struct seq_file *m, struct vm_struct *v)  		if (!counters)  			return; +		/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */ +		smp_rmb(); +		if (v->flags & VM_UNINITIALIZED) +			return; +  		memset(counters, 0, nr_node_ids * sizeof(unsigned int));  		for (nr = 0; nr < v->nr_pages; nr++) @@ -2579,23 +2598,15 @@ static int s_show(struct seq_file *m, void *p)  	struct vmap_area *va = p;  	struct vm_struct *v; -	if (va->flags & (VM_LAZY_FREE | VM_LAZY_FREEING)) -		return 0; - -	if (!(va->flags & VM_VM_AREA)) { -		seq_printf(m, "0x%pK-0x%pK %7ld vm_map_ram\n", -			(void *)va->va_start, (void *)va->va_end, -					va->va_end - va->va_start); +	/* +	 * s_show can encounter race with remove_vm_area, !VM_VM_AREA on +	 * behalf of vmap area is being tear down or vm_map_ram allocation. +	 */ +	if (!(va->flags & VM_VM_AREA))  		return 0; -	}  	v = va->vm; -	/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */ -	smp_rmb(); -	if (v->flags & VM_UNINITIALIZED) -		return 0; -  	seq_printf(m, "0x%pK-0x%pK %7ld",  		v->addr, v->addr + v->size, v->size); @@ -2609,19 +2620,19 @@ static int s_show(struct seq_file *m, void *p)  		seq_printf(m, " phys=%llx", (unsigned long long)v->phys_addr);  	if (v->flags & VM_IOREMAP) -		seq_printf(m, " ioremap"); +		seq_puts(m, " ioremap");  	if (v->flags & VM_ALLOC) -		seq_printf(m, " vmalloc"); +		seq_puts(m, " vmalloc");  	if (v->flags & VM_MAP) -		seq_printf(m, " vmap"); +		seq_puts(m, " vmap");  	if (v->flags & VM_USERMAP) -		seq_printf(m, " user"); +		seq_puts(m, " user");  	if (v->flags & VM_VPAGES) -		seq_printf(m, " vpages"); +		seq_puts(m, " vpages");  	show_numa_info(m, v);  	seq_putc(m, '\n');  | 
