diff options
Diffstat (limited to 'arch/sparc64/mm/hugetlbpage.c')
-rw-r--r-- | arch/sparc64/mm/hugetlbpage.c | 219 |
1 files changed, 194 insertions, 25 deletions
diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 625cbb336a23..074620d413d4 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * SPARC64 Huge TLB page support. | 2 | * SPARC64 Huge TLB page support. |
3 | * | 3 | * |
4 | * Copyright (C) 2002, 2003 David S. Miller (davem@redhat.com) | 4 | * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net) |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/config.h> | 7 | #include <linux/config.h> |
@@ -22,6 +22,175 @@ | |||
22 | #include <asm/cacheflush.h> | 22 | #include <asm/cacheflush.h> |
23 | #include <asm/mmu_context.h> | 23 | #include <asm/mmu_context.h> |
24 | 24 | ||
25 | /* Slightly simplified from the non-hugepage variant because by | ||
26 | * definition we don't have to worry about any page coloring stuff | ||
27 | */ | ||
28 | #define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL)) | ||
29 | #define VA_EXCLUDE_END (0xfffff80000000000UL + (1UL << 32UL)) | ||
30 | |||
31 | static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp, | ||
32 | unsigned long addr, | ||
33 | unsigned long len, | ||
34 | unsigned long pgoff, | ||
35 | unsigned long flags) | ||
36 | { | ||
37 | struct mm_struct *mm = current->mm; | ||
38 | struct vm_area_struct * vma; | ||
39 | unsigned long task_size = TASK_SIZE; | ||
40 | unsigned long start_addr; | ||
41 | |||
42 | if (test_thread_flag(TIF_32BIT)) | ||
43 | task_size = STACK_TOP32; | ||
44 | if (unlikely(len >= VA_EXCLUDE_START)) | ||
45 | return -ENOMEM; | ||
46 | |||
47 | if (len > mm->cached_hole_size) { | ||
48 | start_addr = addr = mm->free_area_cache; | ||
49 | } else { | ||
50 | start_addr = addr = TASK_UNMAPPED_BASE; | ||
51 | mm->cached_hole_size = 0; | ||
52 | } | ||
53 | |||
54 | task_size -= len; | ||
55 | |||
56 | full_search: | ||
57 | addr = ALIGN(addr, HPAGE_SIZE); | ||
58 | |||
59 | for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { | ||
60 | /* At this point: (!vma || addr < vma->vm_end). */ | ||
61 | if (addr < VA_EXCLUDE_START && | ||
62 | (addr + len) >= VA_EXCLUDE_START) { | ||
63 | addr = VA_EXCLUDE_END; | ||
64 | vma = find_vma(mm, VA_EXCLUDE_END); | ||
65 | } | ||
66 | if (unlikely(task_size < addr)) { | ||
67 | if (start_addr != TASK_UNMAPPED_BASE) { | ||
68 | start_addr = addr = TASK_UNMAPPED_BASE; | ||
69 | mm->cached_hole_size = 0; | ||
70 | goto full_search; | ||
71 | } | ||
72 | return -ENOMEM; | ||
73 | } | ||
74 | if (likely(!vma || addr + len <= vma->vm_start)) { | ||
75 | /* | ||
76 | * Remember the place where we stopped the search: | ||
77 | */ | ||
78 | mm->free_area_cache = addr + len; | ||
79 | return addr; | ||
80 | } | ||
81 | if (addr + mm->cached_hole_size < vma->vm_start) | ||
82 | mm->cached_hole_size = vma->vm_start - addr; | ||
83 | |||
84 | addr = ALIGN(vma->vm_end, HPAGE_SIZE); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | static unsigned long | ||
89 | hugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, | ||
90 | const unsigned long len, | ||
91 | const unsigned long pgoff, | ||
92 | const unsigned long flags) | ||
93 | { | ||
94 | struct vm_area_struct *vma; | ||
95 | struct mm_struct *mm = current->mm; | ||
96 | unsigned long addr = addr0; | ||
97 | |||
98 | /* This should only ever run for 32-bit processes. */ | ||
99 | BUG_ON(!test_thread_flag(TIF_32BIT)); | ||
100 | |||
101 | /* check if free_area_cache is useful for us */ | ||
102 | if (len <= mm->cached_hole_size) { | ||
103 | mm->cached_hole_size = 0; | ||
104 | mm->free_area_cache = mm->mmap_base; | ||
105 | } | ||
106 | |||
107 | /* either no address requested or can't fit in requested address hole */ | ||
108 | addr = mm->free_area_cache & HPAGE_MASK; | ||
109 | |||
110 | /* make sure it can fit in the remaining address space */ | ||
111 | if (likely(addr > len)) { | ||
112 | vma = find_vma(mm, addr-len); | ||
113 | if (!vma || addr <= vma->vm_start) { | ||
114 | /* remember the address as a hint for next time */ | ||
115 | return (mm->free_area_cache = addr-len); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (unlikely(mm->mmap_base < len)) | ||
120 | goto bottomup; | ||
121 | |||
122 | addr = (mm->mmap_base-len) & HPAGE_MASK; | ||
123 | |||
124 | do { | ||
125 | /* | ||
126 | * Lookup failure means no vma is above this address, | ||
127 | * else if new region fits below vma->vm_start, | ||
128 | * return with success: | ||
129 | */ | ||
130 | vma = find_vma(mm, addr); | ||
131 | if (likely(!vma || addr+len <= vma->vm_start)) { | ||
132 | /* remember the address as a hint for next time */ | ||
133 | return (mm->free_area_cache = addr); | ||
134 | } | ||
135 | |||
136 | /* remember the largest hole we saw so far */ | ||
137 | if (addr + mm->cached_hole_size < vma->vm_start) | ||
138 | mm->cached_hole_size = vma->vm_start - addr; | ||
139 | |||
140 | /* try just below the current vma->vm_start */ | ||
141 | addr = (vma->vm_start-len) & HPAGE_MASK; | ||
142 | } while (likely(len < vma->vm_start)); | ||
143 | |||
144 | bottomup: | ||
145 | /* | ||
146 | * A failed mmap() very likely causes application failure, | ||
147 | * so fall back to the bottom-up function here. This scenario | ||
148 | * can happen with large stack limits and large mmap() | ||
149 | * allocations. | ||
150 | */ | ||
151 | mm->cached_hole_size = ~0UL; | ||
152 | mm->free_area_cache = TASK_UNMAPPED_BASE; | ||
153 | addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags); | ||
154 | /* | ||
155 | * Restore the topdown base: | ||
156 | */ | ||
157 | mm->free_area_cache = mm->mmap_base; | ||
158 | mm->cached_hole_size = ~0UL; | ||
159 | |||
160 | return addr; | ||
161 | } | ||
162 | |||
163 | unsigned long | ||
164 | hugetlb_get_unmapped_area(struct file *file, unsigned long addr, | ||
165 | unsigned long len, unsigned long pgoff, unsigned long flags) | ||
166 | { | ||
167 | struct mm_struct *mm = current->mm; | ||
168 | struct vm_area_struct *vma; | ||
169 | unsigned long task_size = TASK_SIZE; | ||
170 | |||
171 | if (test_thread_flag(TIF_32BIT)) | ||
172 | task_size = STACK_TOP32; | ||
173 | |||
174 | if (len & ~HPAGE_MASK) | ||
175 | return -EINVAL; | ||
176 | if (len > task_size) | ||
177 | return -ENOMEM; | ||
178 | |||
179 | if (addr) { | ||
180 | addr = ALIGN(addr, HPAGE_SIZE); | ||
181 | vma = find_vma(mm, addr); | ||
182 | if (task_size - len >= addr && | ||
183 | (!vma || addr + len <= vma->vm_start)) | ||
184 | return addr; | ||
185 | } | ||
186 | if (mm->get_unmapped_area == arch_get_unmapped_area) | ||
187 | return hugetlb_get_unmapped_area_bottomup(file, addr, len, | ||
188 | pgoff, flags); | ||
189 | else | ||
190 | return hugetlb_get_unmapped_area_topdown(file, addr, len, | ||
191 | pgoff, flags); | ||
192 | } | ||
193 | |||
25 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | 194 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) |
26 | { | 195 | { |
27 | pgd_t *pgd; | 196 | pgd_t *pgd; |
@@ -30,13 +199,11 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | |||
30 | pte_t *pte = NULL; | 199 | pte_t *pte = NULL; |
31 | 200 | ||
32 | pgd = pgd_offset(mm, addr); | 201 | pgd = pgd_offset(mm, addr); |
33 | if (pgd) { | 202 | pud = pud_alloc(mm, pgd, addr); |
34 | pud = pud_offset(pgd, addr); | 203 | if (pud) { |
35 | if (pud) { | 204 | pmd = pmd_alloc(mm, pud, addr); |
36 | pmd = pmd_alloc(mm, pud, addr); | 205 | if (pmd) |
37 | if (pmd) | 206 | pte = pte_alloc_map(mm, pmd, addr); |
38 | pte = pte_alloc_map(mm, pmd, addr); | ||
39 | } | ||
40 | } | 207 | } |
41 | return pte; | 208 | return pte; |
42 | } | 209 | } |
@@ -48,25 +215,28 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | |||
48 | pmd_t *pmd; | 215 | pmd_t *pmd; |
49 | pte_t *pte = NULL; | 216 | pte_t *pte = NULL; |
50 | 217 | ||
218 | addr &= HPAGE_MASK; | ||
219 | |||
51 | pgd = pgd_offset(mm, addr); | 220 | pgd = pgd_offset(mm, addr); |
52 | if (pgd) { | 221 | if (!pgd_none(*pgd)) { |
53 | pud = pud_offset(pgd, addr); | 222 | pud = pud_offset(pgd, addr); |
54 | if (pud) { | 223 | if (!pud_none(*pud)) { |
55 | pmd = pmd_offset(pud, addr); | 224 | pmd = pmd_offset(pud, addr); |
56 | if (pmd) | 225 | if (!pmd_none(*pmd)) |
57 | pte = pte_offset_map(pmd, addr); | 226 | pte = pte_offset_map(pmd, addr); |
58 | } | 227 | } |
59 | } | 228 | } |
60 | return pte; | 229 | return pte; |
61 | } | 230 | } |
62 | 231 | ||
63 | #define mk_pte_huge(entry) do { pte_val(entry) |= _PAGE_SZHUGE; } while (0) | ||
64 | |||
65 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | 232 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, |
66 | pte_t *ptep, pte_t entry) | 233 | pte_t *ptep, pte_t entry) |
67 | { | 234 | { |
68 | int i; | 235 | int i; |
69 | 236 | ||
237 | if (!pte_present(*ptep) && pte_present(entry)) | ||
238 | mm->context.huge_pte_count++; | ||
239 | |||
70 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { | 240 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { |
71 | set_pte_at(mm, addr, ptep, entry); | 241 | set_pte_at(mm, addr, ptep, entry); |
72 | ptep++; | 242 | ptep++; |
@@ -82,6 +252,8 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, | |||
82 | int i; | 252 | int i; |
83 | 253 | ||
84 | entry = *ptep; | 254 | entry = *ptep; |
255 | if (pte_present(entry)) | ||
256 | mm->context.huge_pte_count--; | ||
85 | 257 | ||
86 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { | 258 | for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { |
87 | pte_clear(mm, addr, ptep); | 259 | pte_clear(mm, addr, ptep); |
@@ -92,18 +264,6 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, | |||
92 | return entry; | 264 | return entry; |
93 | } | 265 | } |
94 | 266 | ||
95 | /* | ||
96 | * This function checks for proper alignment of input addr and len parameters. | ||
97 | */ | ||
98 | int is_aligned_hugepage_range(unsigned long addr, unsigned long len) | ||
99 | { | ||
100 | if (len & ~HPAGE_MASK) | ||
101 | return -EINVAL; | ||
102 | if (addr & ~HPAGE_MASK) | ||
103 | return -EINVAL; | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | struct page *follow_huge_addr(struct mm_struct *mm, | 267 | struct page *follow_huge_addr(struct mm_struct *mm, |
108 | unsigned long address, int write) | 268 | unsigned long address, int write) |
109 | { | 269 | { |
@@ -131,6 +291,15 @@ static void context_reload(void *__data) | |||
131 | 291 | ||
132 | void hugetlb_prefault_arch_hook(struct mm_struct *mm) | 292 | void hugetlb_prefault_arch_hook(struct mm_struct *mm) |
133 | { | 293 | { |
294 | struct tsb_config *tp = &mm->context.tsb_block[MM_TSB_HUGE]; | ||
295 | |||
296 | if (likely(tp->tsb != NULL)) | ||
297 | return; | ||
298 | |||
299 | tsb_grow(mm, MM_TSB_HUGE, 0); | ||
300 | tsb_context_switch(mm); | ||
301 | smp_tsb_sync(mm); | ||
302 | |||
134 | /* On UltraSPARC-III+ and later, configure the second half of | 303 | /* On UltraSPARC-III+ and later, configure the second half of |
135 | * the Data-TLB for huge pages. | 304 | * the Data-TLB for huge pages. |
136 | */ | 305 | */ |