Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
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


      parent reply	other threads:[~2026-11-07 16:10 UTC|newest]

Thread overview: 3+ 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 22:39 ` Andrew Morton
2026-11-07 16:10 ` 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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox