aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Litke <agl@us.ibm.com>2007-10-16 04:26:23 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:43:03 -0400
commit6b0c880dfefecedb9ad353014ed41505c32aca82 (patch)
tree2cf472a539f6de265deede9fb580be821bf9dda1
parente63e1e5a6b75416ab758025fd80e247c4f103b1b (diff)
hugetlb: fix pool resizing corner case
When shrinking the size of the hugetlb pool via the nr_hugepages sysctl, we are careful to keep enough pages around to satisfy reservations. But the calculation is flawed for the following scenario: Action Pool Counters (Total, Free, Resv) ====== ============= Set pool to 1 page 1 1 0 Map 1 page MAP_PRIVATE 1 1 0 Touch the page to fault it in 1 0 0 Set pool to 3 pages 3 2 0 Map 2 pages MAP_SHARED 3 2 2 Set pool to 2 pages 2 1 2 <-- Mistake, should be 3 2 2 Touch the 2 shared pages 2 0 1 <-- Program crashes here The last touch above will terminate the process due to lack of huge pages. This patch corrects the calculation so that it factors in pages being used for private mappings. Andrew, this is a standalone fix suitable for mainline. It is also now corrected in my latest dynamic pool resizing patchset which I will send out soon. Signed-off-by: Adam Litke <agl@us.ibm.com> Acked-by: Ken Chen <kenchen@google.com> Cc: David Gibson <david@gibson.dropbear.id.au> Cc: Badari Pulavarty <pbadari@us.ibm.com> Cc: William Lee Irwin III <wli@holomorphy.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/hugetlb.c9
1 files changed, 4 insertions, 5 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index efd78527ad1e..8fb86ba452b0 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -403,14 +403,14 @@ static void try_to_free_low(unsigned long count)
403 for (i = 0; i < MAX_NUMNODES; ++i) { 403 for (i = 0; i < MAX_NUMNODES; ++i) {
404 struct page *page, *next; 404 struct page *page, *next;
405 list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) { 405 list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) {
406 if (count >= nr_huge_pages)
407 return;
406 if (PageHighMem(page)) 408 if (PageHighMem(page))
407 continue; 409 continue;
408 list_del(&page->lru); 410 list_del(&page->lru);
409 update_and_free_page(page); 411 update_and_free_page(page);
410 free_huge_pages--; 412 free_huge_pages--;
411 free_huge_pages_node[page_to_nid(page)]--; 413 free_huge_pages_node[page_to_nid(page)]--;
412 if (count >= nr_huge_pages)
413 return;
414 } 414 }
415 } 415 }
416} 416}
@@ -450,8 +450,6 @@ static unsigned long set_max_huge_pages(unsigned long count)
450 goto out; 450 goto out;
451 451
452 } 452 }
453 if (count >= persistent_huge_pages)
454 goto out;
455 453
456 /* 454 /*
457 * Decrease the pool size 455 * Decrease the pool size
@@ -460,7 +458,8 @@ static unsigned long set_max_huge_pages(unsigned long count)
460 * pages into surplus state as needed so the pool will shrink 458 * pages into surplus state as needed so the pool will shrink
461 * to the desired size as pages become free. 459 * to the desired size as pages become free.
462 */ 460 */
463 min_count = max(count, resv_huge_pages); 461 min_count = resv_huge_pages + nr_huge_pages - free_huge_pages;
462 min_count = max(count, min_count);
464 try_to_free_low(min_count); 463 try_to_free_low(min_count);
465 while (min_count < persistent_huge_pages) { 464 while (min_count < persistent_huge_pages) {
466 struct page *page = dequeue_huge_page(NULL, 0); 465 struct page *page = dequeue_huge_page(NULL, 0);