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 v3] mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock
Date: Sat, 07 Nov 2026 16:10:02 +0000 [thread overview]
Message-ID: <sashiko-outbox-203024@kernel.org> (raw)
In-Reply-To: <20261106-hwpoison-hugetlb-ro-v3-1-c11186963647@columbia.edu>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] mm/memory-failure: missed retry in memory_failure() on hugetlb
allocation
- [High] mm/memory-failure: first vmemmap page HVO race in
clear_non_hugetlb_hwpoison()
- [Medium] mm/memory-failure: stale tail PG_hwpoison in
clear_non_hugetlb_hwpoison()
--
commit 473f986d60ad157a360960de8cae3064b0d63305
Author: Mani Tofigh <mt4022@columbia.edu>
mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock
This commit sets PG_hugetlb on new hugetlb folios and sets PG_hwpoison on
non-hugetlb pages under hugetlb_lock to prevent racing with HugeTLB vmemmap
optimization (HVO), and adds a helper to conditionally clear PG_hwpoison on
the memory_failure() retry path.
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 78f0a13349cb3..9795f07b26f59 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
[ ... ]
> @@ -2170,10 +2179,32 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags)
> return identify_page_state(pfn, p, page_flags);
> }
>
> +/*
> + * If the page has become part of a hugetlb folio in the meantime, HVO may
> + * remap its struct page unless it is in the first vmemmap page of the
> + * folio. Keep the flag then, since the retry takes it as its own.
> + */
> +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_idx(folio, page) < VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES)
> + ClearPageHWPoison(page);
[Severity: High]
Does HVO also remap the first vmemmap page of a hugetlb folio without holding
hugetlb_lock?
In vmemmap_optimize_pte(), when vmemmap_optimizable_pfn() is false (which is
the first vmemmap page of the folio), HVO duplicates src into a newly
allocated page dst, remaps the PTE to dst, and frees src:
mm/hugetlb_vmemmap.c:vmemmap_optimize_pte() {
if (likely(vmemmap_optimizable_pfn(page_to_pfn(page), folio_order(folio)))) {
dst = folio_vmemmap_shared_tail_page(folio);
if (!dst)
return -ENOMEM;
} else {
dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
}
list_add(&src->lru, walk->vmemmap_pages);
...
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
}
Because init_new_hugetlb_folio() sets PG_hugetlb before HVO runs, this creates
a race window on the first vmemmap page:
CPU0 (memory_failure) CPU1 (hugetlb allocation)
get_huge_page_for_hwpoison()
TestSetPageHWPoison(page)
init_new_hugetlb_folio()
__folio_set_hugetlb(folio)
vmemmap_optimize_pte()
dst = vmemmap_dup_page_from_list(src, ...)
copy_page(dst, src)
clear_non_hugetlb_hwpoison()
ClearPageHWPoison(page) <-- clears old src page
set_pte_at(..., dst, ...)
flush_tlb_all()
After the remap, dst still has PG_hwpoison set. If page is the head page
(index 0), the retry in get_huge_page_for_hwpoison() ->
hugetlb_update_hwpoison() sees folio_test_set_hwpoison(folio) is already true
on dst and aborts recovery with -EHWPOISON (MF_FAILED), leaving the poisoned
hugetlb folio undissolved in the pool or mapped in user processes with an
empty raw_hwp_page list. Conversely, if hugetlb_update_hwpoison() runs
between copy_page() and set_pte_at()/flush_tlb_all(), its writes to
folio->flags and folio->_hugetlb_hwpoison (at struct page index 3, also in
the first vmemmap page) land in the displaced src page and are lost when src
is freed.
> + spin_unlock_irq(&hugetlb_lock);
> +}
[Severity: Medium]
When folio_test_hugetlb(folio) is true and folio_page_idx(folio, page) is at
least VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES, can skipping ClearPageHWPoison()
leave a stale PG_hwpoison bit on a tail struct page?
When HVO is disabled, unsupported for the hstate, fails, or has not run yet,
the tail struct page is not remapped. On the retry path,
try_memory_failure_hugetlb() -> get_huge_page_for_hwpoison() ->
hugetlb_update_hwpoison() sets PG_hwpoison on the head page and adds page to
raw_hwp_list_head(folio), but never clears PG_hwpoison on the tail struct
page.
If unpoison_memory() is later called on that tail PFN while the hugetlb folio
is in use (ghp > 0), it frees raw_hwp_list_head(folio), drops the ghp
reference, and then TestClearPageHWPoison(p) succeeds on the tail page and
calls folio_put(folio) a second time, dropping the active reference on the
in-use hugetlb folio while leaving PG_hwpoison set on the head page:
mm/memory-failure.c:unpoison_memory() {
} else {
if (folio_test_hugetlb(folio)) {
huge = true;
count = folio_free_raw_hwp(folio, false);
if (count == 0) {
folio_put(folio);
goto unlock_mutex;
}
}
folio_put(folio);
if (TestClearPageHWPoison(p)) {
folio_put(folio);
ret = 0;
}
}
}
[ ... ]
> @@ -2487,7 +2520,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]
Can memory_failure() miss this retry altogether when a page poisoned in
get_huge_page_for_hwpoison() becomes part of a new hugetlb folio?
In only_alloc_fresh_hugetlb_folio(), alloc_buddy_frozen_folio() and
alloc_gigantic_frozen_folio() remove the pages from the buddy allocator and
clear PageBuddy before init_new_hugetlb_folio() acquires hugetlb_lock and sets
PG_hugetlb. When check_pages_enabled is false (the default when
CONFIG_DEBUG_VM is disabled and init_on_alloc/page_poison are off) or in
alloc_contig_frozen_range_noprof() (which ignores check_new_pages()'s return
value), the allocator does not reject a page with PG_hwpoison set:
CPU0 (memory_failure) CPU1 (hugetlb allocation)
only_alloc_fresh_hugetlb_folio()
alloc_buddy_frozen_folio()
/* clears PageBuddy(p) */
get_huge_page_for_hwpoison()
/* !folio_test_hugetlb(folio) */
TestSetPageHWPoison(p)
return MF_HUGETLB_NON_HUGEPAGE
init_new_hugetlb_folio()
__folio_set_hugetlb(folio)
hugetlb_vmemmap_optimize_folio()
/* remaps p to shared tail page,
frees old vmemmap page */
get_hwpoison_page(p, flags)
get_hwpoison_hugetlb_folio()
/* returns -EBUSY or 0 */
/* skips is_free_buddy_page(p) retry,
returns MF_IGNORED */ <-- PG_hwpoison lost
Because the retry block in memory_failure() is only reached when
get_hwpoison_page() returns 0 and is_free_buddy_page(p) is true, a concurrent
hugetlb allocation that has already cleared PageBuddy bypasses the retry in
every hugetlb state:
1. While the new folio is still being initialized or HVO-optimized (neither
HPG_freed nor HPG_migratable is set), get_hwpoison_hugetlb_folio() returns
-EBUSY, get_any_page() returns -EIO after exhausting its retries, and
memory_failure() exits via the default case (MF_MSG_GET_HWPOISON,
MF_IGNORED).
2. Once the folio is placed on the hugetlb freelist (HPG_freed set),
get_hwpoison_page() returns 0, is_free_buddy_page(p) is false, and
memory_failure() exits via the MF_MSG_KERNEL_HIGH_ORDER (MF_IGNORED)
branch.
3. If the folio is allocated for use and marked HPG_migratable,
get_hwpoison_page() returns 1 and memory_failure() falls through into the
non-hugetlb in-use page recovery path (hwpoison_filter(),
split_huge_page_to_order(), etc.) on a hugetlb folio.
In (1) and (2), try_memory_failure_hugetlb() is never re-run: if HVO remaps a
tail page, the PG_hwpoison bit set in the old vmemmap page is discarded when
that vmemmap page is freed, leaving the corrupted physical page in the
hugetlb pool as a healthy page.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106-hwpoison-hugetlb-ro-v3-1-c11186963647@columbia.edu?part=1
next prev parent reply other threads:[~2026-11-07 16:10 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 16:09 [PATCH v3] mm/memory-failure: set PG_hwpoison of a non-hugetlb page under hugetlb_lock Mani Tofigh via B4 Relay
2026-11-06 16:09 ` Mani Tofigh
2026-11-06 22:39 ` Andrew Morton
2026-11-09 11:29 ` Miaohe Lin
2026-11-07 16:10 ` sashiko-bot [this message]
2026-11-09 7:23 ` Miaohe Lin
2026-11-11 23:21 ` Mani Tofigh
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-203024@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.