All of lore.kernel.org
 help / color / mirror / Atom feed
From: Usama Arif <usama.arif@linux.dev>
To: Kairui Song <ryncsn@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	david@kernel.org, chrisl@kernel.org, ljs@kernel.org,
	ziy@nvidia.com, linux-mm@kvack.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, npache@redhat.com,
	"Liam R. Howlett" <liam@infradead.org>,
	ryan.roberts@arm.com, Vlastimil Babka <vbabka@kernel.org>,
	lance.yang@linux.dev, linux-kernel@vger.kernel.org,
	nphamcs@gmail.com, shikemeng@huaweicloud.com,
	kernel-team@meta.com
Subject: Re: [PATCH v3 09/11] mm: handle PMD swap entry faults on swap-in
Date: Mon, 6 Jul 2026 22:06:43 +0100	[thread overview]
Message-ID: <d0d47811-44b2-490f-ad57-f9b57aa70a0a@linux.dev> (raw)
In-Reply-To: <CAMgjq7CQ+1gsptFYH+Y0GEqyYj+5QpAKjtZLekRS4h+k4f8QvQ@mail.gmail.com>



On 06/07/2026 18:56, Kairui Song wrote:
> On Sat, Jul 4, 2026 at 1:41 AM Usama Arif <usama.arif@linux.dev> wrote:
>>
>> Add do_huge_pmd_swap_page() and dispatch to it from __handle_mm_fault()
>> when vmf->orig_pmd encodes a swap entry.  The handler resolves the
>> entire 2 MB mapping in one shot, mirroring do_swap_page() (PTE path)
>> at PMD granularity:
>>
>>   - Look up the folio in the swap cache; on a miss, allocate a
>>     PMD-order folio via swap_cache_alloc_folio() and read from swap.
>>
>>   - After locking, re-validate that the folio still corresponds to our
>>     entry and is still PMD-sized.  Between the unlocked cache lookup
>>     and the lock, a racing swap-in on the same entry may have removed
>>     it from the cache via folio_free_swap(), or reclaim / memory_failure
>>     / deferred-split may have split the folio into smaller folios.
>>
>>   - Restore soft_dirty and uffd_wp from the swap PMD.  Map writable
>>     only when the entry was exclusive, the VMA permits writes, and
>>     uffd-wp is not armed.  Drop the exclusive marker when the cached
>>     folio is under writeback to an SWP_STABLE_WRITES backend (zram,
>>     encrypted) so the PMD is mapped read-only; a later write COWs
>>     into a fresh folio rather than corrupting the in-flight writeback.
>>     Mirrors do_swap_page().
>>
>>   - When the resulting PMD is read-only but the fault was a write,
>>     update vmf->orig_pmd and call wp_huge_pmd() in the same handler
>>     to COW immediately rather than forcing a second fault.  Mask
>>     VM_FAULT_FALLBACK from its return: a PMD-COW that splits to
>>     PTE-level is normal, but the bit is part of VM_FAULT_ERROR and
>>     arch fault handlers BUG() on it without SIGBUS/HWPOISON/SIGSEGV.
>>     Requires exposing wp_huge_pmd() via mm/internal.h.
>>
>>   - Free the swap slot via should_try_to_free_swap() (hoisted from
>>     mm/memory.c into mm/internal.h so PTE- and PMD-level swap-in
>>     share the heuristic).
>>
>> When PMD-order resources are unavailable (folio allocation fails,
>> the cached folio was split, memcg charge fails, or swapin_folio()
>> races) split the PMD swap entry into 512 PTE swap entries via
>> __split_huge_pmd() and return 0.  The fault retries and do_swap_page()
>> takes over per-PTE.  This avoids returning VM_FAULT_OOM for transient
>> PMD-order allocation failures.
>>
>> Signed-off-by: Usama Arif <usama.arif@linux.dev>
> 
> Hello Usama
> 
> Amazing work.

Hi Kairui,

Thanks! and Thanks for the review comments!

> 
>> ---
>>  include/linux/huge_mm.h |   9 ++
>>  mm/huge_memory.c        | 216 ++++++++++++++++++++++++++++++++++++++++
>>  mm/internal.h           |  36 +++++++
>>  mm/memory.c             |  40 +-------
>>  4 files changed, 265 insertions(+), 36 deletions(-)
>>
>> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
>> index 1487bf4af1a7..9ec475ccfc91 100644
>> --- a/include/linux/huge_mm.h
>> +++ b/include/linux/huge_mm.h
>> @@ -531,6 +531,15 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf);
>>
>>  vm_fault_t do_huge_pmd_device_private(struct vm_fault *vmf);
>>
>> +#ifdef CONFIG_THP_SWAP
>> +vm_fault_t do_huge_pmd_swap_page(struct vm_fault *vmf);
>> +#else
>> +static inline vm_fault_t do_huge_pmd_swap_page(struct vm_fault *vmf)
>> +{
>> +       return 0;
>> +}
>> +#endif
>> +
>>  extern struct folio *huge_zero_folio;
>>  extern unsigned long huge_zero_pfn;
>>
>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>> index fdc1a503c609..5fa60324a2f0 100644
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -41,6 +41,7 @@
>>  #include <linux/pgalloc.h>
>>  #include <linux/pgalloc_tag.h>
>>  #include <linux/pagewalk.h>
>> +#include <linux/zswap.h>
>>
>>  #include <asm/tlb.h>
>>  #include "internal.h"
>> @@ -2312,6 +2313,221 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf)
>>         return 0;
>>  }
>>
>> +#ifdef CONFIG_THP_SWAP
>> +/**
>> + * do_huge_pmd_swap_page() - Handle a fault on a PMD-level swap entry.
>> + * @vmf: Fault context. vmf->orig_pmd contains the swap PMD.
>> + *
>> + * A PMD swap entry is a compact encoding for HPAGE_PMD_NR consecutive swap
>> + * slots. If the swap cache still has one PMD-sized folio covering the range,
>> + * map it directly at PMD level. If the range has been split into per-page
>> + * cache state, or zswap may have per-page state for it, split the PMD swap
>> + * entry and retry at PTE granularity.
>> + *
>> + * Return: VM_FAULT_* flags.
>> + */
>> +vm_fault_t do_huge_pmd_swap_page(struct vm_fault *vmf)
>> +{
>> +       struct vm_area_struct *vma = vmf->vma;
>> +       struct mm_struct *mm = vma->vm_mm;
>> +       struct folio *folio;
>> +       struct page *page;
>> +       struct swap_info_struct *si;
>> +       unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
>> +       softleaf_t entry;
>> +       swp_entry_t swp_entry;
>> +       pmd_t pmd;
>> +       vm_fault_t ret = 0;
>> +       bool exclusive;
>> +       rmap_t rmap_flags = RMAP_NONE;
>> +       enum swap_pmd_cache cache_state;
>> +
>> +       entry = softleaf_from_pmd(vmf->orig_pmd);
>> +       if (unlikely(!softleaf_is_swap(entry)))
>> +               return 0;
>> +
>> +       swp_entry = entry;
>> +
>> +       /* Prevent swapoff from happening to us. */
>> +       si = get_swap_device(swp_entry);
>> +       if (unlikely(!si))
>> +               return 0;
>> +
>> +       cache_state = swap_pmd_cache_lookup(swp_entry, &folio);
>> +       if (cache_state == SWAP_PMD_CACHE_SPLIT)
>> +               goto split_fallback;
>> +       if (!folio) {
>> +               /*
>> +                * PMD swap entries encode ordinary per-page swap slots. If any
>> +                * slot is in zswap, split and let the PTE swap path load the
>> +                * range per page. Otherwise the range is all on disk and can be
>> +                * read back as one PMD-sized folio.
>> +                */
>> +               if (zswap_range_has_entry(swp_entry, HPAGE_PMD_NR))
>> +                       goto split_fallback;
>> +
>> +               folio = swapin_sync(swp_entry, GFP_HIGHUSER_MOVABLE,
>> +                                   BIT(HPAGE_PMD_ORDER), vmf, NULL, 0);
> 
> Hmm, so here we always do a sync swapin which means there is no
> readahead, kind of make sense since PMD is huge, readahead might be
> not helpful and a order 0 readahead breaks following PMD. But maybe
> worth mention it in the commit message at least so it's easiler to
> follow. (partly because the name swapin_sync is a bit confusing at the
> moment, we could change that name as we improve swap readahead later).

Yes, I’ll add this to the commit message. Thanks!

Yes readahead won't be useful for PMD as you said.

> 
> And just an idea that will it be suffient if we just use the return
> value of swapin_sync directly? If a smaller folio covers the PMD start
> entry, the smaller folio is returned and you catched that just fine.
> If a smaller folio covers a follow-up sub-entry or a sub-entry is
> freed, -EBUSY is returned here and you just do the split. And it
> doesn't do the allocation for the first lookup check, so there should
> be no thrashing issue.

The main issue over here is zswap.
zswap still stores the range as order-0 objects. If we call PMD-order
swapin_sync() when the cache is empty but any slot is in zswap, zswap_load()
fails the large-folio load and leaves a non-uptodate PMD-sized folio in
swapcache. Splitting the PMD after that is not enough unless we also delete that
failed large folio, because the subsequent PTE fault can find the non-uptodate
large folio and SIGBUS instead of loading the individual zswap entries.


> 
>> +               if (IS_ERR_OR_NULL(folio))
>> +                       goto split_fallback;
>> +
>> +               /* Had to read from swap area: Major fault */
>> +               ret = VM_FAULT_MAJOR;
>> +               count_vm_event(PGMAJFAULT);
>> +               count_memcg_event_mm(mm, PGMAJFAULT);
>> +       }
>> +
>> +       ret |= folio_lock_or_retry(folio, vmf);
> 
> With PMD sized folio on a slower device, this try lock could fail much
> more frequently, leading to repeated faults. That's not a new issue
> and I remember Barry talked about it at the LSFMM this year, just I'm
> not sure if that will get much worse for PMD?

Agreed that PMD-sized IO can keep the folio locked longer, especially on
slow swap devices. (Which hopefully are dying away :))
The current behavior matches do_swap_page(): on the normal retryable fault
path it drops the fault lock and waits for the folio unlock before returning
VM_FAULT_RETRY, so it should not be a tight retry loop.
The wait can be longer for 2MB IO, but the retry usually comes back to
either an uptodate folio or a PMD that another fault already  installed.
I think this is the right baseline behavior, and any improvement here
belongs with the broader swap readahead / async swapin work.


  reply	other threads:[~2026-07-06 21:06 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03 17:38 [PATCH v3 00/11] mm: PMD-level swap entries for anonymous THPs Usama Arif
2026-07-03 17:38 ` [PATCH v3 01/11] mm: add PMD swap entry detection support Usama Arif
2026-07-03 17:38 ` [PATCH v3 02/11] mm: add PMD swap entry splitting support Usama Arif
2026-07-03 17:38 ` [PATCH v3 03/11] mm: handle PMD swap entries in fork path Usama Arif
2026-07-03 17:38 ` [PATCH v3 04/11] mm: zswap: add range lookup for large-folio swapin Usama Arif
2026-07-06 21:36   ` Yosry Ahmed
2026-07-07 10:08     ` Usama Arif
2026-07-03 17:38 ` [PATCH v3 05/11] mm: swap in PMD swap entries as whole THPs during swapoff Usama Arif
2026-07-03 17:38 ` [PATCH v3 06/11] mm: handle PMD swap entries in non-present PMD walkers Usama Arif
2026-07-03 17:38 ` [PATCH v3 07/11] mm: handle PMD swap entries in MADV_WILLNEED Usama Arif
2026-07-03 17:38 ` [PATCH v3 08/11] mm: handle PMD swap entries in UFFDIO_MOVE Usama Arif
2026-07-03 17:38 ` [PATCH v3 09/11] mm: handle PMD swap entry faults on swap-in Usama Arif
2026-07-06 17:56   ` Kairui Song
2026-07-06 21:06     ` Usama Arif [this message]
2026-07-03 17:38 ` [PATCH v3 10/11] mm: install PMD swap entries on swap-out Usama Arif
2026-07-03 17:38 ` [PATCH v3 11/11] selftests/mm: add PMD swap entry tests Usama Arif
2026-07-04  6:27   ` kernel test robot
2026-07-04  8:30   ` kernel test robot
2026-07-05  1:43 ` [PATCH v3 00/11] mm: PMD-level swap entries for anonymous THPs Andrew Morton

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=d0d47811-44b2-490f-ad57-f9b57aa70a0a@linux.dev \
    --to=usama.arif@linux.dev \
    --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=chrisl@kernel.org \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=hannes@cmpxchg.org \
    --cc=kas@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=npache@redhat.com \
    --cc=nphamcs@gmail.com \
    --cc=riel@surriel.com \
    --cc=ryan.roberts@arm.com \
    --cc=ryncsn@gmail.com \
    --cc=shakeel.butt@linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=vbabka@kernel.org \
    --cc=willy@infradead.org \
    --cc=ying.huang@linux.alibaba.com \
    --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 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.