1/* 2 * arch/metag/mm/hugetlbpage.c 3 * 4 * METAG HugeTLB page support. 5 * 6 * Cloned from SuperH 7 * 8 * Cloned from sparc64 by Paul Mundt. 9 * 10 * Copyright (C) 2002, 2003 David S. Miller (davem@redhat.com) 11 */ 12 13#include <linux/init.h> 14#include <linux/fs.h> 15#include <linux/mm.h> 16#include <linux/hugetlb.h> 17#include <linux/pagemap.h> 18#include <linux/sysctl.h> 19 20#include <asm/mman.h> 21#include <asm/pgalloc.h> 22#include <asm/tlb.h> 23#include <asm/tlbflush.h> 24#include <asm/cacheflush.h> 25 26/* 27 * If the arch doesn't supply something else, assume that hugepage 28 * size aligned regions are ok without further preparation. 29 */ 30int prepare_hugepage_range(struct file *file, unsigned long addr, 31 unsigned long len) 32{ 33 struct mm_struct *mm = current->mm; 34 struct hstate *h = hstate_file(file); 35 struct vm_area_struct *vma; 36 37 if (len & ~huge_page_mask(h)) 38 return -EINVAL; 39 if (addr & ~huge_page_mask(h)) 40 return -EINVAL; 41 if (TASK_SIZE - len < addr) 42 return -EINVAL; 43 44 vma = find_vma(mm, ALIGN_HUGEPT(addr)); 45 if (vma && !(vma->vm_flags & MAP_HUGETLB)) 46 return -EINVAL; 47 48 vma = find_vma(mm, addr); 49 if (vma) { 50 if (addr + len > vma->vm_start) 51 return -EINVAL; 52 if (!(vma->vm_flags & MAP_HUGETLB) && 53 (ALIGN_HUGEPT(addr + len) > vma->vm_start)) 54 return -EINVAL; 55 } 56 return 0; 57} 58 59pte_t *huge_pte_alloc(struct mm_struct *mm, 60 unsigned long addr, unsigned long sz) 61{ 62 pgd_t *pgd; 63 pud_t *pud; 64 pmd_t *pmd; 65 pte_t *pte; 66 67 pgd = pgd_offset(mm, addr); 68 pud = pud_offset(pgd, addr); 69 pmd = pmd_offset(pud, addr); 70 pte = pte_alloc_map(mm, NULL, pmd, addr); 71 pgd->pgd &= ~_PAGE_SZ_MASK; 72 pgd->pgd |= _PAGE_SZHUGE; 73 74 return pte; 75} 76 77pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) 78{ 79 pgd_t *pgd; 80 pud_t *pud; 81 pmd_t *pmd; 82 pte_t *pte = NULL; 83 84 pgd = pgd_offset(mm, addr); 85 pud = pud_offset(pgd, addr); 86 pmd = pmd_offset(pud, addr); 87 pte = pte_offset_kernel(pmd, addr); 88 89 return pte; 90} 91 92int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) 93{ 94 return 0; 95} 96 97int pmd_huge(pmd_t pmd) 98{ 99 return pmd_page_shift(pmd) > PAGE_SHIFT; 100} 101 102int pud_huge(pud_t pud) 103{ 104 return 0; 105} 106 107struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, 108 pmd_t *pmd, int write) 109{ 110 return NULL; 111} 112 113#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA 114 115/* 116 * Look for an unmapped area starting after another hugetlb vma. 117 * There are guaranteed to be no huge pte's spare if all the huge pages are 118 * full size (4MB), so in that case compile out this search. 119 */ 120#if HPAGE_SHIFT == HUGEPT_SHIFT 121static inline unsigned long 122hugetlb_get_unmapped_area_existing(unsigned long len) 123{ 124 return 0; 125} 126#else 127static unsigned long 128hugetlb_get_unmapped_area_existing(unsigned long len) 129{ 130 struct mm_struct *mm = current->mm; 131 struct vm_area_struct *vma; 132 unsigned long start_addr, addr; 133 int after_huge; 134 135 if (mm->context.part_huge) { 136 start_addr = mm->context.part_huge; 137 after_huge = 1; 138 } else { 139 start_addr = TASK_UNMAPPED_BASE; 140 after_huge = 0; 141 } 142new_search: 143 addr = start_addr; 144 145 for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { 146 if ((!vma && !after_huge) || TASK_SIZE - len < addr) { 147 /* 148 * Start a new search - just in case we missed 149 * some holes. 150 */ 151 if (start_addr != TASK_UNMAPPED_BASE) { 152 start_addr = TASK_UNMAPPED_BASE; 153 goto new_search; 154 } 155 return 0; 156 } 157 /* skip ahead if we've aligned right over some vmas */ 158 if (vma && vma->vm_end <= addr) 159 continue; 160 /* space before the next vma? */ 161 if (after_huge && (!vma || ALIGN_HUGEPT(addr + len) 162 <= vma->vm_start)) { 163 unsigned long end = addr + len; 164 if (end & HUGEPT_MASK) 165 mm->context.part_huge = end; 166 else if (addr == mm->context.part_huge) 167 mm->context.part_huge = 0; 168 return addr; 169 } 170 if (vma->vm_flags & MAP_HUGETLB) { 171 /* space after a huge vma in 2nd level page table? */ 172 if (vma->vm_end & HUGEPT_MASK) { 173 after_huge = 1; 174 /* no need to align to the next PT block */ 175 addr = vma->vm_end; 176 continue; 177 } 178 } 179 after_huge = 0; 180 addr = ALIGN_HUGEPT(vma->vm_end); 181 } 182} 183#endif 184 185/* Do a full search to find an area without any nearby normal pages. */ 186static unsigned long 187hugetlb_get_unmapped_area_new_pmd(unsigned long len) 188{ 189 struct vm_unmapped_area_info info; 190 191 info.flags = 0; 192 info.length = len; 193 info.low_limit = TASK_UNMAPPED_BASE; 194 info.high_limit = TASK_SIZE; 195 info.align_mask = PAGE_MASK & HUGEPT_MASK; 196 info.align_offset = 0; 197 return vm_unmapped_area(&info); 198} 199 200unsigned long 201hugetlb_get_unmapped_area(struct file *file, unsigned long addr, 202 unsigned long len, unsigned long pgoff, unsigned long flags) 203{ 204 struct hstate *h = hstate_file(file); 205 206 if (len & ~huge_page_mask(h)) 207 return -EINVAL; 208 if (len > TASK_SIZE) 209 return -ENOMEM; 210 211 if (flags & MAP_FIXED) { 212 if (prepare_hugepage_range(file, addr, len)) 213 return -EINVAL; 214 return addr; 215 } 216 217 if (addr) { 218 addr = ALIGN(addr, huge_page_size(h)); 219 if (!prepare_hugepage_range(file, addr, len)) 220 return addr; 221 } 222 223 /* 224 * Look for an existing hugetlb vma with space after it (this is to to 225 * minimise fragmentation caused by huge pages. 226 */ 227 addr = hugetlb_get_unmapped_area_existing(len); 228 if (addr) 229 return addr; 230 231 /* 232 * Find an unmapped naturally aligned set of 4MB blocks that we can use 233 * for huge pages. 234 */ 235 return hugetlb_get_unmapped_area_new_pmd(len); 236} 237 238#endif /*HAVE_ARCH_HUGETLB_UNMAPPED_AREA*/ 239 240/* necessary for boot time 4MB huge page allocation */ 241static __init int setup_hugepagesz(char *opt) 242{ 243 unsigned long ps = memparse(opt, &opt); 244 if (ps == (1 << HPAGE_SHIFT)) { 245 hugetlb_add_hstate(HPAGE_SHIFT - PAGE_SHIFT); 246 } else { 247 pr_err("hugepagesz: Unsupported page size %lu M\n", 248 ps >> 20); 249 return 0; 250 } 251 return 1; 252} 253__setup("hugepagesz=", setup_hugepagesz); 254