2739 lines · 69.0 KB · C
RawDownload
/* *  linux/mm/vmalloc.c * *  Copyright (C) 1993  Linus Torvalds *  Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 *  SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000 *  Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002 *  Numa awareness, Christoph Lameter, SGI, June 2005 */ #include <linux/vmalloc.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/highmem.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/debugobjects.h>#include <linux/kallsyms.h>#include <linux/list.h>#include <linux/rbtree.h>#include <linux/radix-tree.h>#include <linux/rcupdate.h>#include <linux/pfn.h>#include <linux/kmemleak.h>#include <linux/atomic.h>#include <linux/compiler.h>#include <linux/llist.h>#include <linux/bitops.h> #include <asm/uaccess.h>#include <asm/tlbflush.h>#include <asm/shmparam.h> struct vfree_deferred {	struct llist_head list;	struct work_struct wq;};static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred); static void __vunmap(const void *, int); static void free_work(struct work_struct *w){	struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);	struct llist_node *llnode = llist_del_all(&p->list);	while (llnode) {		void *p = llnode;		llnode = llist_next(llnode);		__vunmap(p, 1);	}} /*** Page table manipulation functions ***/ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end){	pte_t *pte; 	pte = pte_offset_kernel(pmd, addr);	do {		pte_t ptent = ptep_get_and_clear(&init_mm, addr, pte);		WARN_ON(!pte_none(ptent) && !pte_present(ptent));	} while (pte++, addr += PAGE_SIZE, addr != end);} static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end){	pmd_t *pmd;	unsigned long next; 	pmd = pmd_offset(pud, addr);	do {		next = pmd_addr_end(addr, end);		if (pmd_clear_huge(pmd))			continue;		if (pmd_none_or_clear_bad(pmd))			continue;		vunmap_pte_range(pmd, addr, next);	} while (pmd++, addr = next, addr != end);} static void vunmap_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end){	pud_t *pud;	unsigned long next; 	pud = pud_offset(pgd, addr);	do {		next = pud_addr_end(addr, end);		if (pud_clear_huge(pud))			continue;		if (pud_none_or_clear_bad(pud))			continue;		vunmap_pmd_range(pud, addr, next);	} while (pud++, addr = next, addr != end);} static void vunmap_page_range(unsigned long addr, unsigned long end){	pgd_t *pgd;	unsigned long next; 	BUG_ON(addr >= end);	pgd = pgd_offset_k(addr);	do {		next = pgd_addr_end(addr, end);		if (pgd_none_or_clear_bad(pgd))			continue;		vunmap_pud_range(pgd, addr, next);	} while (pgd++, addr = next, addr != end);} static int vmap_pte_range(pmd_t *pmd, unsigned long addr,		unsigned long end, pgprot_t prot, struct page **pages, int *nr){	pte_t *pte; 	/*	 * nr is a running index into the array which helps higher level	 * callers keep track of where we're up to.	 */ 	pte = pte_alloc_kernel(pmd, addr);	if (!pte)		return -ENOMEM;	do {		struct page *page = pages[*nr]; 		if (WARN_ON(!pte_none(*pte)))			return -EBUSY;		if (WARN_ON(!page))			return -ENOMEM;		set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));		(*nr)++;	} while (pte++, addr += PAGE_SIZE, addr != end);	return 0;} static int vmap_pmd_range(pud_t *pud, unsigned long addr,		unsigned long end, pgprot_t prot, struct page **pages, int *nr){	pmd_t *pmd;	unsigned long next; 	pmd = pmd_alloc(&init_mm, pud, addr);	if (!pmd)		return -ENOMEM;	do {		next = pmd_addr_end(addr, end);		if (vmap_pte_range(pmd, addr, next, prot, pages, nr))			return -ENOMEM;	} while (pmd++, addr = next, addr != end);	return 0;} static int vmap_pud_range(pgd_t *pgd, unsigned long addr,		unsigned long end, pgprot_t prot, struct page **pages, int *nr){	pud_t *pud;	unsigned long next; 	pud = pud_alloc(&init_mm, pgd, addr);	if (!pud)		return -ENOMEM;	do {		next = pud_addr_end(addr, end);		if (vmap_pmd_range(pud, addr, next, prot, pages, nr))			return -ENOMEM;	} while (pud++, addr = next, addr != end);	return 0;} /* * Set up page tables in kva (addr, end). The ptes shall have prot "prot", and * will have pfns corresponding to the "pages" array. * * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N] */static int vmap_page_range_noflush(unsigned long start, unsigned long end,				   pgprot_t prot, struct page **pages){	pgd_t *pgd;	unsigned long next;	unsigned long addr = start;	int err = 0;	int nr = 0; 	BUG_ON(addr >= end);	pgd = pgd_offset_k(addr);	do {		next = pgd_addr_end(addr, end);		err = vmap_pud_range(pgd, addr, next, prot, pages, &nr);		if (err)			return err;	} while (pgd++, addr = next, addr != end); 	return nr;} static int vmap_page_range(unsigned long start, unsigned long end,			   pgprot_t prot, struct page **pages){	int ret; 	ret = vmap_page_range_noflush(start, end, prot, pages);	flush_cache_vmap(start, end);	return ret;} int is_vmalloc_or_module_addr(const void *x){	/*	 * ARM, x86-64 and sparc64 put modules in a special place,	 * and fall back on vmalloc() if that fails. Others	 * just put it in the vmalloc space.	 */#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)	unsigned long addr = (unsigned long)x;	if (addr >= MODULES_VADDR && addr < MODULES_END)		return 1;#endif	return is_vmalloc_addr(x);} /* * Walk a vmap address to the struct page it maps. */struct page *vmalloc_to_page(const void *vmalloc_addr){	unsigned long addr = (unsigned long) vmalloc_addr;	struct page *page = NULL;	pgd_t *pgd = pgd_offset_k(addr); 	/*	 * XXX we might need to change this if we add VIRTUAL_BUG_ON for	 * architectures that do not vmalloc module space	 */	VIRTUAL_BUG_ON(!is_vmalloc_or_module_addr(vmalloc_addr)); 	if (!pgd_none(*pgd)) {		pud_t *pud = pud_offset(pgd, addr);		if (!pud_none(*pud)) {			pmd_t *pmd = pmd_offset(pud, addr);			if (!pmd_none(*pmd)) {				pte_t *ptep, pte; 				ptep = pte_offset_map(pmd, addr);				pte = *ptep;				if (pte_present(pte))					page = pte_page(pte);				pte_unmap(ptep);			}		}	}	return page;}EXPORT_SYMBOL(vmalloc_to_page); /* * Map a vmalloc()-space virtual address to the physical page frame number. */unsigned long vmalloc_to_pfn(const void *vmalloc_addr){	return page_to_pfn(vmalloc_to_page(vmalloc_addr));}EXPORT_SYMBOL(vmalloc_to_pfn);  /*** Global kva allocator ***/ #define VM_LAZY_FREE	0x01#define VM_LAZY_FREEING	0x02#define VM_VM_AREA	0x04 static DEFINE_SPINLOCK(vmap_area_lock);/* Export for kexec only */LIST_HEAD(vmap_area_list);static struct rb_root vmap_area_root = RB_ROOT; /* The vmap cache globals are protected by vmap_area_lock */static struct rb_node *free_vmap_cache;static unsigned long cached_hole_size;static unsigned long cached_vstart;static unsigned long cached_align; static unsigned long vmap_area_pcpu_hole; static struct vmap_area *__find_vmap_area(unsigned long addr){	struct rb_node *n = vmap_area_root.rb_node; 	while (n) {		struct vmap_area *va; 		va = rb_entry(n, struct vmap_area, rb_node);		if (addr < va->va_start)			n = n->rb_left;		else if (addr >= va->va_end)			n = n->rb_right;		else			return va;	} 	return NULL;} static void __insert_vmap_area(struct vmap_area *va){	struct rb_node **p = &vmap_area_root.rb_node;	struct rb_node *parent = NULL;	struct rb_node *tmp; 	while (*p) {		struct vmap_area *tmp_va; 		parent = *p;		tmp_va = rb_entry(parent, struct vmap_area, rb_node);		if (va->va_start < tmp_va->va_end)			p = &(*p)->rb_left;		else if (va->va_end > tmp_va->va_start)			p = &(*p)->rb_right;		else			BUG();	} 	rb_link_node(&va->rb_node, parent, p);	rb_insert_color(&va->rb_node, &vmap_area_root); 	/* address-sort this list */	tmp = rb_prev(&va->rb_node);	if (tmp) {		struct vmap_area *prev;		prev = rb_entry(tmp, struct vmap_area, rb_node);		list_add_rcu(&va->list, &prev->list);	} else		list_add_rcu(&va->list, &vmap_area_list);} static void purge_vmap_area_lazy(void); /* * Allocate a region of KVA of the specified size and alignment, within the * vstart and vend. */static struct vmap_area *alloc_vmap_area(unsigned long size,				unsigned long align,				unsigned long vstart, unsigned long vend,				int node, gfp_t gfp_mask){	struct vmap_area *va;	struct rb_node *n;	unsigned long addr;	int purged = 0;	struct vmap_area *first; 	BUG_ON(!size);	BUG_ON(size & ~PAGE_MASK);	BUG_ON(!is_power_of_2(align)); 	va = kmalloc_node(sizeof(struct vmap_area),			gfp_mask & GFP_RECLAIM_MASK, node);	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);	/*	 * Invalidate cache if we have more permissive parameters.	 * cached_hole_size notes the largest hole noticed _below_	 * the vmap_area cached in free_vmap_cache: if size fits	 * into that hole, we want to scan from vstart to reuse	 * the hole instead of allocating above free_vmap_cache.	 * Note that __free_vmap_area may update free_vmap_cache	 * without updating cached_hole_size or cached_align.	 */	if (!free_vmap_cache ||			size < cached_hole_size ||			vstart < cached_vstart ||			align < cached_align) {nocache:		cached_hole_size = 0;		free_vmap_cache = NULL;	}	/* record if we encounter less permissive parameters */	cached_vstart = vstart;	cached_align = align; 	/* find starting point for our search */	if (free_vmap_cache) {		first = rb_entry(free_vmap_cache, struct vmap_area, rb_node);		addr = ALIGN(first->va_end, align);		if (addr < vstart)			goto nocache;		if (addr + size < addr)			goto overflow; 	} else {		addr = ALIGN(vstart, align);		if (addr + size < addr)			goto overflow; 		n = vmap_area_root.rb_node;		first = NULL; 		while (n) {			struct vmap_area *tmp;			tmp = rb_entry(n, struct vmap_area, rb_node);			if (tmp->va_end >= addr) {				first = tmp;				if (tmp->va_start <= addr)					break;				n = n->rb_left;			} else				n = n->rb_right;		} 		if (!first)			goto found;	} 	/* from the starting point, walk areas until a suitable hole is found */	while (addr + size > first->va_start && addr + size <= vend) {		if (addr + cached_hole_size < first->va_start)			cached_hole_size = first->va_start - addr;		addr = ALIGN(first->va_end, align);		if (addr + size < addr)			goto overflow; 		if (list_is_last(&first->list, &vmap_area_list))			goto found; 		first = list_entry(first->list.next,				struct vmap_area, list);	} found:	if (addr + size > vend)		goto overflow; 	va->va_start = addr;	va->va_end = addr + size;	va->flags = 0;	__insert_vmap_area(va);	free_vmap_cache = &va->rb_node;	spin_unlock(&vmap_area_lock); 	BUG_ON(va->va_start & (align-1));	BUG_ON(va->va_start < vstart);	BUG_ON(va->va_end > vend); 	return va; overflow:	spin_unlock(&vmap_area_lock);	if (!purged) {		purge_vmap_area_lazy();		purged = 1;		goto retry;	}	if (printk_ratelimit())		pr_warn("vmap allocation for size %lu failed: "			"use vmalloc=<size> to increase size.\n", size);	kfree(va);	return ERR_PTR(-EBUSY);} static void __free_vmap_area(struct vmap_area *va){	BUG_ON(RB_EMPTY_NODE(&va->rb_node)); 	if (free_vmap_cache) {		if (va->va_end < cached_vstart) {			free_vmap_cache = NULL;		} else {			struct vmap_area *cache;			cache = rb_entry(free_vmap_cache, struct vmap_area, rb_node);			if (va->va_start <= cache->va_start) {				free_vmap_cache = rb_prev(&va->rb_node);				/*				 * We don't try to update cached_hole_size or				 * cached_align, but it won't go very wrong.				 */			}		}	}	rb_erase(&va->rb_node, &vmap_area_root);	RB_CLEAR_NODE(&va->rb_node);	list_del_rcu(&va->list); 	/*

Lines 1–500 of 2739.