diff options
author | Hugh Dickins <hugh@veritas.com> | 2005-10-20 00:23:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-20 02:04:30 -0400 |
commit | 1c59827d1da9bcd6970800d4f8a031b5859e8b4c (patch) | |
tree | 768e771e4da8f8f5a84d38b4b91d2fb852a4123a /mm/hugetlb.c | |
parent | e03d13e985d48ac4885382c9e3b1510c78bd047f (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.c | 35 |
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 | ||