Commit 082d5b6b60e9f25e1511557fcfcb21eedd267446

Authored by Gerald Schaefer
Committed by Linus Torvalds
1 parent 2247bb335a

mm/hugetlb: check for reserved hugepages during memory offline

In dissolve_free_huge_pages(), free hugepages will be dissolved without
making sure that there are enough of them left to satisfy hugepage
reservations.

Fix this by adding a return value to dissolve_free_huge_pages() and
checking h->free_huge_pages vs.  h->resv_huge_pages.  Note that this may
lead to the situation where dissolve_free_huge_page() returns an error
and all free hugepages that were dissolved before that error are lost,
while the memory block still cannot be set offline.

Fixes: c8721bbb ("mm: memory-hotplug: enable memory hotplug to handle hugepage")
Link: http://lkml.kernel.org/r/20160926172811.94033-3-gerald.schaefer@de.ibm.com
Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: "Aneesh Kumar K . V" <aneesh.kumar@linux.vnet.ibm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Rui Teng <rui.teng@linux.vnet.ibm.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 27 additions and 9 deletions Side-by-side Diff

include/linux/hugetlb.h
... ... @@ -450,8 +450,8 @@
450 450 return __basepage_index(page);
451 451 }
452 452  
453   -extern void dissolve_free_huge_pages(unsigned long start_pfn,
454   - unsigned long end_pfn);
  453 +extern int dissolve_free_huge_pages(unsigned long start_pfn,
  454 + unsigned long end_pfn);
455 455 static inline bool hugepage_migration_supported(struct hstate *h)
456 456 {
457 457 #ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION
... ... @@ -518,7 +518,7 @@
518 518 {
519 519 return page->index;
520 520 }
521   -#define dissolve_free_huge_pages(s, e) do {} while (0)
  521 +#define dissolve_free_huge_pages(s, e) 0
522 522 #define hugepage_migration_supported(h) false
523 523  
524 524 static inline spinlock_t *huge_pte_lockptr(struct hstate *h,
... ... @@ -1437,22 +1437,32 @@
1437 1437  
1438 1438 /*
1439 1439 * Dissolve a given free hugepage into free buddy pages. This function does
1440   - * nothing for in-use (including surplus) hugepages.
  1440 + * nothing for in-use (including surplus) hugepages. Returns -EBUSY if the
  1441 + * number of free hugepages would be reduced below the number of reserved
  1442 + * hugepages.
1441 1443 */
1442   -static void dissolve_free_huge_page(struct page *page)
  1444 +static int dissolve_free_huge_page(struct page *page)
1443 1445 {
  1446 + int rc = 0;
  1447 +
1444 1448 spin_lock(&hugetlb_lock);
1445 1449 if (PageHuge(page) && !page_count(page)) {
1446 1450 struct page *head = compound_head(page);
1447 1451 struct hstate *h = page_hstate(head);
1448 1452 int nid = page_to_nid(head);
  1453 + if (h->free_huge_pages - h->resv_huge_pages == 0) {
  1454 + rc = -EBUSY;
  1455 + goto out;
  1456 + }
1449 1457 list_del(&head->lru);
1450 1458 h->free_huge_pages--;
1451 1459 h->free_huge_pages_node[nid]--;
1452 1460 h->max_huge_pages--;
1453 1461 update_and_free_page(h, head);
1454 1462 }
  1463 +out:
1455 1464 spin_unlock(&hugetlb_lock);
  1465 + return rc;
1456 1466 }
1457 1467  
1458 1468 /*
1459 1469  
1460 1470  
1461 1471  
1462 1472  
... ... @@ -1460,16 +1470,22 @@
1460 1470 * make specified memory blocks removable from the system.
1461 1471 * Note that this will dissolve a free gigantic hugepage completely, if any
1462 1472 * part of it lies within the given range.
  1473 + * Also note that if dissolve_free_huge_page() returns with an error, all
  1474 + * free hugepages that were dissolved before that error are lost.
1463 1475 */
1464   -void dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
  1476 +int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
1465 1477 {
1466 1478 unsigned long pfn;
  1479 + int rc = 0;
1467 1480  
1468 1481 if (!hugepages_supported())
1469   - return;
  1482 + return rc;
1470 1483  
1471 1484 for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order)
1472   - dissolve_free_huge_page(pfn_to_page(pfn));
  1485 + if (rc = dissolve_free_huge_page(pfn_to_page(pfn)))
  1486 + break;
  1487 +
  1488 + return rc;
1473 1489 }
1474 1490  
1475 1491 /*
... ... @@ -1945,7 +1945,9 @@
1945 1945 * dissolve free hugepages in the memory block before doing offlining
1946 1946 * actually in order to make hugetlbfs's object counting consistent.
1947 1947 */
1948   - dissolve_free_huge_pages(start_pfn, end_pfn);
  1948 + ret = dissolve_free_huge_pages(start_pfn, end_pfn);
  1949 + if (ret)
  1950 + goto failed_removal;
1949 1951 /* check again */
1950 1952 offlined_pages = check_pages_isolated(start_pfn, end_pfn);
1951 1953 if (offlined_pages < 0) {