Commit 6b0c880dfefecedb9ad353014ed41505c32aca82

Authored by Adam Litke
Committed by Linus Torvalds
1 parent e63e1e5a6b

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>

Showing 1 changed file with 4 additions and 5 deletions Side-by-side Diff

... ... @@ -403,14 +403,14 @@
403 403 for (i = 0; i < MAX_NUMNODES; ++i) {
404 404 struct page *page, *next;
405 405 list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) {
  406 + if (count >= nr_huge_pages)
  407 + return;
406 408 if (PageHighMem(page))
407 409 continue;
408 410 list_del(&page->lru);
409 411 update_and_free_page(page);
410 412 free_huge_pages--;
411 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 @@
450 450 goto out;
451 451  
452 452 }
453   - if (count >= persistent_huge_pages)
454   - goto out;
455 453  
456 454 /*
457 455 * Decrease the pool size
... ... @@ -460,7 +458,8 @@
460 458 * pages into surplus state as needed so the pool will shrink
461 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 463 try_to_free_low(min_count);
465 464 while (min_count < persistent_huge_pages) {
466 465 struct page *page = dequeue_huge_page(NULL, 0);