diff options
Diffstat (limited to 'mm/vmalloc.c')
| -rw-r--r-- | mm/vmalloc.c | 73 | 
1 files changed, 58 insertions, 15 deletions
| diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d2a00ad4e1dd..4165304d3547 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -31,6 +31,7 @@  #include <linux/kmemleak.h>  #include <linux/atomic.h>  #include <linux/compiler.h> +#include <linux/memcontrol.h>  #include <linux/llist.h>  #include <linux/bitops.h>  #include <linux/rbtree_augmented.h> @@ -38,6 +39,7 @@  #include <linux/pgtable.h>  #include <linux/uaccess.h>  #include <linux/hugetlb.h> +#include <linux/sched/mm.h>  #include <asm/tlbflush.h>  #include <asm/shmparam.h> @@ -2623,12 +2625,13 @@ static void __vunmap(const void *addr, int deallocate_pages)  	if (deallocate_pages) {  		unsigned int page_order = vm_area_page_order(area); -		int i; +		int i, step = 1U << page_order; -		for (i = 0; i < area->nr_pages; i += 1U << page_order) { +		for (i = 0; i < area->nr_pages; i += step) {  			struct page *page = area->pages[i];  			BUG_ON(!page); +			mod_memcg_page_state(page, MEMCG_VMALLOC, -step);  			__free_pages(page, page_order);  			cond_resched();  		} @@ -2844,6 +2847,8 @@ vm_area_alloc_pages(gfp_t gfp, int nid,  	 * more permissive.  	 */  	if (!order) { +		gfp_t bulk_gfp = gfp & ~__GFP_NOFAIL; +  		while (nr_allocated < nr_pages) {  			unsigned int nr, nr_pages_request; @@ -2861,12 +2866,12 @@ vm_area_alloc_pages(gfp_t gfp, int nid,  			 * but mempolcy want to alloc memory by interleaving.  			 */  			if (IS_ENABLED(CONFIG_NUMA) && nid == NUMA_NO_NODE) -				nr = alloc_pages_bulk_array_mempolicy(gfp, +				nr = alloc_pages_bulk_array_mempolicy(bulk_gfp,  							nr_pages_request,  							pages + nr_allocated);  			else -				nr = alloc_pages_bulk_array_node(gfp, nid, +				nr = alloc_pages_bulk_array_node(bulk_gfp, nid,  							nr_pages_request,  							pages + nr_allocated); @@ -2921,11 +2926,14 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  {  	const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;  	const gfp_t orig_gfp_mask = gfp_mask; +	bool nofail = gfp_mask & __GFP_NOFAIL;  	unsigned long addr = (unsigned long)area->addr;  	unsigned long size = get_vm_area_size(area);  	unsigned long array_size;  	unsigned int nr_small_pages = size >> PAGE_SHIFT;  	unsigned int page_order; +	unsigned int flags; +	int ret;  	array_size = (unsigned long)nr_small_pages * sizeof(struct page *);  	gfp_mask |= __GFP_NOWARN; @@ -2955,6 +2963,13 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  		page_order, nr_small_pages, area->pages);  	atomic_long_add(area->nr_pages, &nr_vmalloc_pages); +	if (gfp_mask & __GFP_ACCOUNT) { +		int i, step = 1U << page_order; + +		for (i = 0; i < area->nr_pages; i += step) +			mod_memcg_page_state(area->pages[i], MEMCG_VMALLOC, +					     step); +	}  	/*  	 * If not enough pages were obtained to accomplish an @@ -2967,8 +2982,28 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  		goto fail;  	} -	if (vmap_pages_range(addr, addr + size, prot, area->pages, -			page_shift) < 0) { +	/* +	 * page tables allocations ignore external gfp mask, enforce it +	 * by the scope API +	 */ +	if ((gfp_mask & (__GFP_FS | __GFP_IO)) == __GFP_IO) +		flags = memalloc_nofs_save(); +	else if ((gfp_mask & (__GFP_FS | __GFP_IO)) == 0) +		flags = memalloc_noio_save(); + +	do { +		ret = vmap_pages_range(addr, addr + size, prot, area->pages, +			page_shift); +		if (nofail && (ret < 0)) +			schedule_timeout_uninterruptible(1); +	} while (nofail && (ret < 0)); + +	if ((gfp_mask & (__GFP_FS | __GFP_IO)) == __GFP_IO) +		memalloc_nofs_restore(flags); +	else if ((gfp_mask & (__GFP_FS | __GFP_IO)) == 0) +		memalloc_noio_restore(flags); + +	if (ret < 0) {  		warn_alloc(orig_gfp_mask, NULL,  			"vmalloc error: size %lu, failed to map pages",  			area->nr_pages * PAGE_SIZE); @@ -2996,12 +3031,14 @@ fail:   *   * Allocate enough pages to cover @size from the page level   * allocator with @gfp_mask flags. Please note that the full set of gfp - * flags are not supported. GFP_KERNEL would be a preferred allocation mode - * but GFP_NOFS and GFP_NOIO are supported as well. Zone modifiers are not - * supported. From the reclaim modifiers__GFP_DIRECT_RECLAIM is required (aka - * GFP_NOWAIT is not supported) and only __GFP_NOFAIL is supported (aka - * __GFP_NORETRY and __GFP_RETRY_MAYFAIL are not supported). - * __GFP_NOWARN can be used to suppress error messages about failures. + * flags are not supported. GFP_KERNEL, GFP_NOFS and GFP_NOIO are all + * supported. + * Zone modifiers are not supported. From the reclaim modifiers + * __GFP_DIRECT_RECLAIM is required (aka GFP_NOWAIT is not supported) + * and only __GFP_NOFAIL is supported (i.e. __GFP_NORETRY and + * __GFP_RETRY_MAYFAIL are not supported). + * + * __GFP_NOWARN can be used to suppress failures messages.   *   * Map them into contiguous kernel virtual space, using a pagetable   * protection of @prot. @@ -3056,9 +3093,14 @@ again:  				  VM_UNINITIALIZED | vm_flags, start, end, node,  				  gfp_mask, caller);  	if (!area) { +		bool nofail = gfp_mask & __GFP_NOFAIL;  		warn_alloc(gfp_mask, NULL, -			"vmalloc error: size %lu, vm_struct allocation failed", -			real_size); +			"vmalloc error: size %lu, vm_struct allocation failed%s", +			real_size, (nofail) ? ". Retrying." : ""); +		if (nofail) { +			schedule_timeout_uninterruptible(1); +			goto again; +		}  		goto fail;  	} @@ -3074,7 +3116,8 @@ again:  	clear_vm_uninitialized_flag(area);  	size = PAGE_ALIGN(size); -	kmemleak_vmalloc(area, size, gfp_mask); +	if (!(vm_flags & VM_DEFER_KMEMLEAK)) +		kmemleak_vmalloc(area, size, gfp_mask);  	return addr; |