Commit 082d5b6b60e9f25e1511557fcfcb21eedd267446
Committed by
Linus Torvalds
1 parent
2247bb335a
Exists in
smarct4x-processor-sdk-04.01.00.06
and in
1 other branch
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, |
mm/hugetlb.c
... | ... | @@ -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 | /* |
mm/memory_hotplug.c
... | ... | @@ -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) { |