Commit 6b0c880d authored by Adam Litke's avatar Adam Litke Committed by Linus Torvalds

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: default avatarAdam Litke <agl@us.ibm.com>
Acked-by: default avatarKen 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e63e1e5a
...@@ -403,14 +403,14 @@ static void try_to_free_low(unsigned long count) ...@@ -403,14 +403,14 @@ static void try_to_free_low(unsigned long count)
for (i = 0; i < MAX_NUMNODES; ++i) { for (i = 0; i < MAX_NUMNODES; ++i) {
struct page *page, *next; struct page *page, *next;
list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) { list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) {
if (count >= nr_huge_pages)
return;
if (PageHighMem(page)) if (PageHighMem(page))
continue; continue;
list_del(&page->lru); list_del(&page->lru);
update_and_free_page(page); update_and_free_page(page);
free_huge_pages--; free_huge_pages--;
free_huge_pages_node[page_to_nid(page)]--; 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) ...@@ -450,8 +450,6 @@ static unsigned long set_max_huge_pages(unsigned long count)
goto out; goto out;
} }
if (count >= persistent_huge_pages)
goto out;
/* /*
* Decrease the pool size * Decrease the pool size
...@@ -460,7 +458,8 @@ static unsigned long set_max_huge_pages(unsigned long count) ...@@ -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 * pages into surplus state as needed so the pool will shrink
* to the desired size as pages become free. * 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); try_to_free_low(min_count);
while (min_count < persistent_huge_pages) { while (min_count < persistent_huge_pages) {
struct page *page = dequeue_huge_page(NULL, 0); struct page *page = dequeue_huge_page(NULL, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment