summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2019-06-28 15:06:56 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-06-29 04:43:45 -0400
commitfaf53def3b143df11062d87c12afe6afeb6f8cc7 (patch)
tree63b2a1920766dd92f9d273713834aef5071d50b2
parentb38e5962f8ed0d2a2b28a887fc2221f7f41db119 (diff)
mm: hugetlb: soft-offline: dissolve_free_huge_page() return zero on !PageHuge
madvise(MADV_SOFT_OFFLINE) often returns -EBUSY when calling soft offline for hugepages with overcommitting enabled. That was caused by the suboptimal code in current soft-offline code. See the following part: ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL, MIGRATE_SYNC, MR_MEMORY_FAILURE); if (ret) { ... } else { /* * We set PG_hwpoison only when the migration source hugepage * was successfully dissolved, because otherwise hwpoisoned * hugepage remains on free hugepage list, then userspace will * find it as SIGBUS by allocation failure. That's not expected * in soft-offlining. */ ret = dissolve_free_huge_page(page); if (!ret) { if (set_hwpoison_free_buddy_page(page)) num_poisoned_pages_inc(); } } return ret; Here dissolve_free_huge_page() returns -EBUSY if the migration source page was freed into buddy in migrate_pages(), but even in that case we actually has a chance that set_hwpoison_free_buddy_page() succeeds. So that means current code gives up offlining too early now. dissolve_free_huge_page() checks that a given hugepage is suitable for dissolving, where we should return success for !PageHuge() case because the given hugepage is considered as already dissolved. This change also affects other callers of dissolve_free_huge_page(), which are cleaned up together. [n-horiguchi@ah.jp.nec.com: v3] Link: http://lkml.kernel.org/r/1560761476-4651-3-git-send-email-n-horiguchi@ah.jp.nec.comLink: http://lkml.kernel.org/r/1560154686-18497-3-git-send-email-n-horiguchi@ah.jp.nec.com Fixes: 6bc9b56433b76 ("mm: fix race on soft-offlining") Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Reported-by: Chen, Jerry T <jerry.t.chen@intel.com> Tested-by: Chen, Jerry T <jerry.t.chen@intel.com> Reviewed-by: Mike Kravetz <mike.kravetz@oracle.com> Reviewed-by: Oscar Salvador <osalvador@suse.de> Cc: Michal Hocko <mhocko@kernel.org> Cc: Xishi Qiu <xishi.qiuxishi@alibaba-inc.com> Cc: "Chen, Jerry T" <jerry.t.chen@intel.com> Cc: "Zhuo, Qiuxu" <qiuxu.zhuo@intel.com> Cc: <stable@vger.kernel.org> [4.19+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/hugetlb.c29
-rw-r--r--mm/memory-failure.c5
2 files changed, 21 insertions, 13 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ac843d32b019..ede7e7f5d1ab 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1510,16 +1510,29 @@ static int free_pool_huge_page(struct hstate *h, nodemask_t *nodes_allowed,
1510 1510
1511/* 1511/*
1512 * Dissolve a given free hugepage into free buddy pages. This function does 1512 * Dissolve a given free hugepage into free buddy pages. This function does
1513 * nothing for in-use (including surplus) hugepages. Returns -EBUSY if the 1513 * nothing for in-use hugepages and non-hugepages.
1514 * dissolution fails because a give page is not a free hugepage, or because 1514 * This function returns values like below:
1515 * free hugepages are fully reserved. 1515 *
1516 * -EBUSY: failed to dissolved free hugepages or the hugepage is in-use
1517 * (allocated or reserved.)
1518 * 0: successfully dissolved free hugepages or the page is not a
1519 * hugepage (considered as already dissolved)
1516 */ 1520 */
1517int dissolve_free_huge_page(struct page *page) 1521int dissolve_free_huge_page(struct page *page)
1518{ 1522{
1519 int rc = -EBUSY; 1523 int rc = -EBUSY;
1520 1524
1525 /* Not to disrupt normal path by vainly holding hugetlb_lock */
1526 if (!PageHuge(page))
1527 return 0;
1528
1521 spin_lock(&hugetlb_lock); 1529 spin_lock(&hugetlb_lock);
1522 if (PageHuge(page) && !page_count(page)) { 1530 if (!PageHuge(page)) {
1531 rc = 0;
1532 goto out;
1533 }
1534
1535 if (!page_count(page)) {
1523 struct page *head = compound_head(page); 1536 struct page *head = compound_head(page);
1524 struct hstate *h = page_hstate(head); 1537 struct hstate *h = page_hstate(head);
1525 int nid = page_to_nid(head); 1538 int nid = page_to_nid(head);
@@ -1564,11 +1577,9 @@ int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
1564 1577
1565 for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order) { 1578 for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order) {
1566 page = pfn_to_page(pfn); 1579 page = pfn_to_page(pfn);
1567 if (PageHuge(page) && !page_count(page)) { 1580 rc = dissolve_free_huge_page(page);
1568 rc = dissolve_free_huge_page(page); 1581 if (rc)
1569 if (rc) 1582 break;
1570 break;
1571 }
1572 } 1583 }
1573 1584
1574 return rc; 1585 return rc;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 8ee7b16235ac..d9cc6606f409 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1856,11 +1856,8 @@ static int soft_offline_in_use_page(struct page *page, int flags)
1856 1856
1857static int soft_offline_free_page(struct page *page) 1857static int soft_offline_free_page(struct page *page)
1858{ 1858{
1859 int rc = 0; 1859 int rc = dissolve_free_huge_page(page);
1860 struct page *head = compound_head(page);
1861 1860
1862 if (PageHuge(head))
1863 rc = dissolve_free_huge_page(page);
1864 if (!rc) { 1861 if (!rc) {
1865 if (set_hwpoison_free_buddy_page(page)) 1862 if (set_hwpoison_free_buddy_page(page))
1866 num_poisoned_pages_inc(); 1863 num_poisoned_pages_inc();