xref: /openbmc/linux/arch/sparc/mm/hugetlbpage.c (revision 8ac1f8320a0073f28cf9e0491af4cd98f504f92a)
127137e52SSam Ravnborg /*
227137e52SSam Ravnborg  * SPARC64 Huge TLB page support.
327137e52SSam Ravnborg  *
427137e52SSam Ravnborg  * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net)
527137e52SSam Ravnborg  */
627137e52SSam Ravnborg 
727137e52SSam Ravnborg #include <linux/init.h>
827137e52SSam Ravnborg #include <linux/module.h>
927137e52SSam Ravnborg #include <linux/fs.h>
1027137e52SSam Ravnborg #include <linux/mm.h>
1127137e52SSam Ravnborg #include <linux/hugetlb.h>
1227137e52SSam Ravnborg #include <linux/pagemap.h>
1327137e52SSam Ravnborg #include <linux/sysctl.h>
1427137e52SSam Ravnborg 
1527137e52SSam Ravnborg #include <asm/mman.h>
1627137e52SSam Ravnborg #include <asm/pgalloc.h>
1727137e52SSam Ravnborg #include <asm/tlb.h>
1827137e52SSam Ravnborg #include <asm/tlbflush.h>
1927137e52SSam Ravnborg #include <asm/cacheflush.h>
2027137e52SSam Ravnborg #include <asm/mmu_context.h>
2127137e52SSam Ravnborg 
2227137e52SSam Ravnborg /* Slightly simplified from the non-hugepage variant because by
2327137e52SSam Ravnborg  * definition we don't have to worry about any page coloring stuff
2427137e52SSam Ravnborg  */
2527137e52SSam Ravnborg #define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL))
2627137e52SSam Ravnborg #define VA_EXCLUDE_END   (0xfffff80000000000UL + (1UL << 32UL))
2727137e52SSam Ravnborg 
2827137e52SSam Ravnborg static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp,
2927137e52SSam Ravnborg 							unsigned long addr,
3027137e52SSam Ravnborg 							unsigned long len,
3127137e52SSam Ravnborg 							unsigned long pgoff,
3227137e52SSam Ravnborg 							unsigned long flags)
3327137e52SSam Ravnborg {
3427137e52SSam Ravnborg 	struct mm_struct *mm = current->mm;
3527137e52SSam Ravnborg 	struct vm_area_struct * vma;
3627137e52SSam Ravnborg 	unsigned long task_size = TASK_SIZE;
3727137e52SSam Ravnborg 	unsigned long start_addr;
3827137e52SSam Ravnborg 
3927137e52SSam Ravnborg 	if (test_thread_flag(TIF_32BIT))
4027137e52SSam Ravnborg 		task_size = STACK_TOP32;
4127137e52SSam Ravnborg 	if (unlikely(len >= VA_EXCLUDE_START))
4227137e52SSam Ravnborg 		return -ENOMEM;
4327137e52SSam Ravnborg 
4427137e52SSam Ravnborg 	if (len > mm->cached_hole_size) {
4527137e52SSam Ravnborg 	        start_addr = addr = mm->free_area_cache;
4627137e52SSam Ravnborg 	} else {
4727137e52SSam Ravnborg 	        start_addr = addr = TASK_UNMAPPED_BASE;
4827137e52SSam Ravnborg 	        mm->cached_hole_size = 0;
4927137e52SSam Ravnborg 	}
5027137e52SSam Ravnborg 
5127137e52SSam Ravnborg 	task_size -= len;
5227137e52SSam Ravnborg 
5327137e52SSam Ravnborg full_search:
5427137e52SSam Ravnborg 	addr = ALIGN(addr, HPAGE_SIZE);
5527137e52SSam Ravnborg 
5627137e52SSam Ravnborg 	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
5727137e52SSam Ravnborg 		/* At this point:  (!vma || addr < vma->vm_end). */
5827137e52SSam Ravnborg 		if (addr < VA_EXCLUDE_START &&
5927137e52SSam Ravnborg 		    (addr + len) >= VA_EXCLUDE_START) {
6027137e52SSam Ravnborg 			addr = VA_EXCLUDE_END;
6127137e52SSam Ravnborg 			vma = find_vma(mm, VA_EXCLUDE_END);
6227137e52SSam Ravnborg 		}
6327137e52SSam Ravnborg 		if (unlikely(task_size < addr)) {
6427137e52SSam Ravnborg 			if (start_addr != TASK_UNMAPPED_BASE) {
6527137e52SSam Ravnborg 				start_addr = addr = TASK_UNMAPPED_BASE;
6627137e52SSam Ravnborg 				mm->cached_hole_size = 0;
6727137e52SSam Ravnborg 				goto full_search;
6827137e52SSam Ravnborg 			}
6927137e52SSam Ravnborg 			return -ENOMEM;
7027137e52SSam Ravnborg 		}
7127137e52SSam Ravnborg 		if (likely(!vma || addr + len <= vma->vm_start)) {
7227137e52SSam Ravnborg 			/*
7327137e52SSam Ravnborg 			 * Remember the place where we stopped the search:
7427137e52SSam Ravnborg 			 */
7527137e52SSam Ravnborg 			mm->free_area_cache = addr + len;
7627137e52SSam Ravnborg 			return addr;
7727137e52SSam Ravnborg 		}
7827137e52SSam Ravnborg 		if (addr + mm->cached_hole_size < vma->vm_start)
7927137e52SSam Ravnborg 		        mm->cached_hole_size = vma->vm_start - addr;
8027137e52SSam Ravnborg 
8127137e52SSam Ravnborg 		addr = ALIGN(vma->vm_end, HPAGE_SIZE);
8227137e52SSam Ravnborg 	}
8327137e52SSam Ravnborg }
8427137e52SSam Ravnborg 
8527137e52SSam Ravnborg static unsigned long
8627137e52SSam Ravnborg hugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
8727137e52SSam Ravnborg 				  const unsigned long len,
8827137e52SSam Ravnborg 				  const unsigned long pgoff,
8927137e52SSam Ravnborg 				  const unsigned long flags)
9027137e52SSam Ravnborg {
9127137e52SSam Ravnborg 	struct vm_area_struct *vma;
9227137e52SSam Ravnborg 	struct mm_struct *mm = current->mm;
9327137e52SSam Ravnborg 	unsigned long addr = addr0;
9427137e52SSam Ravnborg 
9527137e52SSam Ravnborg 	/* This should only ever run for 32-bit processes.  */
9627137e52SSam Ravnborg 	BUG_ON(!test_thread_flag(TIF_32BIT));
9727137e52SSam Ravnborg 
9827137e52SSam Ravnborg 	/* check if free_area_cache is useful for us */
9927137e52SSam Ravnborg 	if (len <= mm->cached_hole_size) {
10027137e52SSam Ravnborg  	        mm->cached_hole_size = 0;
10127137e52SSam Ravnborg  		mm->free_area_cache = mm->mmap_base;
10227137e52SSam Ravnborg  	}
10327137e52SSam Ravnborg 
10427137e52SSam Ravnborg 	/* either no address requested or can't fit in requested address hole */
10527137e52SSam Ravnborg 	addr = mm->free_area_cache & HPAGE_MASK;
10627137e52SSam Ravnborg 
10727137e52SSam Ravnborg 	/* make sure it can fit in the remaining address space */
10827137e52SSam Ravnborg 	if (likely(addr > len)) {
10927137e52SSam Ravnborg 		vma = find_vma(mm, addr-len);
11027137e52SSam Ravnborg 		if (!vma || addr <= vma->vm_start) {
11127137e52SSam Ravnborg 			/* remember the address as a hint for next time */
11227137e52SSam Ravnborg 			return (mm->free_area_cache = addr-len);
11327137e52SSam Ravnborg 		}
11427137e52SSam Ravnborg 	}
11527137e52SSam Ravnborg 
11627137e52SSam Ravnborg 	if (unlikely(mm->mmap_base < len))
11727137e52SSam Ravnborg 		goto bottomup;
11827137e52SSam Ravnborg 
11927137e52SSam Ravnborg 	addr = (mm->mmap_base-len) & HPAGE_MASK;
12027137e52SSam Ravnborg 
12127137e52SSam Ravnborg 	do {
12227137e52SSam Ravnborg 		/*
12327137e52SSam Ravnborg 		 * Lookup failure means no vma is above this address,
12427137e52SSam Ravnborg 		 * else if new region fits below vma->vm_start,
12527137e52SSam Ravnborg 		 * return with success:
12627137e52SSam Ravnborg 		 */
12727137e52SSam Ravnborg 		vma = find_vma(mm, addr);
12827137e52SSam Ravnborg 		if (likely(!vma || addr+len <= vma->vm_start)) {
12927137e52SSam Ravnborg 			/* remember the address as a hint for next time */
13027137e52SSam Ravnborg 			return (mm->free_area_cache = addr);
13127137e52SSam Ravnborg 		}
13227137e52SSam Ravnborg 
13327137e52SSam Ravnborg  		/* remember the largest hole we saw so far */
13427137e52SSam Ravnborg  		if (addr + mm->cached_hole_size < vma->vm_start)
13527137e52SSam Ravnborg  		        mm->cached_hole_size = vma->vm_start - addr;
13627137e52SSam Ravnborg 
13727137e52SSam Ravnborg 		/* try just below the current vma->vm_start */
13827137e52SSam Ravnborg 		addr = (vma->vm_start-len) & HPAGE_MASK;
13927137e52SSam Ravnborg 	} while (likely(len < vma->vm_start));
14027137e52SSam Ravnborg 
14127137e52SSam Ravnborg bottomup:
14227137e52SSam Ravnborg 	/*
14327137e52SSam Ravnborg 	 * A failed mmap() very likely causes application failure,
14427137e52SSam Ravnborg 	 * so fall back to the bottom-up function here. This scenario
14527137e52SSam Ravnborg 	 * can happen with large stack limits and large mmap()
14627137e52SSam Ravnborg 	 * allocations.
14727137e52SSam Ravnborg 	 */
14827137e52SSam Ravnborg 	mm->cached_hole_size = ~0UL;
14927137e52SSam Ravnborg   	mm->free_area_cache = TASK_UNMAPPED_BASE;
15027137e52SSam Ravnborg 	addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
15127137e52SSam Ravnborg 	/*
15227137e52SSam Ravnborg 	 * Restore the topdown base:
15327137e52SSam Ravnborg 	 */
15427137e52SSam Ravnborg 	mm->free_area_cache = mm->mmap_base;
15527137e52SSam Ravnborg 	mm->cached_hole_size = ~0UL;
15627137e52SSam Ravnborg 
15727137e52SSam Ravnborg 	return addr;
15827137e52SSam Ravnborg }
15927137e52SSam Ravnborg 
16027137e52SSam Ravnborg unsigned long
16127137e52SSam Ravnborg hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
16227137e52SSam Ravnborg 		unsigned long len, unsigned long pgoff, unsigned long flags)
16327137e52SSam Ravnborg {
16427137e52SSam Ravnborg 	struct mm_struct *mm = current->mm;
16527137e52SSam Ravnborg 	struct vm_area_struct *vma;
16627137e52SSam Ravnborg 	unsigned long task_size = TASK_SIZE;
16727137e52SSam Ravnborg 
16827137e52SSam Ravnborg 	if (test_thread_flag(TIF_32BIT))
16927137e52SSam Ravnborg 		task_size = STACK_TOP32;
17027137e52SSam Ravnborg 
17127137e52SSam Ravnborg 	if (len & ~HPAGE_MASK)
17227137e52SSam Ravnborg 		return -EINVAL;
17327137e52SSam Ravnborg 	if (len > task_size)
17427137e52SSam Ravnborg 		return -ENOMEM;
17527137e52SSam Ravnborg 
17627137e52SSam Ravnborg 	if (flags & MAP_FIXED) {
17727137e52SSam Ravnborg 		if (prepare_hugepage_range(file, addr, len))
17827137e52SSam Ravnborg 			return -EINVAL;
17927137e52SSam Ravnborg 		return addr;
18027137e52SSam Ravnborg 	}
18127137e52SSam Ravnborg 
18227137e52SSam Ravnborg 	if (addr) {
18327137e52SSam Ravnborg 		addr = ALIGN(addr, HPAGE_SIZE);
18427137e52SSam Ravnborg 		vma = find_vma(mm, addr);
18527137e52SSam Ravnborg 		if (task_size - len >= addr &&
18627137e52SSam Ravnborg 		    (!vma || addr + len <= vma->vm_start))
18727137e52SSam Ravnborg 			return addr;
18827137e52SSam Ravnborg 	}
18927137e52SSam Ravnborg 	if (mm->get_unmapped_area == arch_get_unmapped_area)
19027137e52SSam Ravnborg 		return hugetlb_get_unmapped_area_bottomup(file, addr, len,
19127137e52SSam Ravnborg 				pgoff, flags);
19227137e52SSam Ravnborg 	else
19327137e52SSam Ravnborg 		return hugetlb_get_unmapped_area_topdown(file, addr, len,
19427137e52SSam Ravnborg 				pgoff, flags);
19527137e52SSam Ravnborg }
19627137e52SSam Ravnborg 
19727137e52SSam Ravnborg pte_t *huge_pte_alloc(struct mm_struct *mm,
19827137e52SSam Ravnborg 			unsigned long addr, unsigned long sz)
19927137e52SSam Ravnborg {
20027137e52SSam Ravnborg 	pgd_t *pgd;
20127137e52SSam Ravnborg 	pud_t *pud;
20227137e52SSam Ravnborg 	pmd_t *pmd;
20327137e52SSam Ravnborg 	pte_t *pte = NULL;
20427137e52SSam Ravnborg 
20527137e52SSam Ravnborg 	/* We must align the address, because our caller will run
20627137e52SSam Ravnborg 	 * set_huge_pte_at() on whatever we return, which writes out
20727137e52SSam Ravnborg 	 * all of the sub-ptes for the hugepage range.  So we have
20827137e52SSam Ravnborg 	 * to give it the first such sub-pte.
20927137e52SSam Ravnborg 	 */
21027137e52SSam Ravnborg 	addr &= HPAGE_MASK;
21127137e52SSam Ravnborg 
21227137e52SSam Ravnborg 	pgd = pgd_offset(mm, addr);
21327137e52SSam Ravnborg 	pud = pud_alloc(mm, pgd, addr);
21427137e52SSam Ravnborg 	if (pud) {
21527137e52SSam Ravnborg 		pmd = pmd_alloc(mm, pud, addr);
21627137e52SSam Ravnborg 		if (pmd)
217*8ac1f832SAndrea Arcangeli 			pte = pte_alloc_map(mm, NULL, pmd, addr);
21827137e52SSam Ravnborg 	}
21927137e52SSam Ravnborg 	return pte;
22027137e52SSam Ravnborg }
22127137e52SSam Ravnborg 
22227137e52SSam Ravnborg pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
22327137e52SSam Ravnborg {
22427137e52SSam Ravnborg 	pgd_t *pgd;
22527137e52SSam Ravnborg 	pud_t *pud;
22627137e52SSam Ravnborg 	pmd_t *pmd;
22727137e52SSam Ravnborg 	pte_t *pte = NULL;
22827137e52SSam Ravnborg 
22927137e52SSam Ravnborg 	addr &= HPAGE_MASK;
23027137e52SSam Ravnborg 
23127137e52SSam Ravnborg 	pgd = pgd_offset(mm, addr);
23227137e52SSam Ravnborg 	if (!pgd_none(*pgd)) {
23327137e52SSam Ravnborg 		pud = pud_offset(pgd, addr);
23427137e52SSam Ravnborg 		if (!pud_none(*pud)) {
23527137e52SSam Ravnborg 			pmd = pmd_offset(pud, addr);
23627137e52SSam Ravnborg 			if (!pmd_none(*pmd))
23727137e52SSam Ravnborg 				pte = pte_offset_map(pmd, addr);
23827137e52SSam Ravnborg 		}
23927137e52SSam Ravnborg 	}
24027137e52SSam Ravnborg 	return pte;
24127137e52SSam Ravnborg }
24227137e52SSam Ravnborg 
24327137e52SSam Ravnborg int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
24427137e52SSam Ravnborg {
24527137e52SSam Ravnborg 	return 0;
24627137e52SSam Ravnborg }
24727137e52SSam Ravnborg 
24827137e52SSam Ravnborg void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
24927137e52SSam Ravnborg 		     pte_t *ptep, pte_t entry)
25027137e52SSam Ravnborg {
25127137e52SSam Ravnborg 	int i;
25227137e52SSam Ravnborg 
25327137e52SSam Ravnborg 	if (!pte_present(*ptep) && pte_present(entry))
25427137e52SSam Ravnborg 		mm->context.huge_pte_count++;
25527137e52SSam Ravnborg 
25627137e52SSam Ravnborg 	addr &= HPAGE_MASK;
25727137e52SSam Ravnborg 	for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
25827137e52SSam Ravnborg 		set_pte_at(mm, addr, ptep, entry);
25927137e52SSam Ravnborg 		ptep++;
26027137e52SSam Ravnborg 		addr += PAGE_SIZE;
26127137e52SSam Ravnborg 		pte_val(entry) += PAGE_SIZE;
26227137e52SSam Ravnborg 	}
26327137e52SSam Ravnborg }
26427137e52SSam Ravnborg 
26527137e52SSam Ravnborg pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
26627137e52SSam Ravnborg 			      pte_t *ptep)
26727137e52SSam Ravnborg {
26827137e52SSam Ravnborg 	pte_t entry;
26927137e52SSam Ravnborg 	int i;
27027137e52SSam Ravnborg 
27127137e52SSam Ravnborg 	entry = *ptep;
27227137e52SSam Ravnborg 	if (pte_present(entry))
27327137e52SSam Ravnborg 		mm->context.huge_pte_count--;
27427137e52SSam Ravnborg 
27527137e52SSam Ravnborg 	addr &= HPAGE_MASK;
27627137e52SSam Ravnborg 
27727137e52SSam Ravnborg 	for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
27827137e52SSam Ravnborg 		pte_clear(mm, addr, ptep);
27927137e52SSam Ravnborg 		addr += PAGE_SIZE;
28027137e52SSam Ravnborg 		ptep++;
28127137e52SSam Ravnborg 	}
28227137e52SSam Ravnborg 
28327137e52SSam Ravnborg 	return entry;
28427137e52SSam Ravnborg }
28527137e52SSam Ravnborg 
28627137e52SSam Ravnborg struct page *follow_huge_addr(struct mm_struct *mm,
28727137e52SSam Ravnborg 			      unsigned long address, int write)
28827137e52SSam Ravnborg {
28927137e52SSam Ravnborg 	return ERR_PTR(-EINVAL);
29027137e52SSam Ravnborg }
29127137e52SSam Ravnborg 
29227137e52SSam Ravnborg int pmd_huge(pmd_t pmd)
29327137e52SSam Ravnborg {
29427137e52SSam Ravnborg 	return 0;
29527137e52SSam Ravnborg }
29627137e52SSam Ravnborg 
29727137e52SSam Ravnborg int pud_huge(pud_t pud)
29827137e52SSam Ravnborg {
29927137e52SSam Ravnborg 	return 0;
30027137e52SSam Ravnborg }
30127137e52SSam Ravnborg 
30227137e52SSam Ravnborg struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address,
30327137e52SSam Ravnborg 			     pmd_t *pmd, int write)
30427137e52SSam Ravnborg {
30527137e52SSam Ravnborg 	return NULL;
30627137e52SSam Ravnborg }
30727137e52SSam Ravnborg 
30827137e52SSam Ravnborg static void context_reload(void *__data)
30927137e52SSam Ravnborg {
31027137e52SSam Ravnborg 	struct mm_struct *mm = __data;
31127137e52SSam Ravnborg 
31227137e52SSam Ravnborg 	if (mm == current->mm)
31327137e52SSam Ravnborg 		load_secondary_context(mm);
31427137e52SSam Ravnborg }
31527137e52SSam Ravnborg 
31627137e52SSam Ravnborg void hugetlb_prefault_arch_hook(struct mm_struct *mm)
31727137e52SSam Ravnborg {
31827137e52SSam Ravnborg 	struct tsb_config *tp = &mm->context.tsb_block[MM_TSB_HUGE];
31927137e52SSam Ravnborg 
32027137e52SSam Ravnborg 	if (likely(tp->tsb != NULL))
32127137e52SSam Ravnborg 		return;
32227137e52SSam Ravnborg 
32327137e52SSam Ravnborg 	tsb_grow(mm, MM_TSB_HUGE, 0);
32427137e52SSam Ravnborg 	tsb_context_switch(mm);
32527137e52SSam Ravnborg 	smp_tsb_sync(mm);
32627137e52SSam Ravnborg 
32727137e52SSam Ravnborg 	/* On UltraSPARC-III+ and later, configure the second half of
32827137e52SSam Ravnborg 	 * the Data-TLB for huge pages.
32927137e52SSam Ravnborg 	 */
33027137e52SSam Ravnborg 	if (tlb_type == cheetah_plus) {
33127137e52SSam Ravnborg 		unsigned long ctx;
33227137e52SSam Ravnborg 
33327137e52SSam Ravnborg 		spin_lock(&ctx_alloc_lock);
33427137e52SSam Ravnborg 		ctx = mm->context.sparc64_ctx_val;
33527137e52SSam Ravnborg 		ctx &= ~CTX_PGSZ_MASK;
33627137e52SSam Ravnborg 		ctx |= CTX_PGSZ_BASE << CTX_PGSZ0_SHIFT;
33727137e52SSam Ravnborg 		ctx |= CTX_PGSZ_HUGE << CTX_PGSZ1_SHIFT;
33827137e52SSam Ravnborg 
33927137e52SSam Ravnborg 		if (ctx != mm->context.sparc64_ctx_val) {
34027137e52SSam Ravnborg 			/* When changing the page size fields, we
34127137e52SSam Ravnborg 			 * must perform a context flush so that no
34227137e52SSam Ravnborg 			 * stale entries match.  This flush must
34327137e52SSam Ravnborg 			 * occur with the original context register
34427137e52SSam Ravnborg 			 * settings.
34527137e52SSam Ravnborg 			 */
34627137e52SSam Ravnborg 			do_flush_tlb_mm(mm);
34727137e52SSam Ravnborg 
34827137e52SSam Ravnborg 			/* Reload the context register of all processors
34927137e52SSam Ravnborg 			 * also executing in this address space.
35027137e52SSam Ravnborg 			 */
35127137e52SSam Ravnborg 			mm->context.sparc64_ctx_val = ctx;
35227137e52SSam Ravnborg 			on_each_cpu(context_reload, mm, 0);
35327137e52SSam Ravnborg 		}
35427137e52SSam Ravnborg 		spin_unlock(&ctx_alloc_lock);
35527137e52SSam Ravnborg 	}
35627137e52SSam Ravnborg }
357