Re: [PATCH v6 06/12] mm: swap in PMD swap entries as whole THPs during swapoff
From: Usama Arif
Date: Wed Aug 19 2026 - 08:54:35 EST
On 19/08/2026 06:38, Lance Yang wrote:
> +Cc Miaohe
>
> On Tue, Aug 18, 2026 at 06:09:47AM -0700, Usama Arif wrote:
> [...]
>> +#ifdef CONFIG_THP_SWAP
>> +/*
>> + * unuse_pmd - Map a locked folio at PMD granularity during swapoff.
>> + *
>> + * The caller provides a locked, swapped-in folio. Returns 0 on success
>> + * (PMD was mapped). Returns -EAGAIN if the swap cache folio no longer
>> + * matches the entry or the PMD changed under the lock (try_to_unuse will
>> + * rescan). Returns -EIO if the folio is not uptodate or contains a poisoned
>> + * subpage; in that case the PMD is split so unuse_pte_range() can handle
>> + * individual pages.
>> + */
>> +static int unuse_pmd(struct vm_area_struct *vma, pmd_t *pmd,
>> + unsigned long addr, softleaf_t entry,
>> + struct folio *folio)
>> +{
>> + struct mm_struct *mm = vma->vm_mm;
>> + struct page *page;
>> + pmd_t new_pmd, old_pmd;
>> + spinlock_t *ptl;
>> + rmap_t rmap_flags = RMAP_NONE;
>> + bool exclusive;
>> +
>> + if (unlikely(!folio_matches_swap_entry(folio, entry)))
>> + return -EAGAIN;
>> +
>> + if (unlikely(!folio_test_uptodate(folio))) {
>> + /* Let PTE fallback reread each slot independently. */
>> + swap_cache_del_folio(folio);
>> + __split_huge_pmd(vma, pmd, addr, false);
>> + return -EIO;
>> + }
>> +
>> + if (unlikely(folio_contain_hwpoisoned_page(folio))) {
>> + /* Let PTE fallback isolate the poisoned subpages. */
>> + __split_huge_pmd(vma, pmd, addr, false);
>> + return -EIO;
>> + }
>
> Hmm ... can this miss a poisoned tail?
>
> memory_failure() sets PageHWPoison(p) before taking folio_lock(), but
> PG_has_hwpoisoned is only set later. Since unuse_pmd_entry() holds folio
> lock across this check, memory_failure() can be blocked on folio_lock()
> with a tail already poisoned, IIUC ...
>
> folio_contain_hwpoisoned_page() then sees neither a poisoned head nor
> PG_has_hwpoisoned, and set_pmd_at() maps that tail through a normal PMD.
>
> Note that unuse_pte() checks PageHWPoison(page) directly. Should we check
> each subpage here as well before installing the PMD?
Good catch!
I am going to introduce a helper like below and use it in the next
revision. Thanks!
static inline bool folio_has_hwpoisoned_subpage(struct folio *folio)
{
unsigned long i;
for (i = 0; i < folio_nr_pages(folio); i++)
if (PageHWPoison(folio_page(folio, i)))
return true;
return false;
}
>
>> +
>> + page = folio_page(folio, 0);
>> +
>> + ptl = pmd_lock(mm, pmd);
>> + old_pmd = pmdp_get(pmd);
>> +
>> + if (!pmd_is_swap_entry(old_pmd) ||
>> + softleaf_from_pmd(old_pmd).val != entry.val) {
>> + spin_unlock(ptl);
>> + return -EAGAIN;
>> + }
>> +
>> + exclusive = pmd_swp_exclusive(old_pmd);
>> +
>> + /*
>> + * Some architectures may have to restore extra metadata to the folio
>> + * when reading from swap. This metadata may be indexed by swap entry
>> + * so this must be called before folio_put_swap().
>> + */
>> + arch_swap_restore(folio_swap(entry, folio), folio);
>> +
>> + add_mm_counter(mm, MM_ANONPAGES, HPAGE_PMD_NR);
>> + add_mm_counter(mm, MM_SWAPENTS, -HPAGE_PMD_NR);
>> +
>> + new_pmd = folio_mk_pmd(folio, vma->vm_page_prot);
>> + new_pmd = pmd_mkold(new_pmd);
>> + if (pmd_swp_soft_dirty(old_pmd))
>> + new_pmd = pmd_mksoft_dirty(new_pmd);
>> + if (pmd_swp_uffd(old_pmd))
>> + new_pmd = pmd_mkuffd(new_pmd);
>> + if (pmd_swp_uffd(old_pmd) && userfaultfd_rwp(vma))
>> + new_pmd = pmd_modify(new_pmd, PAGE_NONE);
>> +
>> + if (exclusive)
>> + rmap_flags |= RMAP_EXCLUSIVE;
>> +
>> + folio_get(folio);
>> + if (!folio_test_anon(folio))
>> + folio_add_new_anon_rmap(folio, vma, addr, rmap_flags);
>> + else
>> + folio_add_anon_rmap_pmd(folio, page, vma, addr, rmap_flags);
>> +
>> + set_pmd_at(mm, addr, pmd, new_pmd);
>> + folio_put_swap(folio, NULL);
>> +
>> + spin_unlock(ptl);
>> +
>> + folio_free_swap(folio);
>> + return 0;
>> +}
>
> [...]
>
> Cheers, Lance