From 6b0c880dfefecedb9ad353014ed41505c32aca82 Mon Sep 17 00:00:00 2001 From: Adam Litke Date: Tue, 16 Oct 2007 01:26:23 -0700 Subject: 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 Acked-by: Ken Chen Cc: David Gibson Cc: Badari Pulavarty Cc: William Lee Irwin III Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/hugetlb.c | 9 ++++----- 1 file 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) for (i = 0; i < MAX_NUMNODES; ++i) { struct page *page, *next; list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) { + if (count >= nr_huge_pages) + return; if (PageHighMem(page)) continue; list_del(&page->lru); update_and_free_page(page); free_huge_pages--; free_huge_pages_node[page_to_nid(page)]--; - if (count >= nr_huge_pages) - return; } } } @@ -450,8 +450,6 @@ static unsigned long set_max_huge_pages(unsigned long count) goto out; } - if (count >= persistent_huge_pages) - goto out; /* * Decrease the pool size @@ -460,7 +458,8 @@ static unsigned long set_max_huge_pages(unsigned long count) * pages into surplus state as needed so the pool will shrink * to the desired size as pages become free. */ - min_count = max(count, resv_huge_pages); + min_count = resv_huge_pages + nr_huge_pages - free_huge_pages; + min_count = max(count, min_count); try_to_free_low(min_count); while (min_count < persistent_huge_pages) { struct page *page = dequeue_huge_page(NULL, 0); -- cgit v1.2.2