diff options
Diffstat (limited to 'arch/x86/mm/hugetlbpage.c')
| -rw-r--r-- | arch/x86/mm/hugetlbpage.c | 318 | 
1 files changed, 26 insertions, 292 deletions
diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 069ce7c37c0..8b977ebf938 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -16,159 +16,6 @@  #include <asm/tlbflush.h>  #include <asm/pgalloc.h> -static unsigned long page_table_shareable(struct vm_area_struct *svma, -				struct vm_area_struct *vma, -				unsigned long addr, pgoff_t idx) -{ -	unsigned long saddr = ((idx - svma->vm_pgoff) << PAGE_SHIFT) + -				svma->vm_start; -	unsigned long sbase = saddr & PUD_MASK; -	unsigned long s_end = sbase + PUD_SIZE; - -	/* Allow segments to share if only one is marked locked */ -	unsigned long vm_flags = vma->vm_flags & ~VM_LOCKED; -	unsigned long svm_flags = svma->vm_flags & ~VM_LOCKED; - -	/* -	 * match the virtual addresses, permission and the alignment of the -	 * page table page. -	 */ -	if (pmd_index(addr) != pmd_index(saddr) || -	    vm_flags != svm_flags || -	    sbase < svma->vm_start || svma->vm_end < s_end) -		return 0; - -	return saddr; -} - -static int vma_shareable(struct vm_area_struct *vma, unsigned long addr) -{ -	unsigned long base = addr & PUD_MASK; -	unsigned long end = base + PUD_SIZE; - -	/* -	 * check on proper vm_flags and page table alignment -	 */ -	if (vma->vm_flags & VM_MAYSHARE && -	    vma->vm_start <= base && end <= vma->vm_end) -		return 1; -	return 0; -} - -/* - * search for a shareable pmd page for hugetlb. - */ -static void huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud) -{ -	struct vm_area_struct *vma = find_vma(mm, addr); -	struct address_space *mapping = vma->vm_file->f_mapping; -	pgoff_t idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + -			vma->vm_pgoff; -	struct prio_tree_iter iter; -	struct vm_area_struct *svma; -	unsigned long saddr; -	pte_t *spte = NULL; - -	if (!vma_shareable(vma, addr)) -		return; - -	spin_lock(&mapping->i_mmap_lock); -	vma_prio_tree_foreach(svma, &iter, &mapping->i_mmap, idx, idx) { -		if (svma == vma) -			continue; - -		saddr = page_table_shareable(svma, vma, addr, idx); -		if (saddr) { -			spte = huge_pte_offset(svma->vm_mm, saddr); -			if (spte) { -				get_page(virt_to_page(spte)); -				break; -			} -		} -	} - -	if (!spte) -		goto out; - -	spin_lock(&mm->page_table_lock); -	if (pud_none(*pud)) -		pud_populate(mm, pud, (pmd_t *)((unsigned long)spte & PAGE_MASK)); -	else -		put_page(virt_to_page(spte)); -	spin_unlock(&mm->page_table_lock); -out: -	spin_unlock(&mapping->i_mmap_lock); -} - -/* - * unmap huge page backed by shared pte. - * - * Hugetlb pte page is ref counted at the time of mapping.  If pte is shared - * indicated by page_count > 1, unmap is achieved by clearing pud and - * decrementing the ref count. If count == 1, the pte page is not shared. - * - * called with vma->vm_mm->page_table_lock held. - * - * returns: 1 successfully unmapped a shared pte page - *	    0 the underlying pte page is not shared, or it is the last user - */ -int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) -{ -	pgd_t *pgd = pgd_offset(mm, *addr); -	pud_t *pud = pud_offset(pgd, *addr); - -	BUG_ON(page_count(virt_to_page(ptep)) == 0); -	if (page_count(virt_to_page(ptep)) == 1) -		return 0; - -	pud_clear(pud); -	put_page(virt_to_page(ptep)); -	*addr = ALIGN(*addr, HPAGE_SIZE * PTRS_PER_PTE) - HPAGE_SIZE; -	return 1; -} - -pte_t *huge_pte_alloc(struct mm_struct *mm, -			unsigned long addr, unsigned long sz) -{ -	pgd_t *pgd; -	pud_t *pud; -	pte_t *pte = NULL; - -	pgd = pgd_offset(mm, addr); -	pud = pud_alloc(mm, pgd, addr); -	if (pud) { -		if (sz == PUD_SIZE) { -			pte = (pte_t *)pud; -		} else { -			BUG_ON(sz != PMD_SIZE); -			if (pud_none(*pud)) -				huge_pmd_share(mm, addr, pud); -			pte = (pte_t *) pmd_alloc(mm, pud, addr); -		} -	} -	BUG_ON(pte && !pte_none(*pte) && !pte_huge(*pte)); - -	return pte; -} - -pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) -{ -	pgd_t *pgd; -	pud_t *pud; -	pmd_t *pmd = NULL; - -	pgd = pgd_offset(mm, addr); -	if (pgd_present(*pgd)) { -		pud = pud_offset(pgd, addr); -		if (pud_present(*pud)) { -			if (pud_large(*pud)) -				return (pte_t *)pud; -			pmd = pmd_offset(pud, addr); -		} -	} -	return (pte_t *) pmd; -} -  #if 0	/* This is just for testing */  struct page *  follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) @@ -211,7 +58,6 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address,  {  	return NULL;  } -  #else  struct page * @@ -229,77 +75,23 @@ int pud_huge(pud_t pud)  {  	return !!(pud_val(pud) & _PAGE_PSE);  } - -struct page * -follow_huge_pmd(struct mm_struct *mm, unsigned long address, -		pmd_t *pmd, int write) -{ -	struct page *page; - -	page = pte_page(*(pte_t *)pmd); -	if (page) -		page += ((address & ~PMD_MASK) >> PAGE_SHIFT); -	return page; -} - -struct page * -follow_huge_pud(struct mm_struct *mm, unsigned long address, -		pud_t *pud, int write) -{ -	struct page *page; - -	page = pte_page(*(pte_t *)pud); -	if (page) -		page += ((address & ~PUD_MASK) >> PAGE_SHIFT); -	return page; -} -  #endif -/* x86_64 also uses this file */ - -#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA +#ifdef CONFIG_HUGETLB_PAGE  static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file,  		unsigned long addr, unsigned long len,  		unsigned long pgoff, unsigned long flags)  {  	struct hstate *h = hstate_file(file); -	struct mm_struct *mm = current->mm; -	struct vm_area_struct *vma; -	unsigned long start_addr; - -	if (len > mm->cached_hole_size) { -	        start_addr = mm->free_area_cache; -	} else { -	        start_addr = TASK_UNMAPPED_BASE; -	        mm->cached_hole_size = 0; -	} - -full_search: -	addr = ALIGN(start_addr, huge_page_size(h)); +	struct vm_unmapped_area_info info; -	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { -		/* At this point:  (!vma || addr < vma->vm_end). */ -		if (TASK_SIZE - len < addr) { -			/* -			 * Start a new search - just in case we missed -			 * some holes. -			 */ -			if (start_addr != TASK_UNMAPPED_BASE) { -				start_addr = TASK_UNMAPPED_BASE; -				mm->cached_hole_size = 0; -				goto full_search; -			} -			return -ENOMEM; -		} -		if (!vma || addr + len <= vma->vm_start) { -			mm->free_area_cache = addr + len; -			return addr; -		} -		if (addr + mm->cached_hole_size < vma->vm_start) -		        mm->cached_hole_size = vma->vm_start - addr; -		addr = ALIGN(vma->vm_end, huge_page_size(h)); -	} +	info.flags = 0; +	info.length = len; +	info.low_limit = current->mm->mmap_legacy_base; +	info.high_limit = TASK_SIZE; +	info.align_mask = PAGE_MASK & ~huge_page_mask(h); +	info.align_offset = 0; +	return vm_unmapped_area(&info);  }  static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file, @@ -307,87 +99,30 @@ static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file,  		unsigned long pgoff, unsigned long flags)  {  	struct hstate *h = hstate_file(file); -	struct mm_struct *mm = current->mm; -	struct vm_area_struct *vma, *prev_vma; -	unsigned long base = mm->mmap_base, addr = addr0; -	unsigned long largest_hole = mm->cached_hole_size; -	int first_time = 1; - -	/* don't allow allocations above current base */ -	if (mm->free_area_cache > base) -		mm->free_area_cache = base; +	struct vm_unmapped_area_info info; +	unsigned long addr; -	if (len <= largest_hole) { -	        largest_hole = 0; -		mm->free_area_cache  = base; -	} -try_again: -	/* make sure it can fit in the remaining address space */ -	if (mm->free_area_cache < len) -		goto fail; - -	/* either no address requested or cant fit in requested address hole */ -	addr = (mm->free_area_cache - len) & huge_page_mask(h); -	do { -		/* -		 * Lookup failure means no vma is above this address, -		 * i.e. return with success: -		 */ -		if (!(vma = find_vma_prev(mm, addr, &prev_vma))) -			return addr; +	info.flags = VM_UNMAPPED_AREA_TOPDOWN; +	info.length = len; +	info.low_limit = PAGE_SIZE; +	info.high_limit = current->mm->mmap_base; +	info.align_mask = PAGE_MASK & ~huge_page_mask(h); +	info.align_offset = 0; +	addr = vm_unmapped_area(&info); -		/* -		 * new region fits between prev_vma->vm_end and -		 * vma->vm_start, use it: -		 */ -		if (addr + len <= vma->vm_start && -		            (!prev_vma || (addr >= prev_vma->vm_end))) { -			/* remember the address as a hint for next time */ -		        mm->cached_hole_size = largest_hole; -		        return (mm->free_area_cache = addr); -		} else { -			/* pull free_area_cache down to the first hole */ -		        if (mm->free_area_cache == vma->vm_end) { -				mm->free_area_cache = vma->vm_start; -				mm->cached_hole_size = largest_hole; -			} -		} - -		/* remember the largest hole we saw so far */ -		if (addr + largest_hole < vma->vm_start) -		        largest_hole = vma->vm_start - addr; - -		/* try just below the current vma->vm_start */ -		addr = (vma->vm_start - len) & huge_page_mask(h); -	} while (len <= vma->vm_start); - -fail: -	/* -	 * if hint left us with no space for the requested -	 * mapping then try again: -	 */ -	if (first_time) { -		mm->free_area_cache = base; -		largest_hole = 0; -		first_time = 0; -		goto try_again; -	}  	/*  	 * A failed mmap() very likely causes application failure,  	 * so fall back to the bottom-up function here. This scenario  	 * can happen with large stack limits and large mmap()  	 * allocations.  	 */ -	mm->free_area_cache = TASK_UNMAPPED_BASE; -	mm->cached_hole_size = ~0UL; -	addr = hugetlb_get_unmapped_area_bottomup(file, addr0, -			len, pgoff, flags); - -	/* -	 * Restore the topdown base: -	 */ -	mm->free_area_cache = base; -	mm->cached_hole_size = ~0UL; +	if (addr & ~PAGE_MASK) { +		VM_BUG_ON(addr != -ENOMEM); +		info.flags = 0; +		info.low_limit = TASK_UNMAPPED_BASE; +		info.high_limit = TASK_SIZE; +		addr = vm_unmapped_area(&info); +	}  	return addr;  } @@ -425,8 +160,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,  		return hugetlb_get_unmapped_area_topdown(file, addr, len,  				pgoff, flags);  } - -#endif /*HAVE_ARCH_HUGETLB_UNMAPPED_AREA*/ +#endif /* CONFIG_HUGETLB_PAGE */  #ifdef CONFIG_X86_64  static __init int setup_hugepagesz(char *opt)  | 
