From: sashiko-bot@kernel.org
To: "Usama Arif" <usama.arif@linux.dev>,
"Andrew Morton" <akpm@linux-foundation.org>,
david@kernel.org, chrisl@kernel.org, kasong@tencent.com,
ljs@kernel.org, ziy@nvidia.com, linux-mm@kvack.org
Cc: chenhuacai@kernel.org, ying.huang@linux.alibaba.com,
"Baoquan He" <baoquan.he@linux.dev>,
willy@infradead.org, youngjun.park@lge.com, hannes@cmpxchg.org,
riel@surriel.com, shakeel.butt@linux.dev, alex@ghiti.fr,
kas@kernel.org, baohua@kernel.org, dev.jain@arm.com,
baolin.wang@linux.alibaba.com,
"Nico Pache" <nico.pache@linux.dev>,
"Liam R. Howlett" <liam@infradead.org>,
ryan.roberts@arm.com, "Vlastimil Babka" <vbabka@kernel.org>,
lance.yang@linux.dev, nphamcs@gmail.com,
shikemeng@huaweicloud.com, yosry@kernel.org, qi.zheng@linux.dev,
luizcap@redhat.com, kernel-team@meta.com
Subject: Re: [PATCH v9 17/32] mm: swap in PMD swap entries as whole THPs during swapoff
Date: Fri, 06 Nov 2026 18:43:05 +0000 [thread overview]
Message-ID: <sashiko-outbox-202072@kernel.org> (raw)
In-Reply-To: <20261105183940.2236717-18-usama.arif@linux.dev>
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Critical] mm/swapfile: race between unuse_pmd_entry() and
do_swap_page() corrupts rmap
--
commit 2a3a8bc71439dccac7a6dee3671d26f7c3dba876
Author: Usama Arif <usama.arif@linux.dev>
mm: swap in PMD swap entries as whole THPs during swapoff
This commit adds swap_pmd_cache_lookup(), unuse_pmd(), and unuse_pmd_entry()
to swap in PMD swap entries as whole THPs during swapoff when possible,
falling back to splitting the PMD into PTEs otherwise.
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index 6b5149b88fa43..7e91dc50a5131 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
[ ... ]
> @@ -2695,6 +2696,182 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
[ ... ]
> +static int unuse_pmd_entry(struct vm_area_struct *vma, pmd_t *pmd,
> + unsigned long addr, softleaf_t entry)
> +{
> + enum swap_pmd_cache cache_state;
> + struct folio *folio;
> + pmd_t pmdval;
> + int ret;
> +
> + cache_state = swap_pmd_cache_lookup(entry, &folio);
> + if (cache_state == SWAP_PMD_CACHE_SPLIT) {
> + ret = -EAGAIN;
> + goto split_fallback;
> + }
> + if (!folio) {
> + struct vm_fault vmf = {
> + .vma = vma,
> + .address = addr,
> + .real_address = addr,
> + .pmd = pmd,
> + };
> +
> + if (zswap_is_present(entry, HPAGE_PMD_NR)) {
> + ret = -EAGAIN;
> + goto split_fallback;
> + }
> +
> + folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,
> + BIT(HPAGE_PMD_ORDER), &vmf, NULL, 0);
> + if (IS_ERR_OR_NULL(folio)) {
> + ret = folio ? PTR_ERR(folio) : -ENOMEM;
> + goto split_fallback;
> + }
> + }
> +
> + folio_lock(folio);
> + folio_wait_writeback(folio);
> + /*
> + * If the cached folio is no longer PMD-sized (e.g. split in the
> + * swap cache by deferred_split_scan() or memory_failure() while
> + * the PMD swap entry was installed), the PMD swap entry no longer
> + * maps a single contiguous folio. Split the PMD swap entry so
> + * unuse_pte_range() can swap the per-slot folios in individually.
> + */
> + if (folio_nr_pages(folio) != HPAGE_PMD_NR) {
> + folio_unlock(folio);
> + folio_put(folio);
> + ret = -EAGAIN;
> + goto split_fallback;
> + }
> + ret = unuse_pmd(vma, pmd, addr, entry, folio);
> + /*
> + * PTE fallback cannot add a single-page rmap to a newly allocated,
> + * PMD-sized !anon folio, and it cannot do anything useful with a folio
> + * that failed to read. Remove either from the swap cache so each slot
> + * is read back into an order-0 folio. An uptodate anon swap-cache folio
> + * can be mapped one PTE at a time and must stay cached so poisoned
> + * subpages remain visible.
> + */
> + if (ret && folio_matches_swap_entry(folio, entry) &&
> + (!folio_test_uptodate(folio) || !folio_test_anon(folio)))
> + swap_cache_del_folio(folio);
[Severity: Critical]
Can a concurrent page fault in do_swap_page() race with unuse_pmd_entry() and
map this newly allocated PMD-sized !anon folio via PTEs before
swap_cache_del_folio() runs, corrupting the folio's rmap state?
In mm/swapfile.c, try_to_unuse() -> unuse_mm() -> unuse_pmd_range() ->
unuse_pmd_entry() calls swapin_sync() with BIT(HPAGE_PMD_ORDER) without
holding pmd_lock (and with only mmap_read_lock(mm) held). swapin_sync()
allocates an order HPAGE_PMD_ORDER folio with !folio_test_anon(folio) and
folio_test_pmd_mappable(folio) == true, inserts it into the swap cache
covering [entry, entry + HPAGE_PMD_NR), and initiates swap_read_folio().
When the swap read completes, the folio is unlocked before unuse_pmd_entry()
acquires folio_lock(folio). During that window, a concurrent page fault on
split PTEs covering the same swap range (either in the same mm after a
concurrent PMD split such as madvise or mprotect, or in a forked mm whose PMD
swap entry was split into HPAGE_PMD_NR contiguous PTE swap entries) can enter
do_swap_page(), find the folio via swap_cache_get_folio(entry), and lock it
first.
In mm/memory.c:do_swap_page(), when all HPAGE_PMD_NR PTEs in the split page
table are still contiguous swap entries, folio_pte_range() and
swap_pte_batch() compute nr_pages == HPAGE_PMD_NR == folio_nr_pages(folio), so
the size mismatch check does not trigger:
if (!folio_test_anon(folio) && folio_test_large(folio) &&
nr_pages != folio_nr_pages(folio)) {
if (!WARN_ON_ONCE(folio_test_dirty(folio)))
swap_cache_del_folio(folio);
goto out_nomap;
}
do_swap_page() then calls folio_add_new_anon_rmap(folio, vma, address,
rmap_flags) and maps the folio across HPAGE_PMD_NR PTEs with set_ptes().
However, mm/rmap.c:folio_add_new_anon_rmap() assumes any
folio_test_pmd_mappable(folio) folio is being mapped at the PMD level:
} else if (!folio_test_pmd_mappable(folio)) {
...
} else {
nr = folio_large_nr_pages(folio);
/* increment count (starts at -1) */
atomic_set(&folio->_entire_mapcount, 0);
folio_set_large_mapcount(folio, 1, vma);
if (IS_ENABLED(CONFIG_PAGE_MAPCOUNT))
atomic_set(&folio->_nr_pages_mapped, ENTIRELY_MAPPED);
if (exclusive)
SetPageAnonExclusive(&folio->page);
nr_pmdmapped = nr;
}
This initializes PMD-mapped rmap accounting (_entire_mapcount = 0,
_large_mapcount = 1, per-page _mapcount left at -1, and PageAnonExclusive set
only on page 0) on a folio that is actually mapped by HPAGE_PMD_NR PTEs, which
will corrupt mapcounts and trigger BUG checks when those PTEs are later
unmapped via folio_remove_rmap_ptes().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105183940.2236717-1-usama.arif@linux.dev?part=17
next prev parent reply other threads:[~2026-11-06 18:43 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 18:38 [PATCH v9 00/32] mm: PMD-level swap entries for anonymous THPs Usama Arif
2026-11-05 18:38 ` [PATCH v9 01/32] mm: rename pmd_to_softleaf_folio() to pmd_softleaf_to_folio() Usama Arif
2026-11-05 18:38 ` [PATCH v9 02/32] arm64: mm: add PMD swap-exclusive helpers Usama Arif
2026-11-05 18:38 ` [PATCH v9 03/32] loongarch: " Usama Arif
2026-11-05 18:38 ` [PATCH v9 04/32] powerpc: " Usama Arif
2026-11-05 18:38 ` [PATCH v9 05/32] riscv: " Usama Arif
2026-11-05 18:38 ` [PATCH v9 06/32] s390: " Usama Arif
2026-11-05 18:38 ` [PATCH v9 07/32] x86: " Usama Arif
2026-11-05 18:38 ` [PATCH v9 08/32] mm: recognize PMD swap entries in the softleaf layer Usama Arif
2026-11-05 18:38 ` [PATCH v9 09/32] mm/debug_vm_pgtable: test PMD swap-exclusive helpers Usama Arif
2026-11-05 18:38 ` [PATCH v9 10/32] mm/migrate_device: avoid leaking folio references during THP splitting Usama Arif
2026-11-05 18:38 ` [PATCH v9 11/32] mm: make PMD migration-entry splitting explicit Usama Arif
2026-11-05 18:38 ` [PATCH v9 12/32] mm: split PMD swap entries into PTE swap entries Usama Arif
2026-11-05 18:38 ` [PATCH v9 13/32] mm/swap: allow duplicating a range of " Usama Arif
2026-11-05 18:38 ` [PATCH v9 14/32] mm: handle PMD swap entries in fork path Usama Arif
2026-11-05 18:38 ` [PATCH v9 15/32] mm: swap: share swap-cache allocation error cleanup Usama Arif
2026-11-06 21:10 ` Yosry Ahmed
2026-11-06 21:39 ` Nhat Pham
2026-11-05 18:39 ` [PATCH v9 16/32] mm: zswap: reject high-order swap cache allocations backed by zswap Usama Arif
2026-11-06 16:26 ` Nhat Pham
2026-11-06 21:16 ` Yosry Ahmed
2026-11-05 18:39 ` [PATCH v9 17/32] mm: swap in PMD swap entries as whole THPs during swapoff Usama Arif
2026-11-06 18:43 ` sashiko-bot [this message]
2026-11-05 18:39 ` [PATCH v9 18/32] fs/proc: account PMD swap entries in smaps Usama Arif
2026-11-05 18:39 ` [PATCH v9 19/32] mm: handle soft-dirty and uffd-wp on PMD swap entries Usama Arif
2026-11-05 18:39 ` [PATCH v9 20/32] mm/hmm: fault PMD swap entries on demand Usama Arif
2026-11-05 18:39 ` [PATCH v9 21/32] mm: free PMD swap entries in zap_huge_pmd() Usama Arif
2026-11-05 18:39 ` [PATCH v9 22/32] mm/madvise: free PMD swap entries with MADV_FREE Usama Arif
2026-11-05 18:39 ` [PATCH v9 23/32] mm/madvise: skip PMD swap entries for MADV_COLD and MADV_PAGEOUT Usama Arif
2026-11-05 18:39 ` [PATCH v9 24/32] mm/madvise: keep PMD swap entries whole for MADV_GUARD_INSTALL/REMOVE Usama Arif
2026-11-05 18:39 ` [PATCH v9 25/32] mm/mincore: report PMD swap-cache residency Usama Arif
2026-11-05 18:39 ` [PATCH v9 26/32] mm/khugepaged: treat PMD swap entries as mapped THPs Usama Arif
2026-11-05 18:39 ` [PATCH v9 27/32] mm: handle PMD swap entries in MADV_WILLNEED Usama Arif
2026-11-06 18:43 ` sashiko-bot
2026-11-05 18:39 ` [PATCH v9 28/32] mm: handle PMD swap entries in UFFDIO_MOVE Usama Arif
2026-11-06 18:43 ` sashiko-bot
2026-11-05 18:39 ` [PATCH v9 29/32] mm: don't PTE-batch a swap-in over a hardware-poisoned subpage Usama Arif
2026-11-05 18:39 ` [PATCH v9 30/32] mm: handle PMD swap entry faults on swap-in Usama Arif
2026-11-06 18:43 ` sashiko-bot
2026-11-05 18:39 ` [PATCH v9 31/32] mm: install PMD swap entries on swap-out Usama Arif
2026-11-06 18:43 ` sashiko-bot
2026-11-05 18:39 ` [PATCH v9 32/32] selftests/mm: add PMD swap entry tests Usama Arif
2026-11-06 18:43 ` sashiko-bot
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-202072@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=alex@ghiti.fr \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=baoquan.he@linux.dev \
--cc=chenhuacai@kernel.org \
--cc=chrisl@kernel.org \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=hannes@cmpxchg.org \
--cc=kas@kernel.org \
--cc=kasong@tencent.com \
--cc=kernel-team@meta.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=luizcap@redhat.com \
--cc=nico.pache@linux.dev \
--cc=nphamcs@gmail.com \
--cc=qi.zheng@linux.dev \
--cc=riel@surriel.com \
--cc=ryan.roberts@arm.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=shikemeng@huaweicloud.com \
--cc=usama.arif@linux.dev \
--cc=vbabka@kernel.org \
--cc=willy@infradead.org \
--cc=ying.huang@linux.alibaba.com \
--cc=yosry@kernel.org \
--cc=youngjun.park@lge.com \
--cc=ziy@nvidia.com \
/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