diff options
Diffstat (limited to 'arch/i386/mm/hugetlbpage.c')
-rw-r--r-- | arch/i386/mm/hugetlbpage.c | 204 |
1 files changed, 37 insertions, 167 deletions
diff --git a/arch/i386/mm/hugetlbpage.c b/arch/i386/mm/hugetlbpage.c index 171fc925e1e4..3b099f32b948 100644 --- a/arch/i386/mm/hugetlbpage.c +++ b/arch/i386/mm/hugetlbpage.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include <asm/tlb.h> | 18 | #include <asm/tlb.h> |
19 | #include <asm/tlbflush.h> | 19 | #include <asm/tlbflush.h> |
20 | 20 | ||
21 | static pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | 21 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) |
22 | { | 22 | { |
23 | pgd_t *pgd; | 23 | pgd_t *pgd; |
24 | pud_t *pud; | 24 | pud_t *pud; |
@@ -30,7 +30,7 @@ static pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | |||
30 | return (pte_t *) pmd; | 30 | return (pte_t *) pmd; |
31 | } | 31 | } |
32 | 32 | ||
33 | static pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | 33 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) |
34 | { | 34 | { |
35 | pgd_t *pgd; | 35 | pgd_t *pgd; |
36 | pud_t *pud; | 36 | pud_t *pud; |
@@ -42,21 +42,6 @@ static pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | |||
42 | return (pte_t *) pmd; | 42 | return (pte_t *) pmd; |
43 | } | 43 | } |
44 | 44 | ||
45 | static void set_huge_pte(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page, pte_t * page_table, int write_access) | ||
46 | { | ||
47 | pte_t entry; | ||
48 | |||
49 | add_mm_counter(mm, rss, HPAGE_SIZE / PAGE_SIZE); | ||
50 | if (write_access) { | ||
51 | entry = | ||
52 | pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))); | ||
53 | } else | ||
54 | entry = pte_wrprotect(mk_pte(page, vma->vm_page_prot)); | ||
55 | entry = pte_mkyoung(entry); | ||
56 | mk_pte_huge(entry); | ||
57 | set_pte(page_table, entry); | ||
58 | } | ||
59 | |||
60 | /* | 45 | /* |
61 | * This function checks for proper alignment of input addr and len parameters. | 46 | * This function checks for proper alignment of input addr and len parameters. |
62 | */ | 47 | */ |
@@ -69,77 +54,6 @@ int is_aligned_hugepage_range(unsigned long addr, unsigned long len) | |||
69 | return 0; | 54 | return 0; |
70 | } | 55 | } |
71 | 56 | ||
72 | int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, | ||
73 | struct vm_area_struct *vma) | ||
74 | { | ||
75 | pte_t *src_pte, *dst_pte, entry; | ||
76 | struct page *ptepage; | ||
77 | unsigned long addr = vma->vm_start; | ||
78 | unsigned long end = vma->vm_end; | ||
79 | |||
80 | while (addr < end) { | ||
81 | dst_pte = huge_pte_alloc(dst, addr); | ||
82 | if (!dst_pte) | ||
83 | goto nomem; | ||
84 | src_pte = huge_pte_offset(src, addr); | ||
85 | entry = *src_pte; | ||
86 | ptepage = pte_page(entry); | ||
87 | get_page(ptepage); | ||
88 | set_pte(dst_pte, entry); | ||
89 | add_mm_counter(dst, rss, HPAGE_SIZE / PAGE_SIZE); | ||
90 | addr += HPAGE_SIZE; | ||
91 | } | ||
92 | return 0; | ||
93 | |||
94 | nomem: | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | |||
98 | int | ||
99 | follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, | ||
100 | struct page **pages, struct vm_area_struct **vmas, | ||
101 | unsigned long *position, int *length, int i) | ||
102 | { | ||
103 | unsigned long vpfn, vaddr = *position; | ||
104 | int remainder = *length; | ||
105 | |||
106 | WARN_ON(!is_vm_hugetlb_page(vma)); | ||
107 | |||
108 | vpfn = vaddr/PAGE_SIZE; | ||
109 | while (vaddr < vma->vm_end && remainder) { | ||
110 | |||
111 | if (pages) { | ||
112 | pte_t *pte; | ||
113 | struct page *page; | ||
114 | |||
115 | pte = huge_pte_offset(mm, vaddr); | ||
116 | |||
117 | /* hugetlb should be locked, and hence, prefaulted */ | ||
118 | WARN_ON(!pte || pte_none(*pte)); | ||
119 | |||
120 | page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)]; | ||
121 | |||
122 | WARN_ON(!PageCompound(page)); | ||
123 | |||
124 | get_page(page); | ||
125 | pages[i] = page; | ||
126 | } | ||
127 | |||
128 | if (vmas) | ||
129 | vmas[i] = vma; | ||
130 | |||
131 | vaddr += PAGE_SIZE; | ||
132 | ++vpfn; | ||
133 | --remainder; | ||
134 | ++i; | ||
135 | } | ||
136 | |||
137 | *length = remainder; | ||
138 | *position = vaddr; | ||
139 | |||
140 | return i; | ||
141 | } | ||
142 | |||
143 | #if 0 /* This is just for testing */ | 57 | #if 0 /* This is just for testing */ |
144 | struct page * | 58 | struct page * |
145 | follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) | 59 | follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) |
@@ -204,83 +118,15 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, | |||
204 | } | 118 | } |
205 | #endif | 119 | #endif |
206 | 120 | ||
207 | void unmap_hugepage_range(struct vm_area_struct *vma, | 121 | void hugetlb_clean_stale_pgtable(pte_t *pte) |
208 | unsigned long start, unsigned long end) | ||
209 | { | 122 | { |
210 | struct mm_struct *mm = vma->vm_mm; | 123 | pmd_t *pmd = (pmd_t *) pte; |
211 | unsigned long address; | ||
212 | pte_t pte, *ptep; | ||
213 | struct page *page; | 124 | struct page *page; |
214 | 125 | ||
215 | BUG_ON(start & (HPAGE_SIZE - 1)); | 126 | page = pmd_page(*pmd); |
216 | BUG_ON(end & (HPAGE_SIZE - 1)); | 127 | pmd_clear(pmd); |
217 | 128 | dec_page_state(nr_page_table_pages); | |
218 | for (address = start; address < end; address += HPAGE_SIZE) { | 129 | page_cache_release(page); |
219 | ptep = huge_pte_offset(mm, address); | ||
220 | if (!ptep) | ||
221 | continue; | ||
222 | pte = ptep_get_and_clear(mm, address, ptep); | ||
223 | if (pte_none(pte)) | ||
224 | continue; | ||
225 | page = pte_page(pte); | ||
226 | put_page(page); | ||
227 | } | ||
228 | add_mm_counter(mm ,rss, -((end - start) >> PAGE_SHIFT)); | ||
229 | flush_tlb_range(vma, start, end); | ||
230 | } | ||
231 | |||
232 | int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma) | ||
233 | { | ||
234 | struct mm_struct *mm = current->mm; | ||
235 | unsigned long addr; | ||
236 | int ret = 0; | ||
237 | |||
238 | BUG_ON(vma->vm_start & ~HPAGE_MASK); | ||
239 | BUG_ON(vma->vm_end & ~HPAGE_MASK); | ||
240 | |||
241 | spin_lock(&mm->page_table_lock); | ||
242 | for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) { | ||
243 | unsigned long idx; | ||
244 | pte_t *pte = huge_pte_alloc(mm, addr); | ||
245 | struct page *page; | ||
246 | |||
247 | if (!pte) { | ||
248 | ret = -ENOMEM; | ||
249 | goto out; | ||
250 | } | ||
251 | |||
252 | if (!pte_none(*pte)) | ||
253 | continue; | ||
254 | |||
255 | idx = ((addr - vma->vm_start) >> HPAGE_SHIFT) | ||
256 | + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); | ||
257 | page = find_get_page(mapping, idx); | ||
258 | if (!page) { | ||
259 | /* charge the fs quota first */ | ||
260 | if (hugetlb_get_quota(mapping)) { | ||
261 | ret = -ENOMEM; | ||
262 | goto out; | ||
263 | } | ||
264 | page = alloc_huge_page(); | ||
265 | if (!page) { | ||
266 | hugetlb_put_quota(mapping); | ||
267 | ret = -ENOMEM; | ||
268 | goto out; | ||
269 | } | ||
270 | ret = add_to_page_cache(page, mapping, idx, GFP_ATOMIC); | ||
271 | if (! ret) { | ||
272 | unlock_page(page); | ||
273 | } else { | ||
274 | hugetlb_put_quota(mapping); | ||
275 | free_huge_page(page); | ||
276 | goto out; | ||
277 | } | ||
278 | } | ||
279 | set_huge_pte(mm, vma, page, pte, vma->vm_flags & VM_WRITE); | ||
280 | } | ||
281 | out: | ||
282 | spin_unlock(&mm->page_table_lock); | ||
283 | return ret; | ||
284 | } | 130 | } |
285 | 131 | ||
286 | /* x86_64 also uses this file */ | 132 | /* x86_64 also uses this file */ |
@@ -294,7 +140,12 @@ static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file, | |||
294 | struct vm_area_struct *vma; | 140 | struct vm_area_struct *vma; |
295 | unsigned long start_addr; | 141 | unsigned long start_addr; |
296 | 142 | ||
297 | start_addr = mm->free_area_cache; | 143 | if (len > mm->cached_hole_size) { |
144 | start_addr = mm->free_area_cache; | ||
145 | } else { | ||
146 | start_addr = TASK_UNMAPPED_BASE; | ||
147 | mm->cached_hole_size = 0; | ||
148 | } | ||
298 | 149 | ||
299 | full_search: | 150 | full_search: |
300 | addr = ALIGN(start_addr, HPAGE_SIZE); | 151 | addr = ALIGN(start_addr, HPAGE_SIZE); |
@@ -308,6 +159,7 @@ full_search: | |||
308 | */ | 159 | */ |
309 | if (start_addr != TASK_UNMAPPED_BASE) { | 160 | if (start_addr != TASK_UNMAPPED_BASE) { |
310 | start_addr = TASK_UNMAPPED_BASE; | 161 | start_addr = TASK_UNMAPPED_BASE; |
162 | mm->cached_hole_size = 0; | ||
311 | goto full_search; | 163 | goto full_search; |
312 | } | 164 | } |
313 | return -ENOMEM; | 165 | return -ENOMEM; |
@@ -316,6 +168,8 @@ full_search: | |||
316 | mm->free_area_cache = addr + len; | 168 | mm->free_area_cache = addr + len; |
317 | return addr; | 169 | return addr; |
318 | } | 170 | } |
171 | if (addr + mm->cached_hole_size < vma->vm_start) | ||
172 | mm->cached_hole_size = vma->vm_start - addr; | ||
319 | addr = ALIGN(vma->vm_end, HPAGE_SIZE); | 173 | addr = ALIGN(vma->vm_end, HPAGE_SIZE); |
320 | } | 174 | } |
321 | } | 175 | } |
@@ -327,12 +181,17 @@ static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file, | |||
327 | struct mm_struct *mm = current->mm; | 181 | struct mm_struct *mm = current->mm; |
328 | struct vm_area_struct *vma, *prev_vma; | 182 | struct vm_area_struct *vma, *prev_vma; |
329 | unsigned long base = mm->mmap_base, addr = addr0; | 183 | unsigned long base = mm->mmap_base, addr = addr0; |
184 | unsigned long largest_hole = mm->cached_hole_size; | ||
330 | int first_time = 1; | 185 | int first_time = 1; |
331 | 186 | ||
332 | /* don't allow allocations above current base */ | 187 | /* don't allow allocations above current base */ |
333 | if (mm->free_area_cache > base) | 188 | if (mm->free_area_cache > base) |
334 | mm->free_area_cache = base; | 189 | mm->free_area_cache = base; |
335 | 190 | ||
191 | if (len <= largest_hole) { | ||
192 | largest_hole = 0; | ||
193 | mm->free_area_cache = base; | ||
194 | } | ||
336 | try_again: | 195 | try_again: |
337 | /* make sure it can fit in the remaining address space */ | 196 | /* make sure it can fit in the remaining address space */ |
338 | if (mm->free_area_cache < len) | 197 | if (mm->free_area_cache < len) |
@@ -353,13 +212,21 @@ try_again: | |||
353 | * vma->vm_start, use it: | 212 | * vma->vm_start, use it: |
354 | */ | 213 | */ |
355 | if (addr + len <= vma->vm_start && | 214 | if (addr + len <= vma->vm_start && |
356 | (!prev_vma || (addr >= prev_vma->vm_end))) | 215 | (!prev_vma || (addr >= prev_vma->vm_end))) { |
357 | /* remember the address as a hint for next time */ | 216 | /* remember the address as a hint for next time */ |
358 | return (mm->free_area_cache = addr); | 217 | mm->cached_hole_size = largest_hole; |
359 | else | 218 | return (mm->free_area_cache = addr); |
219 | } else { | ||
360 | /* pull free_area_cache down to the first hole */ | 220 | /* pull free_area_cache down to the first hole */ |
361 | if (mm->free_area_cache == vma->vm_end) | 221 | if (mm->free_area_cache == vma->vm_end) { |
362 | mm->free_area_cache = vma->vm_start; | 222 | mm->free_area_cache = vma->vm_start; |
223 | mm->cached_hole_size = largest_hole; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /* remember the largest hole we saw so far */ | ||
228 | if (addr + largest_hole < vma->vm_start) | ||
229 | largest_hole = vma->vm_start - addr; | ||
363 | 230 | ||
364 | /* try just below the current vma->vm_start */ | 231 | /* try just below the current vma->vm_start */ |
365 | addr = (vma->vm_start - len) & HPAGE_MASK; | 232 | addr = (vma->vm_start - len) & HPAGE_MASK; |
@@ -372,6 +239,7 @@ fail: | |||
372 | */ | 239 | */ |
373 | if (first_time) { | 240 | if (first_time) { |
374 | mm->free_area_cache = base; | 241 | mm->free_area_cache = base; |
242 | largest_hole = 0; | ||
375 | first_time = 0; | 243 | first_time = 0; |
376 | goto try_again; | 244 | goto try_again; |
377 | } | 245 | } |
@@ -382,6 +250,7 @@ fail: | |||
382 | * allocations. | 250 | * allocations. |
383 | */ | 251 | */ |
384 | mm->free_area_cache = TASK_UNMAPPED_BASE; | 252 | mm->free_area_cache = TASK_UNMAPPED_BASE; |
253 | mm->cached_hole_size = ~0UL; | ||
385 | addr = hugetlb_get_unmapped_area_bottomup(file, addr0, | 254 | addr = hugetlb_get_unmapped_area_bottomup(file, addr0, |
386 | len, pgoff, flags); | 255 | len, pgoff, flags); |
387 | 256 | ||
@@ -389,6 +258,7 @@ fail: | |||
389 | * Restore the topdown base: | 258 | * Restore the topdown base: |
390 | */ | 259 | */ |
391 | mm->free_area_cache = base; | 260 | mm->free_area_cache = base; |
261 | mm->cached_hole_size = ~0UL; | ||
392 | 262 | ||
393 | return addr; | 263 | return addr; |
394 | } | 264 | } |