mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +02:00 
			
		
		
		
	mm/vmalloc: hugepage vmalloc mappings
Support huge page vmalloc mappings. Config option HAVE_ARCH_HUGE_VMALLOC enables support on architectures that define HAVE_ARCH_HUGE_VMAP and supports PMD sized vmap mappings. vmalloc will attempt to allocate PMD-sized pages if allocating PMD size or larger, and fall back to small pages if that was unsuccessful. Architectures must ensure that any arch specific vmalloc allocations that require PAGE_SIZE mappings (e.g., module allocations vs strict module rwx) use the VM_NOHUGE flag to inhibit larger mappings. This can result in more internal fragmentation and memory overhead for a given allocation, an option nohugevmalloc is added to disable at boot. [colin.king@canonical.com: fix read of uninitialized pointer area] Link: https://lkml.kernel.org/r/20210318155955.18220-1-colin.king@canonical.com Link: https://lkml.kernel.org/r/20210317062402.533919-14-npiggin@gmail.com Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Ding Tianhong <dingtianhong@huawei.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Miaohe Lin <linmiaohe@huawei.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Russell King <linux@armlinux.org.uk> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Uladzislau Rezki (Sony) <urezki@gmail.com> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									5d87510de1
								
							
						
					
					
						commit
						121e6f3258
					
				
					 4 changed files with 210 additions and 49 deletions
				
			
		
							
								
								
									
										11
									
								
								arch/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								arch/Kconfig
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -829,6 +829,17 @@ config HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
 | 
			
		|||
config HAVE_ARCH_HUGE_VMAP
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#  Archs that select this would be capable of PMD-sized vmaps (i.e.,
 | 
			
		||||
#  arch_vmap_pmd_supported() returns true), and they must make no assumptions
 | 
			
		||||
#  that vmalloc memory is mapped with PAGE_SIZE ptes. The VM_NO_HUGE_VMAP flag
 | 
			
		||||
#  can be used to prohibit arch-specific allocations from using hugepages to
 | 
			
		||||
#  help with this (e.g., modules may require it).
 | 
			
		||||
#
 | 
			
		||||
config HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
	depends on HAVE_ARCH_HUGE_VMAP
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config ARCH_WANT_HUGE_PMD_SHARE
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ struct notifier_block;		/* in notifier.h */
 | 
			
		|||
#define VM_KASAN		0x00000080      /* has allocated kasan shadow memory */
 | 
			
		||||
#define VM_FLUSH_RESET_PERMS	0x00000100	/* reset direct map and flush TLB on unmap, can't be freed in atomic context */
 | 
			
		||||
#define VM_MAP_PUT_PAGES	0x00000200	/* put pages and free array in vfree */
 | 
			
		||||
#define VM_NO_HUGE_VMAP		0x00000400	/* force PAGE_SIZE pte mapping */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * VM_KASAN is used slighly differently depending on CONFIG_KASAN_VMALLOC.
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +55,9 @@ struct vm_struct {
 | 
			
		|||
	unsigned long		size;
 | 
			
		||||
	unsigned long		flags;
 | 
			
		||||
	struct page		**pages;
 | 
			
		||||
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
	unsigned int		page_order;
 | 
			
		||||
#endif
 | 
			
		||||
	unsigned int		nr_pages;
 | 
			
		||||
	phys_addr_t		phys_addr;
 | 
			
		||||
	const void		*caller;
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +192,22 @@ void free_vm_area(struct vm_struct *area);
 | 
			
		|||
extern struct vm_struct *remove_vm_area(const void *addr);
 | 
			
		||||
extern struct vm_struct *find_vm_area(const void *addr);
 | 
			
		||||
 | 
			
		||||
static inline bool is_vm_area_hugepages(const void *addr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * This may not 100% tell if the area is mapped with > PAGE_SIZE
 | 
			
		||||
	 * page table entries, if for some reason the architecture indicates
 | 
			
		||||
	 * larger sizes are available but decides not to use them, nothing
 | 
			
		||||
	 * prevents that. This only indicates the size of the physical page
 | 
			
		||||
	 * allocated in the vmalloc layer.
 | 
			
		||||
	 */
 | 
			
		||||
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
	return find_vm_area(addr)->page_order > 0;
 | 
			
		||||
#else
 | 
			
		||||
	return false;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MMU
 | 
			
		||||
int vmap_range(unsigned long addr, unsigned long end,
 | 
			
		||||
			phys_addr_t phys_addr, pgprot_t prot,
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +225,7 @@ static inline void set_vm_flush_reset_perms(void *addr)
 | 
			
		|||
	if (vm)
 | 
			
		||||
		vm->flags |= VM_FLUSH_RESET_PERMS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
static inline int
 | 
			
		||||
map_kernel_range_noflush(unsigned long start, unsigned long size,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,6 +72,7 @@
 | 
			
		|||
#include <linux/padata.h>
 | 
			
		||||
#include <linux/khugepaged.h>
 | 
			
		||||
#include <linux/buffer_head.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/tlbflush.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -8222,6 +8223,7 @@ void *__init alloc_large_system_hash(const char *tablename,
 | 
			
		|||
	void *table = NULL;
 | 
			
		||||
	gfp_t gfp_flags;
 | 
			
		||||
	bool virt;
 | 
			
		||||
	bool huge;
 | 
			
		||||
 | 
			
		||||
	/* allow the kernel cmdline to have a say */
 | 
			
		||||
	if (!numentries) {
 | 
			
		||||
| 
						 | 
				
			
			@ -8289,6 +8291,7 @@ void *__init alloc_large_system_hash(const char *tablename,
 | 
			
		|||
		} else if (get_order(size) >= MAX_ORDER || hashdist) {
 | 
			
		||||
			table = __vmalloc(size, gfp_flags);
 | 
			
		||||
			virt = true;
 | 
			
		||||
			huge = is_vm_area_hugepages(table);
 | 
			
		||||
		} else {
 | 
			
		||||
			/*
 | 
			
		||||
			 * If bucketsize is not a power-of-two, we may free
 | 
			
		||||
| 
						 | 
				
			
			@ -8305,7 +8308,7 @@ void *__init alloc_large_system_hash(const char *tablename,
 | 
			
		|||
 | 
			
		||||
	pr_info("%s hash table entries: %ld (order: %d, %lu bytes, %s)\n",
 | 
			
		||||
		tablename, 1UL << log2qty, ilog2(size) - PAGE_SHIFT, size,
 | 
			
		||||
		virt ? "vmalloc" : "linear");
 | 
			
		||||
		virt ? (huge ? "vmalloc hugepage" : "vmalloc") : "linear");
 | 
			
		||||
 | 
			
		||||
	if (_hash_shift)
 | 
			
		||||
		*_hash_shift = log2qty;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										220
									
								
								mm/vmalloc.c
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								mm/vmalloc.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -42,6 +42,19 @@
 | 
			
		|||
#include "internal.h"
 | 
			
		||||
#include "pgalloc-track.h"
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
static bool __ro_after_init vmap_allow_huge = true;
 | 
			
		||||
 | 
			
		||||
static int __init set_nohugevmalloc(char *str)
 | 
			
		||||
{
 | 
			
		||||
	vmap_allow_huge = false;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
early_param("nohugevmalloc", set_nohugevmalloc);
 | 
			
		||||
#else /* CONFIG_HAVE_ARCH_HUGE_VMALLOC */
 | 
			
		||||
static const bool vmap_allow_huge = false;
 | 
			
		||||
#endif	/* CONFIG_HAVE_ARCH_HUGE_VMALLOC */
 | 
			
		||||
 | 
			
		||||
bool is_vmalloc_addr(const void *x)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long addr = (unsigned long)x;
 | 
			
		||||
| 
						 | 
				
			
			@ -483,6 +496,69 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
 | 
			
		||||
		pgprot_t prot, struct page **pages)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = addr;
 | 
			
		||||
	pgd_t *pgd;
 | 
			
		||||
	unsigned long next;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	int nr = 0;
 | 
			
		||||
	pgtbl_mod_mask mask = 0;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(addr >= end);
 | 
			
		||||
	pgd = pgd_offset_k(addr);
 | 
			
		||||
	do {
 | 
			
		||||
		next = pgd_addr_end(addr, end);
 | 
			
		||||
		if (pgd_bad(*pgd))
 | 
			
		||||
			mask |= PGTBL_PGD_MODIFIED;
 | 
			
		||||
		err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, &mask);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
	} while (pgd++, addr = next, addr != end);
 | 
			
		||||
 | 
			
		||||
	if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
 | 
			
		||||
		arch_sync_kernel_mappings(start, end);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
 | 
			
		||||
		pgprot_t prot, struct page **pages, unsigned int page_shift)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
 | 
			
		||||
 | 
			
		||||
	WARN_ON(page_shift < PAGE_SHIFT);
 | 
			
		||||
 | 
			
		||||
	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
 | 
			
		||||
			page_shift == PAGE_SHIFT)
 | 
			
		||||
		return vmap_small_pages_range_noflush(addr, end, prot, pages);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
 | 
			
		||||
		int err;
 | 
			
		||||
 | 
			
		||||
		err = vmap_range_noflush(addr, addr + (1UL << page_shift),
 | 
			
		||||
					__pa(page_address(pages[i])), prot,
 | 
			
		||||
					page_shift);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		addr += 1UL << page_shift;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int vmap_pages_range(unsigned long addr, unsigned long end,
 | 
			
		||||
		pgprot_t prot, struct page **pages, unsigned int page_shift)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
 | 
			
		||||
	flush_cache_vmap(addr, end);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * map_kernel_range_noflush - map kernel VM area with the specified pages
 | 
			
		||||
 * @addr: start of the VM area to map
 | 
			
		||||
| 
						 | 
				
			
			@ -504,29 +580,7 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 | 
			
		|||
int map_kernel_range_noflush(unsigned long addr, unsigned long size,
 | 
			
		||||
			     pgprot_t prot, struct page **pages)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = addr;
 | 
			
		||||
	unsigned long end = addr + size;
 | 
			
		||||
	unsigned long next;
 | 
			
		||||
	pgd_t *pgd;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	int nr = 0;
 | 
			
		||||
	pgtbl_mod_mask mask = 0;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(addr >= end);
 | 
			
		||||
	pgd = pgd_offset_k(addr);
 | 
			
		||||
	do {
 | 
			
		||||
		next = pgd_addr_end(addr, end);
 | 
			
		||||
		if (pgd_bad(*pgd))
 | 
			
		||||
			mask |= PGTBL_PGD_MODIFIED;
 | 
			
		||||
		err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, &mask);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
	} while (pgd++, addr = next, addr != end);
 | 
			
		||||
 | 
			
		||||
	if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
 | 
			
		||||
		arch_sync_kernel_mappings(start, end);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return vmap_pages_range_noflush(addr, addr + size, prot, pages, PAGE_SHIFT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int map_kernel_range(unsigned long start, unsigned long size, pgprot_t prot,
 | 
			
		||||
| 
						 | 
				
			
			@ -2112,6 +2166,24 @@ EXPORT_SYMBOL(vm_map_ram);
 | 
			
		|||
 | 
			
		||||
static struct vm_struct *vmlist __initdata;
 | 
			
		||||
 | 
			
		||||
static inline unsigned int vm_area_page_order(struct vm_struct *vm)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
	return vm->page_order;
 | 
			
		||||
#else
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void set_vm_area_page_order(struct vm_struct *vm, unsigned int order)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
 | 
			
		||||
	vm->page_order = order;
 | 
			
		||||
#else
 | 
			
		||||
	BUG_ON(order != 0);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * vm_area_add_early - add vmap area early during boot
 | 
			
		||||
 * @vm: vm_struct to add
 | 
			
		||||
| 
						 | 
				
			
			@ -2422,6 +2494,7 @@ static inline void set_area_direct_map(const struct vm_struct *area,
 | 
			
		|||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* HUGE_VMALLOC passes small pages to set_direct_map */
 | 
			
		||||
	for (i = 0; i < area->nr_pages; i++)
 | 
			
		||||
		if (page_address(area->pages[i]))
 | 
			
		||||
			set_direct_map(area->pages[i]);
 | 
			
		||||
| 
						 | 
				
			
			@ -2431,6 +2504,7 @@ static inline void set_area_direct_map(const struct vm_struct *area,
 | 
			
		|||
static void vm_remove_mappings(struct vm_struct *area, int deallocate_pages)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = ULONG_MAX, end = 0;
 | 
			
		||||
	unsigned int page_order = vm_area_page_order(area);
 | 
			
		||||
	int flush_reset = area->flags & VM_FLUSH_RESET_PERMS;
 | 
			
		||||
	int flush_dmap = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
| 
						 | 
				
			
			@ -2455,11 +2529,14 @@ static void vm_remove_mappings(struct vm_struct *area, int deallocate_pages)
 | 
			
		|||
	 * map. Find the start and end range of the direct mappings to make sure
 | 
			
		||||
	 * the vm_unmap_aliases() flush includes the direct map.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 0; i < area->nr_pages; i++) {
 | 
			
		||||
	for (i = 0; i < area->nr_pages; i += 1U << page_order) {
 | 
			
		||||
		unsigned long addr = (unsigned long)page_address(area->pages[i]);
 | 
			
		||||
		if (addr) {
 | 
			
		||||
			unsigned long page_size;
 | 
			
		||||
 | 
			
		||||
			page_size = PAGE_SIZE << page_order;
 | 
			
		||||
			start = min(addr, start);
 | 
			
		||||
			end = max(addr + PAGE_SIZE, end);
 | 
			
		||||
			end = max(addr + page_size, end);
 | 
			
		||||
			flush_dmap = 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -2500,13 +2577,14 @@ static void __vunmap(const void *addr, int deallocate_pages)
 | 
			
		|||
	vm_remove_mappings(area, deallocate_pages);
 | 
			
		||||
 | 
			
		||||
	if (deallocate_pages) {
 | 
			
		||||
		unsigned int page_order = vm_area_page_order(area);
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < area->nr_pages; i++) {
 | 
			
		||||
		for (i = 0; i < area->nr_pages; i += 1U << page_order) {
 | 
			
		||||
			struct page *page = area->pages[i];
 | 
			
		||||
 | 
			
		||||
			BUG_ON(!page);
 | 
			
		||||
			__free_pages(page, 0);
 | 
			
		||||
			__free_pages(page, page_order);
 | 
			
		||||
		}
 | 
			
		||||
		atomic_long_sub(area->nr_pages, &nr_vmalloc_pages);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2697,15 +2775,19 @@ EXPORT_SYMBOL_GPL(vmap_pfn);
 | 
			
		|||
#endif /* CONFIG_VMAP_PFN */
 | 
			
		||||
 | 
			
		||||
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
 | 
			
		||||
				 pgprot_t prot, int node)
 | 
			
		||||
				 pgprot_t prot, unsigned int page_shift,
 | 
			
		||||
				 int node)
 | 
			
		||||
{
 | 
			
		||||
	const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
 | 
			
		||||
	unsigned int nr_pages = get_vm_area_size(area) >> PAGE_SHIFT;
 | 
			
		||||
	unsigned long addr = (unsigned long)area->addr;
 | 
			
		||||
	unsigned long size = get_vm_area_size(area);
 | 
			
		||||
	unsigned long array_size;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int nr_small_pages = size >> PAGE_SHIFT;
 | 
			
		||||
	unsigned int page_order;
 | 
			
		||||
	struct page **pages;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	array_size = (unsigned long)nr_pages * sizeof(struct page *);
 | 
			
		||||
	array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
 | 
			
		||||
	gfp_mask |= __GFP_NOWARN;
 | 
			
		||||
	if (!(gfp_mask & (GFP_DMA | GFP_DMA32)))
 | 
			
		||||
		gfp_mask |= __GFP_HIGHMEM;
 | 
			
		||||
| 
						 | 
				
			
			@ -2724,30 +2806,38 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	area->pages = pages;
 | 
			
		||||
	area->nr_pages = nr_pages;
 | 
			
		||||
	area->nr_pages = nr_small_pages;
 | 
			
		||||
	set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < area->nr_pages; i++) {
 | 
			
		||||
	page_order = vm_area_page_order(area);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Careful, we allocate and map page_order pages, but tracking is done
 | 
			
		||||
	 * per PAGE_SIZE page so as to keep the vm_struct APIs independent of
 | 
			
		||||
	 * the physical/mapped size.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 0; i < area->nr_pages; i += 1U << page_order) {
 | 
			
		||||
		struct page *page;
 | 
			
		||||
		int p;
 | 
			
		||||
 | 
			
		||||
		if (node == NUMA_NO_NODE)
 | 
			
		||||
			page = alloc_page(gfp_mask);
 | 
			
		||||
		else
 | 
			
		||||
			page = alloc_pages_node(node, gfp_mask, 0);
 | 
			
		||||
 | 
			
		||||
		/* Compound pages required for remap_vmalloc_page */
 | 
			
		||||
		page = alloc_pages_node(node, gfp_mask | __GFP_COMP, page_order);
 | 
			
		||||
		if (unlikely(!page)) {
 | 
			
		||||
			/* Successfully allocated i pages, free them in __vfree() */
 | 
			
		||||
			area->nr_pages = i;
 | 
			
		||||
			atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
 | 
			
		||||
			goto fail;
 | 
			
		||||
		}
 | 
			
		||||
		area->pages[i] = page;
 | 
			
		||||
 | 
			
		||||
		for (p = 0; p < (1U << page_order); p++)
 | 
			
		||||
			area->pages[i + p] = page + p;
 | 
			
		||||
 | 
			
		||||
		if (gfpflags_allow_blocking(gfp_mask))
 | 
			
		||||
			cond_resched();
 | 
			
		||||
	}
 | 
			
		||||
	atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
 | 
			
		||||
 | 
			
		||||
	if (map_kernel_range((unsigned long)area->addr, get_vm_area_size(area),
 | 
			
		||||
			prot, pages) < 0)
 | 
			
		||||
	if (vmap_pages_range(addr, addr + size, prot, pages, page_shift) < 0)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	return area->addr;
 | 
			
		||||
| 
						 | 
				
			
			@ -2755,7 +2845,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
 | 
			
		|||
fail:
 | 
			
		||||
	warn_alloc(gfp_mask, NULL,
 | 
			
		||||
			  "vmalloc: allocation failure, allocated %ld of %ld bytes",
 | 
			
		||||
			  (area->nr_pages*PAGE_SIZE), area->size);
 | 
			
		||||
			  (area->nr_pages*PAGE_SIZE), size);
 | 
			
		||||
	__vfree(area->addr);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2786,19 +2876,45 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
 | 
			
		|||
	struct vm_struct *area;
 | 
			
		||||
	void *addr;
 | 
			
		||||
	unsigned long real_size = size;
 | 
			
		||||
	unsigned long real_align = align;
 | 
			
		||||
	unsigned int shift = PAGE_SHIFT;
 | 
			
		||||
 | 
			
		||||
	size = PAGE_ALIGN(size);
 | 
			
		||||
	if (!size || (size >> PAGE_SHIFT) > totalram_pages())
 | 
			
		||||
	if (!size || (size >> PAGE_SHIFT) > totalram_pages()) {
 | 
			
		||||
		area = NULL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	area = __get_vm_area_node(real_size, align, VM_ALLOC | VM_UNINITIALIZED |
 | 
			
		||||
	if (vmap_allow_huge && !(vm_flags & VM_NO_HUGE_VMAP) &&
 | 
			
		||||
			arch_vmap_pmd_supported(prot)) {
 | 
			
		||||
		unsigned long size_per_node;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Try huge pages. Only try for PAGE_KERNEL allocations,
 | 
			
		||||
		 * others like modules don't yet expect huge pages in
 | 
			
		||||
		 * their allocations due to apply_to_page_range not
 | 
			
		||||
		 * supporting them.
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		size_per_node = size;
 | 
			
		||||
		if (node == NUMA_NO_NODE)
 | 
			
		||||
			size_per_node /= num_online_nodes();
 | 
			
		||||
		if (size_per_node >= PMD_SIZE) {
 | 
			
		||||
			shift = PMD_SHIFT;
 | 
			
		||||
			align = max(real_align, 1UL << shift);
 | 
			
		||||
			size = ALIGN(real_size, 1UL << shift);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
again:
 | 
			
		||||
	size = PAGE_ALIGN(size);
 | 
			
		||||
	area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED |
 | 
			
		||||
				vm_flags, start, end, node, gfp_mask, caller);
 | 
			
		||||
	if (!area)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	addr = __vmalloc_area_node(area, gfp_mask, prot, node);
 | 
			
		||||
	addr = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
 | 
			
		||||
	if (!addr)
 | 
			
		||||
		return NULL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * In this function, newly allocated vm_struct has VM_UNINITIALIZED
 | 
			
		||||
| 
						 | 
				
			
			@ -2812,8 +2928,18 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
 | 
			
		|||
	return addr;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
	if (shift > PAGE_SHIFT) {
 | 
			
		||||
		shift = PAGE_SHIFT;
 | 
			
		||||
		align = real_align;
 | 
			
		||||
		size = real_size;
 | 
			
		||||
		goto again;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!area) {
 | 
			
		||||
		/* Warn for area allocation, page allocations already warn */
 | 
			
		||||
		warn_alloc(gfp_mask, NULL,
 | 
			
		||||
			  "vmalloc: allocation failure: %lu bytes", real_size);
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue