All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lance Yang <lance.yang@linux.dev>
To: shivankg@amd.com, linmiaohe@huawei.com
Cc: akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org,
	riel@surriel.com, liam@infradead.org, vbabka@kernel.org,
	harry@kernel.org, jannh@google.com, rppt@kernel.org,
	surenb@google.com, mhocko@suse.com, ziy@nvidia.com,
	matthew.brost@intel.com, joshua.hahnjy@gmail.com,
	rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
	ying.huang@linux.alibaba.com, apopple@nvidia.com,
	kmanaouil.dev@gmail.com, fvdl@google.com, kinseyho@google.com,
	weixugc@google.com, bharata@amd.com, rientjes@google.com,
	dev.jain@arm.com, linux-mm@kvack.org,
	linux-kernel@vger.kernel.org, Lance Yang <lance.yang@linux.dev>
Subject: Re: [PATCH v2 7/7] mm/rmap: batch the unmap of large folios in try_to_migrate_one()
Date: Mon, 17 Aug 2026 17:14:44 +0800	[thread overview]
Message-ID: <20260817091444.58876-1-lance.yang@linux.dev> (raw)
In-Reply-To: <20260813-migrate-rmap-batch-v2-7-3c5424c555c7@amd.com>

+Cc Miaohe

On Thu, Aug 13, 2026 at 04:23:18AM +0000, Shivank Garg wrote:
>try_to_migrate_one() converts present PTEs to migration entries one at a
>time. For a PTE-mapped large folio, this repeat calls to ptep clear+flush,
>the migration entry build and set, folio_remove_rmap_pte() and folio_put(),
>each re-entering page_vma_mapped_walk() once per base page (256 times for
>1M folio).
>
>Mirror try_to_unmap_one() to introduce folio_migrate_pte_batch() to detect
>eligible batch for PTEs mapping conseuctive subpages of a large folios,
>and convert the whole batch in one shot using the batched helpers.
>
>A side-effect of this change is trace_set_migration_pte() will record
>one event per batched run instead of earlier behavior of one per base page.
>
>Signed-off-by: Shivank Garg <shivankg@amd.com>
>---
> mm/rmap.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 86 insertions(+), 29 deletions(-)
>
>diff --git a/mm/rmap.c b/mm/rmap.c
>index 35752a70f3a0..63b885c0b7ef 100644
>--- a/mm/rmap.c
>+++ b/mm/rmap.c
>@@ -2675,6 +2675,44 @@ static bool try_to_migrate_hugetlb_one(struct folio *folio,
> 	return ret;
> }
> 
>+static inline unsigned int folio_migrate_pte_batch(struct folio *folio,
>+		struct page_vma_mapped_walk *pvmw, pte_t pte,
>+		struct page *subpage, bool anon_exclusive)
>+{
>+	unsigned long end_addr, addr = pvmw->address;
>+	struct vm_area_struct *vma = pvmw->vma;
>+	unsigned int max_nr, nr;
>+
>+#ifdef __HAVE_ARCH_UNMAP_ONE
>+	/* Cannot batch unmap if arch_unmap_one() is defined. */
>+	return 1;
>+#endif
>+
>+	if (!folio_test_large(folio))
>+		return 1;
>+	if (folio_is_zone_device(folio) || folio_test_has_hwpoisoned(folio))
>+		return 1;
>+	if (pte_unused(pte))
>+		return 1;
>+
>+	/* We may only batch within a single VMA and a single page table. */
>+	end_addr = pmd_addr_end(addr, vma->vm_end);
>+	max_nr = (end_addr - addr) >> PAGE_SHIFT;

Hmm ... can this still batch over a poisoned tail page?

memory_failure() sets PageHWPoison() before taking folio lock, but
cannot set PG_has_hwpoisoned until it acquires and releases that lock.

So tail page can already be poisoned while folio_test_has_hwpoisoned()
still returns false ... no?

Starting from a healthy first subpage, folio_migrate_pte_batch() can
then batch across poisoned tail page. hwpoison only describes first
subpage, so set_softleaf_ptes() installs a normal migration entry for
poisoned page instead of an HWPoison entry ...

Should folio_migrate_pte_batch() check PageHWPoison() on every candidate
subpage and stop before a poisoned one?

Cheers, Lance


>+	/*
>+	 * If unmap fails, we need to restore the ptes. To avoid accidentally
>+	 * upgrading write permissions for ptes that were not originally writable,
>+	 * and to avoid losing the soft-dirty bit, use the appropriate FPB flags.
>+	 */
>+	nr = folio_pte_batch_flags(folio, vma, pvmw->pte, &pte, max_nr,
>+				   FPB_RESPECT_WRITE | FPB_RESPECT_SOFT_DIRTY);
>+
>+	/* Limit possible batch count to a uniform PageAnonExclusive value */
>+	if (folio_test_anon(folio))
>+		nr = page_anon_exclusive_batch(0, nr, subpage, anon_exclusive);
>+
>+	return nr;
>+}
>+
> /*
>  * @arg: enum ttu_flags will be passed to this argument.
>  *
>@@ -2686,12 +2724,12 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> {
> 	struct mm_struct *mm = vma->vm_mm;
> 	DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, address, 0);
>-	bool anon_exclusive, writable, ret = true;
>+	bool anon_exclusive, hwpoison, writable, ret = true;
> 	pte_t pteval;
> 	struct page *subpage;
> 	struct mmu_notifier_range range;
> 	enum ttu_flags flags = (enum ttu_flags)(long)arg;
>-	unsigned long pfn;
>+	unsigned long pfn, end_addr, nr_pages;
> 
> 	/*
> 	 * When racing against e.g. zap_pte_range() on another cpu,
>@@ -2744,11 +2782,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 			VM_BUG_ON_FOLIO(folio_test_hugetlb(folio) ||
> 					!folio_test_pmd_mappable(folio), folio);
> 
>-			if (set_pmd_migration_entry(&pvmw, subpage)) {
>-				ret = false;
>-				page_vma_mapped_walk_done(&pvmw);
>-				break;
>-			}
>+			if (set_pmd_migration_entry(&pvmw, subpage))
>+				goto walk_abort;
> 			continue;
> #endif
> 		}
>@@ -2773,10 +2808,25 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 		subpage = folio_page(folio, pfn - folio_pfn(folio));
> 		anon_exclusive = folio_test_anon(folio) &&
> 				 PageAnonExclusive(subpage);
>+		/*
>+		 * memory_failure() can set PageHWPoison concurrently without holding
>+		 * the folio lock. Snapshot the flag here to decide whether to batch
>+		 * PTEs or install hwpoison entry.
>+		 */
>+		hwpoison = PageHWPoison(subpage);
> 
>+		nr_pages = 1;
> 		if (likely(pte_present(pteval))) {
>-			flush_cache_page(vma, address, pfn);
>-			/* Nuke the page table entry. */
>+			if (!hwpoison)
>+				nr_pages = folio_migrate_pte_batch(folio, &pvmw,
>+								   pteval, subpage,
>+								   anon_exclusive);
>+
>+			end_addr = address + nr_pages * PAGE_SIZE;
>+			flush_cache_range(vma, address, end_addr);
>+
>+			/* Nuke the page table entries. */
>+			pteval = get_and_clear_ptes(mm, address, pvmw.pte, nr_pages);
> 			if (should_defer_flush(mm, flags)) {
> 				/*
> 				 * We clear the PTE but do not flush so potentially
>@@ -2786,11 +2836,9 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 				 * transition on a cached TLB entry is written through
> 				 * and traps if the PTE is unmapped.
> 				 */
>-				pteval = ptep_get_and_clear(mm, address, pvmw.pte);
>-
>-				set_tlb_ubc_flush_pending(mm, pteval, address, address + PAGE_SIZE);
>+				set_tlb_ubc_flush_pending(mm, pteval, address, end_addr);
> 			} else {
>-				pteval = ptep_clear_flush(vma, address, pvmw.pte);
>+				flush_tlb_range(vma, address, end_addr);
> 			}
> 			if (pte_dirty(pteval))
> 				folio_mark_dirty(folio);
>@@ -2809,7 +2857,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 		/* Update high watermark before we lower rss */
> 		update_hiwater_rss(mm);
> 
>-		if (PageHWPoison(subpage)) {
>+		if (hwpoison) {
>+			VM_WARN_ON_ONCE(nr_pages != 1);
> 			VM_WARN_ON_FOLIO(folio_is_device_private(folio), folio);
> 
> 			pteval = swp_entry_to_pte(make_hwpoison_entry(subpage));
>@@ -2837,19 +2886,15 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 			 * so we'll not check/care.
> 			 */
> 			if (arch_unmap_one(mm, vma, address, pteval) < 0) {
>-				set_pte_at(mm, address, pvmw.pte, pteval);
>-				ret = false;
>-				page_vma_mapped_walk_done(&pvmw);
>-				break;
>+				set_ptes(mm, address, pvmw.pte, pteval, nr_pages);
>+				goto walk_abort;
> 			}
> 
>-			/* See folio_try_share_anon_rmap_pte(): clear PTE first. */
>+			/* See folio_try_share_anon_rmap_ptes(): clear PTE first. */
> 			if (anon_exclusive &&
>-			    folio_try_share_anon_rmap_pte(folio, subpage)) {
>-				set_pte_at(mm, address, pvmw.pte, pteval);
>-				ret = false;
>-				page_vma_mapped_walk_done(&pvmw);
>-				break;
>+			    folio_try_share_anon_rmap_ptes(folio, subpage, nr_pages)) {
>+				set_ptes(mm, address, pvmw.pte, pteval, nr_pages);
>+				goto walk_abort;
> 			}
> 
> 			/*
>@@ -2859,19 +2904,31 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
> 			 */
> 			swp_pte = make_migration_pte(subpage, pteval,
> 						     writable, anon_exclusive);
>-			set_pte_at(mm, address, pvmw.pte, swp_pte);
> 			trace_set_migration_pte(address, pte_val(swp_pte),
> 						folio_order(folio));
>+
>+			/* Set nr_pages migration entries, advancing the PFN. */
>+			set_softleaf_ptes(mm, address, pvmw.pte, swp_pte, nr_pages);
> 			/*
> 			 * No need to invalidate here it will synchronize on
> 			 * against the special swap migration pte.
> 			 */
> 		}
> 
>-		folio_remove_rmap_pte(folio, subpage, vma);
>-		if (vma->vm_flags & VM_LOCKED)
>-			mlock_drain_local();
>-		folio_put(folio);
>+		finish_folio_unmap(vma, folio, subpage, nr_pages);
>+
>+		/*
>+		 * If we batched the entire folio, there is nothing left to
>+		 * walk; stop right here.
>+		 */
>+		if (nr_pages == folio_nr_pages(folio))
>+			goto walk_done;
>+		continue;
>+walk_abort:
>+		ret = false;
>+walk_done:
>+		page_vma_mapped_walk_done(&pvmw);
>+		break;
> 	}
> 
> 	mmu_notifier_invalidate_range_end(&range);
>
>-- 
>2.43.0
>
>


      reply	other threads:[~2026-08-17  9:15 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-13  4:23 [PATCH v2 0/7] mm: batch rmap walks during large folio migration Shivank Garg
2026-08-13  4:23 ` [PATCH v2 1/7] mm: factor out generic PTE batch detection from swap_pte_batch() Shivank Garg
2026-08-13  9:57   ` David Hildenbrand (Arm)
2026-08-14  8:00     ` Garg, Shivank
2026-08-16  7:48       ` Garg, Shivank
2026-08-13  4:23 ` [PATCH v2 2/7] mm/migrate: factor out migration PTE construction Shivank Garg
2026-08-13  4:23 ` [PATCH v2 3/7] mm/migrate: split remove_migration_pte_hugetlb() out of remove_migration_pte() Shivank Garg
2026-08-13  4:23 ` [PATCH v2 4/7] mm/migrate: batch the restore-side migration rmap walk Shivank Garg
2026-08-13  4:23 ` [PATCH v2 5/7] mm/rmap: factor out migration PTE construction Shivank Garg
2026-08-13  4:23 ` [PATCH v2 6/7] mm/rmap: split try_to_migrate_hugetlb_one() out of try_to_migrate_one() Shivank Garg
2026-08-13  4:23 ` [PATCH v2 7/7] mm/rmap: batch the unmap of large folios in try_to_migrate_one() Shivank Garg
2026-08-17  9:14   ` Lance Yang [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=20260817091444.58876-1-lance.yang@linux.dev \
    --to=lance.yang@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=apopple@nvidia.com \
    --cc=bharata@amd.com \
    --cc=byungchul@sk.com \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=fvdl@google.com \
    --cc=gourry@gourry.net \
    --cc=harry@kernel.org \
    --cc=jannh@google.com \
    --cc=joshua.hahnjy@gmail.com \
    --cc=kinseyho@google.com \
    --cc=kmanaouil.dev@gmail.com \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=matthew.brost@intel.com \
    --cc=mhocko@suse.com \
    --cc=rakie.kim@sk.com \
    --cc=riel@surriel.com \
    --cc=rientjes@google.com \
    --cc=rppt@kernel.org \
    --cc=shivankg@amd.com \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    --cc=weixugc@google.com \
    --cc=ying.huang@linux.alibaba.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.