From: sashiko-bot@kernel.org
To: "Mani Tofigh" <mt4022@columbia.edu>,
"Muchun Song" <muchun.song@linux.dev>,
"Oscar Salvador" <osalvador@suse.de>,
"Miaohe Lin" <linmiaohe@huawei.com>,
"Andrew Morton" <akpm@linux-foundation.org>
Cc: "David Hildenbrand" <david@kernel.org>,
"Naoya Horiguchi" <nao.horiguchi@gmail.com>,
"Kiryl Shutsemau" <kas@kernel.org>,
"Mike Rapoport" <rppt@kernel.org>,
"Mike Kravetz" <mike.kravetz@oracle.com>,
"Matthew Wilcox" <willy@infradead.org>,
linux-mm@kvack.org, tz2294@columbia.edu
Subject: Re: [PATCH v2] mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock
Date: Wed, 04 Nov 2026 21:09:08 +0000 [thread overview]
Message-ID: <sashiko-outbox-198049@kernel.org> (raw)
In-Reply-To: <20261103-hwpoison-hugetlb-ro-v2-1-43876fc9eef1@columbia.edu>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm/memory-failure: missed retry when poisoned page becomes hugetlb folio
- [High] mm/memory-failure: broken HVO assumptions in clear_non_hugetlb_hwpoison()
--
commit cd482078045f6c62698aca153687853e7f4a179f
Author: Mani Tofigh <mt4022@columbia.edu>
mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock
This commit sets the hugetlb flag on newly initialized hugetlb folios and
sets PG_hwpoison on non-hugetlb pages while holding hugetlb_lock in
get_huge_page_for_hwpoison(). The goal is to prevent memory_failure() from
setting PG_hwpoison on the shared vmemmap tail page if the target page
concurrently becomes part of an HVO-optimized hugetlb folio.
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index b215312f261b8..80bac8d544d40 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
[ ... ]
> @@ -2169,10 +2177,32 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
> return identify_page_state(pfn, p, page_flags);
> }
>
> +/*
> + * Keep the flag if the page has become a tail page of a hugetlb folio in
> + * the meantime, as HVO may remap its struct page. A head page is never
> + * remapped, and keeping the flag there would make the retry take the
> + * folio for already poisoned.
> + */
> +static void clear_non_hugetlb_hwpoison(struct page *page)
> +{
> + struct folio *folio;
> +
> + spin_lock_irq(&hugetlb_lock);
> + folio = page_folio(page);
> + if (!folio_test_hugetlb(folio) || &folio->page == page)
> + ClearPageHWPoison(page);
> + spin_unlock_irq(&hugetlb_lock);
> +}
[Severity: High]
As reported by Sashiko on v1 [1], does clear_non_hugetlb_hwpoison() rely on
accurate assumptions about how HVO remaps struct page entries?
First, the comment states that a head page is never remapped, yet
__hugetlb_vmemmap_optimize_folio() allocates a new vmemmap_head page, copies
the first vmemmap page into it, and remaps &folio->page to vmemmap_head
outside hugetlb_lock:
mm/hugetlb_vmemmap.c:__hugetlb_vmemmap_optimize_folio() {
vmemmap_head = alloc_pages_node(nid, GFP_KERNEL, 0);
if (!vmemmap_head) {
ret = -ENOMEM;
goto out;
}
copy_page(page_to_virt(vmemmap_head), folio);
...
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end,
vmemmap_head, vmemmap_tail,
vmemmap_pages, flags);
}
When page == &folio->page, if copy_page() runs after
get_huge_page_for_hwpoison() sets PG_hwpoison on page, and
clear_non_hugetlb_hwpoison() runs ClearPageHWPoison(page) before the PTE for
&folio->page is updated to vmemmap_head and TLB-flushed, ClearPageHWPoison()
only clears the flag on the old vmemmap page being freed. vmemmap_head keeps
PG_hwpoison set, so the retry in hugetlb_update_hwpoison() sees
folio_test_set_hwpoison(folio) == true and aborts with
MF_HUGETLB_FOLIO_PRE_POISONED (-EHWPOISON), leaving the poisoned hugetlb
folio undissolved.
Second, when page is a tail page (&folio->page != page) and
folio_test_hugetlb(folio) is true, clear_non_hugetlb_hwpoison()
unconditionally skips ClearPageHWPoison(page). However, tail pages 1..63
reside in the first 4KB vmemmap page (HUGETLB_VMEMMAP_RESERVE_SIZE), which is
copied to vmemmap_head and is never remapped to vmemmap_tail or overwritten
by __hugetlb_vmemmap_restore_folio(); and when HVO is disabled or fails, no
tail pages are remapped at all.
If PG_hwpoison is left set on one of those non-remapped tail struct pages,
can this cause recovery to abort with -EEXIST if the hugetlb folio is freed
before try_again re-acquires hugetlb_lock (such as when
alloc_surplus_hugetlb_folio() fails its overcommit recheck), or trigger
bad_page() upon buddy free if the hugetlb error is later filtered by
hwpoison_filter() or cleared via unpoison_memory()?
[ ... ]
> @@ -2486,7 +2517,7 @@ int memory_failure(unsigned long pfn, int flags)
> } else {
> /* We lost the race, try again */
> if (retry) {
> - ClearPageHWPoison(p);
> + clear_non_hugetlb_hwpoison(p);
> retry = false;
> goto try_again;
> }
[Severity: High]
As reported by Sashiko on v1 [1], is this retry block actually reached when a
non-hugetlb page becomes part of a new hugetlb folio after
get_huge_page_for_hwpoison() sets PG_hwpoison and drops hugetlb_lock?
In only_alloc_fresh_hugetlb_folio(), alloc_buddy_frozen_folio() and
alloc_gigantic_frozen_folio() remove the pages from the buddy or contig
allocator (clearing PageBuddy and preparing the compound page) before calling
init_new_hugetlb_folio() to take hugetlb_lock and set PG_hugetlb;
demote_free_hugetlb_folios() similarly prepares split compound folios before
calling init_new_hugetlb_folio(). Moreover, when check_pages_enabled is false
or in alloc_contig_frozen_range_noprof() (which ignores the return value of
check_new_pages()), the page allocator does not reject a free buddy page with
PG_hwpoison set, and get_hwpoison_page() sleeps in zone_pcp_disable() before
inspecting p.
In memory_failure(), clear_non_hugetlb_hwpoison(p) and goto try_again are
gated behind get_hwpoison_page(p, flags) returning 0 and
is_free_buddy_page(p) returning true:
res = get_hwpoison_page(p, flags);
switch (res) {
case 0:
if (is_free_buddy_page(p)) {
if (take_page_off_buddy(p)) {
...
} else {
/* We lost the race, try again */
if (retry) {
clear_non_hugetlb_hwpoison(p);
retry = false;
goto try_again;
}
res = MF_FAILED;
}
res = action_result(pfn, MF_MSG_BUDDY, res);
} else {
res = action_result(pfn, MF_MSG_KERNEL_HIGH_ORDER, MF_IGNORED);
}
goto unlock_mutex;
Whenever the concurrent hugetlb allocation has already removed p from the
buddy freelist before memory_failure() reaches is_free_buddy_page(p), won't
the retry path be bypassed in all three hugetlb states?
1. If the new hugetlb folio is on the hugetlb freelist (HPG_freed set),
get_hwpoison_hugetlb_folio() and get_any_page() return 0,
is_free_buddy_page(p) is false, and memory_failure() exits via the
MF_MSG_KERNEL_HIGH_ORDER branch without retrying.
2. If the new hugetlb folio is still in init_new_hugetlb_folio() or
hugetlb_vmemmap_optimize_folios() (!HPG_freed and !HPG_migratable),
get_hwpoison_hugetlb_folio() returns -EBUSY, get_any_page() returns -EIO
after exhausting its retries, and memory_failure() exits via the default
MF_MSG_GET_HWPOISON branch without retrying.
3. If the new hugetlb folio has already become in-use (HPG_migratable set),
get_hwpoison_hugetlb_folio() pins the folio and returns 1, and
get_any_page() sees PageHuge(p) and returns 1. memory_failure() then falls
through into the non-hugetlb in-use path with a hugetlb folio: if
hwpoison_filter(p) matches, it calls ClearPageHWPoison(p) without
hugetlb_lock (touching the shared vmemmap_tail page if HVO-optimized), and
otherwise it calls folio_set_has_hwpoisoned(folio) and
split_huge_page_to_order() on the hugetlb folio.
In the first two cases, if p became a remapped tail page (>= 64) of an
HVO-optimized hugetlb folio, freeing the original vmemmap page discards
PG_hwpoison and leaves the poisoned hugetlb folio in the pool; if p became
the head page, PG_hwpoison stays set with an empty raw_hwp_page list and is
cleared when the folio is later freed; and if p became a non-remapped tail
page, PG_hwpoison stays on the tail struct page and triggers bad_page() when
the folio is freed.
[1] https://lore.kernel.org/r/20261101-hwpoison-hugetlb-ro-v1-1-a42d6411cb65@columbia.edu
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261103-hwpoison-hugetlb-ro-v2-1-43876fc9eef1@columbia.edu?part=1
prev parent reply other threads:[~2026-11-04 21:09 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 21:08 [PATCH v2] mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock Mani Tofigh via B4 Relay
2026-11-03 21:08 ` Mani Tofigh
2026-11-04 21:09 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-198049@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=kas@kernel.org \
--cc=linmiaohe@huawei.com \
--cc=linux-mm@kvack.org \
--cc=mike.kravetz@oracle.com \
--cc=mt4022@columbia.edu \
--cc=muchun.song@linux.dev \
--cc=nao.horiguchi@gmail.com \
--cc=osalvador@suse.de \
--cc=rppt@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tz2294@columbia.edu \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.