When soft offlining a free hugtlb, try first to allocate a new hugetlb to the pool and pass the old state to the new one by move_hugetlb_state(). Either we succeed or not, we dissolve the poisoned hugetlb page. Worst-scenario case is that we cannot allocate a new fresh hugetlb page as a replacement. Signed-off-by: Oscar Salvador <osalvador@xxxxxxx> --- mm/hugetlb.c | 16 ++++++++++++++++ mm/memory-failure.c | 34 ++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 139e1c05c9a1..d0844aec7531 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5154,3 +5154,19 @@ void move_hugetlb_state(struct page *oldpage, struct page *newpage, int reason) spin_unlock(&hugetlb_lock); } } + +#ifdef CONFIG_MEMORY_FAILURE +int hugetlb_replace_page(struct page *page, int reason) +{ + int nid = page_to_nid(page); + struct hstate *h = page_hstate(page); + struct page *new_page; + + new_page = alloc_huge_page_nodemask(h, nid, &node_states[N_MEMORY]); + if (!new_page) + return -ENOMEM; + + move_hugetlb_state(page, new_page, reason); + return 0; +} +#endif diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 03f07015a106..fe73fe19c6e9 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -79,6 +79,7 @@ EXPORT_SYMBOL_GPL(hwpoison_filter_flags_mask); EXPORT_SYMBOL_GPL(hwpoison_filter_flags_value); extern bool take_page_off_buddy(struct page *page); +extern int hugetlb_replace_page(struct page *page, int reason); static bool page_set_poison(struct page *page) { @@ -1804,16 +1805,37 @@ static int soft_offline_in_use_page(struct page *page) return __soft_offline_page(page); } +static int soft_offline_free_huge_page(struct page *page) +{ + struct page *hpage = compound_head(page); + + /* + * Try to add a new hugetlb page to the pool + */ + if (hugetlb_replace_page(hpage, MR_MEMORY_FAILURE)) + return -EBUSY; + + /* + * Remove old hugetlb from the pool + */ + if (!page_set_poison(hpage)) + return -EBUSY; + + return 0; +} + static int soft_offline_free_page(struct page *page) { - int rc = dissolve_free_huge_page(page); + int rc = -EBUSY; - if (!rc) { - if (take_page_off_buddy(page)) + if (PageHuge(page)) + rc = soft_offline_free_huge_page(page); + else + if (take_page_off_buddy(page)) { page_set_poison(page); - else - rc = -EBUSY; - } + rc = 0; + } + return rc; } -- 2.12.3