So far, is_page_hwpoison() has returned true for the entire poisoned hugetlb folio, but we can usually allow access to the pages which aren't poisoned. It's slightly tricky due to not having a reference to the folio, but the hugetlb lock can save us here. Signed-off-by: Matthew Wilcox (Oracle) --- include/linux/page-flags.h | 4 +++- mm/memory-failure.c | 43 +++++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 4fab3fbfd430..ee026ba9b0b5 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -1094,6 +1094,8 @@ static inline bool PageHuge(const struct page *page) return folio_test_hugetlb(page_folio(page)); } +bool hugetlb_unref_page_hwpoison(const struct page *page); + /* * Check if a page is currently marked HWPoisoned. This check is best * effort only and inherently racy: there is no way to synchronize with @@ -1108,7 +1110,7 @@ static inline bool is_page_hwpoison(const struct page *page) return true; folio = page_folio(page); if (folio_test_huge_poison(folio)) - return true; + return hugetlb_unref_page_hwpoison(page); /* In case we raced with hugetlb transferring flags */ return PageHWPoison(page); } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 30470f8ea590..ab2094ed320c 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1820,17 +1820,12 @@ struct hwp_page { struct page *page; }; -/* - * Check if a given @page in a hugetlb folio is HWPOISON. - */ -bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page) +static bool precise_page_poisoned(const struct folio *folio, + const struct page *page) { const struct hwp_page *p; unsigned long flags; - if (!folio_test_has_hwpoisoned(folio)) - return false; - spin_lock_irqsave(&hwp_page_lock, flags); /* @@ -1851,6 +1846,40 @@ bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page) return p != NULL; } +/* + * Check if a given @page in a hugetlb folio is HWPOISON. + */ +bool hugetlb_page_hwpoison(const struct folio *folio, const struct page *page) +{ + if (!folio_test_has_hwpoisoned(folio)) + return false; + + return precise_page_poisoned(folio, page); +} + +/* + * We have no reference on the folio containing this page. + * The hugetlb_lock keeps hugetlb folios from being freed. + */ +bool hugetlb_unref_page_hwpoison(const struct page *page) +{ + const struct folio *folio; + unsigned long flags; + bool ret; + + spin_lock_irqsave(&hugetlb_lock, flags); + folio = page_folio(page); + if (!folio_test_huge_poison(folio)) { + ret = PageHWPoison(page); + goto unlock; + } + + ret = precise_page_poisoned(folio, page); +unlock: + spin_unlock_irqrestore(&hugetlb_lock, flags); + return ret; +} + static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag) { struct hwp_page *p, *next; -- 2.47.3