aboutsummaryrefslogtreecommitdiffstats
path: root/mm/hugetlb.c
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2005-10-20 00:23:43 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-20 02:04:30 -0400
commit1c59827d1da9bcd6970800d4f8a031b5859e8b4c (patch)
tree768e771e4da8f8f5a84d38b4b91d2fb852a4123a /mm/hugetlb.c
parente03d13e985d48ac4885382c9e3b1510c78bd047f (diff)
[PATCH] mm: hugetlb truncation fixes
hugetlbfs allows truncation of its files (should it?), but hugetlb.c often forgets that: crashes and misaccounting ensue. copy_hugetlb_page_range better grab the src page_table_lock since we don't want to guess what happens if concurrently truncated. unmap_hugepage_range rss accounting must not assume the full range was mapped. follow_hugetlb_page must guard with page_table_lock and be prepared to exit early. Restyle copy_hugetlb_page_range with a for loop like the others there. Signed-off-by: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r--mm/hugetlb.c35
1 files changed, 21 insertions, 14 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 901ac523a1c..a1b30d45459 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -274,21 +274,22 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
274{ 274{
275 pte_t *src_pte, *dst_pte, entry; 275 pte_t *src_pte, *dst_pte, entry;
276 struct page *ptepage; 276 struct page *ptepage;
277 unsigned long addr = vma->vm_start; 277 unsigned long addr;
278 unsigned long end = vma->vm_end;
279 278
280 while (addr < end) { 279 for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) {
281 dst_pte = huge_pte_alloc(dst, addr); 280 dst_pte = huge_pte_alloc(dst, addr);
282 if (!dst_pte) 281 if (!dst_pte)
283 goto nomem; 282 goto nomem;
283 spin_lock(&src->page_table_lock);
284 src_pte = huge_pte_offset(src, addr); 284 src_pte = huge_pte_offset(src, addr);
285 BUG_ON(!src_pte || pte_none(*src_pte)); /* prefaulted */ 285 if (src_pte && !pte_none(*src_pte)) {
286 entry = *src_pte; 286 entry = *src_pte;
287 ptepage = pte_page(entry); 287 ptepage = pte_page(entry);
288 get_page(ptepage); 288 get_page(ptepage);
289 add_mm_counter(dst, rss, HPAGE_SIZE / PAGE_SIZE); 289 add_mm_counter(dst, rss, HPAGE_SIZE / PAGE_SIZE);
290 set_huge_pte_at(dst, addr, dst_pte, entry); 290 set_huge_pte_at(dst, addr, dst_pte, entry);
291 addr += HPAGE_SIZE; 291 }
292 spin_unlock(&src->page_table_lock);
292 } 293 }
293 return 0; 294 return 0;
294 295
@@ -323,8 +324,8 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
323 324
324 page = pte_page(pte); 325 page = pte_page(pte);
325 put_page(page); 326 put_page(page);
327 add_mm_counter(mm, rss, - (HPAGE_SIZE / PAGE_SIZE));
326 } 328 }
327 add_mm_counter(mm, rss, -((end - start) >> PAGE_SHIFT));
328 flush_tlb_range(vma, start, end); 329 flush_tlb_range(vma, start, end);
329} 330}
330 331
@@ -403,6 +404,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
403 BUG_ON(!is_vm_hugetlb_page(vma)); 404 BUG_ON(!is_vm_hugetlb_page(vma));
404 405
405 vpfn = vaddr/PAGE_SIZE; 406 vpfn = vaddr/PAGE_SIZE;
407 spin_lock(&mm->page_table_lock);
406 while (vaddr < vma->vm_end && remainder) { 408 while (vaddr < vma->vm_end && remainder) {
407 409
408 if (pages) { 410 if (pages) {
@@ -415,8 +417,13 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
415 * indexing below to work. */ 417 * indexing below to work. */
416 pte = huge_pte_offset(mm, vaddr & HPAGE_MASK); 418 pte = huge_pte_offset(mm, vaddr & HPAGE_MASK);
417 419
418 /* hugetlb should be locked, and hence, prefaulted */ 420 /* the hugetlb file might have been truncated */
419 WARN_ON(!pte || pte_none(*pte)); 421 if (!pte || pte_none(*pte)) {
422 remainder = 0;
423 if (!i)
424 i = -EFAULT;
425 break;
426 }
420 427
421 page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)]; 428 page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];
422 429
@@ -434,7 +441,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
434 --remainder; 441 --remainder;
435 ++i; 442 ++i;
436 } 443 }
437 444 spin_unlock(&mm->page_table_lock);
438 *length = remainder; 445 *length = remainder;
439 *position = vaddr; 446 *position = vaddr;
440 447