mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Andrew Morton <akpm@linux-foundation.org>,
	 Johannes Weiner <hannes@cmpxchg.org>,
	Michal Hocko <mhocko@kernel.org>,  Qi Zheng <qi.zheng@linux.dev>,
	Shakeel Butt <shakeel.butt@linux.dev>,
	 Lorenzo Stoakes <ljs@kernel.org>,
	Kairui Song <kasong@tencent.com>,  Barry Song <baohua@kernel.org>,
	Axel Rasmussen <axelrasmussen@google.com>,
	 Yuanchu Xie <yuanchu@google.com>, Wei Xu <weixugc@google.com>,
	 Baoquan He <baoquan.he@linux.dev>,
	 Baolin Wang <baolin.wang@linux.alibaba.com>,
	 "Liam R. Howlett" <liam@infradead.org>,
	Vlastimil Babka <vbabka@kernel.org>,
	 Mike Rapoport <rppt@kernel.org>,
	Suren Baghdasaryan <surenb@google.com>,
	 Michal Hocko <mhocko@suse.com>, Kees Cook <kees@kernel.org>,
	 Peter Zijlstra <peterz@infradead.org>,
	Jann Horn <jannh@google.com>,  Pedro Falcato <pfalcato@suse.de>,
	Hugh Dickins <hughd@google.com>,
	 Pasha Tatashin <pasha.tatashin@soleen.com>,
	 Pratyush Yadav <pratyush@kernel.org>,
	 "Matthew Wilcox (Oracle)" <willy@infradead.org>,
	Jan Kara <jack@suse.cz>,  Zi Yan <ziy@nvidia.com>,
	Lance Yang <lance.yang@linux.dev>,
	 Rik van Riel <riel@surriel.com>, Harry Yoo <harry@kernel.org>,
	 Alistair Popple <apopple@nvidia.com>,
	Nico Pache <nico.pache@linux.dev>,
	 Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
	 Usama Arif <usama.arif@linux.dev>,
	Kiryl Shutsemau <kas@kernel.org>,
	 Matthew Brost <matthew.brost@intel.com>,
	John Hubbard <jhubbard@nvidia.com>,  Peter Xu <peterx@redhat.com>,
	Will Deacon <will@kernel.org>,
	 "Aneesh Kumar K.V" <aneesh.kumar@kernel.org>,
	 Nick Piggin <npiggin@gmail.com>,
	Oscar Salvador <osalvador@suse.de>,
	 Minchan Kim <minchan@kernel.org>,
	Muchun Song <muchun.song@linux.dev>,  Xu Xin <xu.xin@linux.dev>,
	Chengming Zhou <chengming.zhou@linux.dev>,
	 Chris Li <chrisl@kernel.org>,
	Kemeng Shi <shikemeng@huaweicloud.com>,
	 Nhat Pham <nphamcs@gmail.com>,
	Youngjun Park <youngjun.park@lge.com>,
	 Gregory Price <gourry@gourry.net>,
	Joshua Hahn <joshua.hahnjy@gmail.com>,
	 Rakie Kim <rakie.kim@sk.com>,
	Ying Huang <ying.huang@linux.alibaba.com>,
	 Jason Gunthorpe <jgg@ziepe.ca>
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	 kexec@lists.infradead.org, linux-fsdevel@vger.kernel.org,
	 linux-arch@vger.kernel.org, linux-cxl@vger.kernel.org,
	 "David Hildenbrand (Arm)" <david@kernel.org>
Subject: [PATCH 18/46] mm/khugepaged: check if PTE table is empty in try_collapse_pte_mapped_thp()
Date: Tue, 27 Oct 2026 00:17:38 +0100	[thread overview]
Message-ID: <20261027-fixes_core-v1-18-6f74bec9a5bb@kernel.org> (raw)
In-Reply-To: <20261027-fixes_core-v1-0-6f74bec9a5bb@kernel.org>

The existing code is rather horrible and already resulted in bugs in the
past: we special-case UFFD inserting anonymous folios by grabbing the PMD
lock early, but ignore any other markers (guard, poison through uffd) that
could have been inserted into the page table in the meantime, effectively
dropping them.

Let's just unify and simplify the locking: try to grab the PMD table
lock and if that fails, flush the TLB, drop the PTE lock, take PT locks in
the right order and check if the page table is still empty.

We still perform the heavy lifting (zapping the folio ptes) under PTE table
lock only.

Include <linux/hugetlb.h> for now, as x86's flush_tlb_range() macro
depends on hugetlb helpers. This dependency needs a separate cleanup.

Both issues this fixes are rather corner cases. Let's not CC stable for
now.

Fixes: f807123d578d ("mm: allow guard regions in file-backed and read-only mappings")
Fixes: 1043173eb5eb ("mm/khugepaged: collapse_pte_mapped_thp() with mmap_read_lock()")
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
 mm/khugepaged.c | 64 ++++++++++++++++++++++++++++-----------------------------
 1 file changed, 31 insertions(+), 33 deletions(-)

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index dd17c3dae8006..4313abf9cfcb9 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -10,6 +10,7 @@
 #include <linux/mm_inline.h>
 #include <linux/kthread.h>
 #include <linux/khugepaged.h>
+#include <linux/hugetlb.h>
 #include <linux/freezer.h>
 #include <linux/mman.h>
 #include <linux/hashtable.h>
@@ -1832,6 +1833,17 @@ static enum scan_result set_huge_pmd(struct vm_area_struct *vma, unsigned long a
 	return SCAN_SUCCEED;
 }
 
+static bool pte_table_empty(pte_t *pte)
+{
+	int i;
+
+	for (i = 0; i < HPAGE_PMD_NR; i++, pte++) {
+		if (!pte_none(ptep_get(pte)))
+			return false;
+	}
+	return true;
+}
+
 static enum scan_result try_collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
 		bool install_pmd)
 {
@@ -1935,33 +1947,14 @@ static enum scan_result try_collapse_pte_mapped_thp(struct mm_struct *mm, unsign
 	}
 
 	pte_unmap_unlock(start_pte, ptl);
-	ptl = NULL;
 	mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, mm,
 				haddr, haddr + HPAGE_PMD_SIZE);
 	mmu_notifier_invalidate_range_start(&range);
 	notified = true;
 
-	/*
-	 * pmd_lock covers a wider range than ptl, and (if split from mm's
-	 * page_table_lock) ptl nests inside pml. The less time we hold pml,
-	 * the better; but userfaultfd's mfill_atomic_pte() on a private VMA
-	 * inserts a valid as-if-COWed PTE without even looking up page cache.
-	 * So page lock of folio does not protect from it, so we must not drop
-	 * ptl before pgt_pmd is removed, so uffd private needs pml taken now.
-	 */
-	if (userfaultfd_armed(vma) && !(vma->vm_flags & VM_SHARED))
-		pml = pmd_lock(mm, pmd);
-
-	start_pte = pte_offset_map_rw_nolock(mm, pmd, haddr, &pgt_pmd, &ptl);
+	start_pte = pte_offset_map_lock(mm, pmd, haddr, &ptl);
 	if (!start_pte)
 		goto abort;
-	if (!pml)
-		spin_lock(ptl);
-	else if (ptl != pml)
-		spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
-
-	if (unlikely(!pmd_same(pgt_pmd, pmdp_get_lockless(pmd))))
-		goto abort;
 
 	/* step 2: clear page table and adjust rmap */
 	for (i = 0, addr = haddr, pte = start_pte; i < HPAGE_PMD_NR;
@@ -1992,10 +1985,7 @@ static enum scan_result try_collapse_pte_mapped_thp(struct mm_struct *mm, unsign
 
 		nr_batch_ptes = folio_pte_batch(folio, pte, ptent, max_nr_batch_ptes);
 
-		/*
-		 * Must clear entry, or a racing truncate may re-remove it.
-		 * TLB flush can be left until pmdp_collapse_flush() does it.
-		 */
+		/* Must clear entry, or a racing truncate may re-remove it. */
 		ptent = get_and_clear_ptes(mm, addr, pte, nr_batch_ptes);
 		if (pte_dirty(ptent))
 			folio_mark_dirty(folio);
@@ -2003,25 +1993,33 @@ static enum scan_result try_collapse_pte_mapped_thp(struct mm_struct *mm, unsign
 		nr_mapped_ptes += nr_batch_ptes;
 	}
 
-	if (!pml)
-		spin_unlock(ptl);
-
 	/* step 3: set proper refcount and mm_counters. */
 	if (nr_mapped_ptes) {
 		folio_ref_sub(folio, nr_mapped_ptes);
 		add_mm_counter(mm, mm_counter_file(folio), -nr_mapped_ptes);
 	}
 
-	/* step 4: remove empty page table */
-	if (!pml) {
+	/* step 4: acquire the PMD lock and remove the empty page table */
+	pml = pmd_lockptr(mm, pmd);
+	if (ptl != pml && !spin_trylock(pml)) {
+		/* We must flush before dropping the PTL. */
+		if (nr_mapped_ptes)
+			flush_tlb_range(vma, haddr, end);
+		pte_unmap_unlock(start_pte, ptl);
+		ptl = NULL;
+
 		pml = pmd_lock(mm, pmd);
+		start_pte = pte_offset_map_rw_nolock(mm, pmd, haddr, &pgt_pmd, &ptl);
+		if (!start_pte)
+			goto unlock;
 		if (ptl != pml)
 			spin_lock_nested(ptl, SINGLE_DEPTH_NESTING);
-		if (unlikely(!pmd_same(pgt_pmd, pmdp_get_lockless(pmd)))) {
-			flush_tlb_mm(mm);
+
+		if (!pte_table_empty(start_pte))
 			goto unlock;
-		}
 	}
+
+	/* This also flushes the zapped folio ptes properly. */
 	pgt_pmd = pmdp_collapse_flush(vma, haddr, pmd);
 	pmdp_get_lockless_sync();
 	pte_unmap_unlock(start_pte, ptl);
@@ -2042,7 +2040,7 @@ static enum scan_result try_collapse_pte_mapped_thp(struct mm_struct *mm, unsign
 	goto drop_folio;
 abort:
 	if (nr_mapped_ptes) {
-		flush_tlb_mm(mm);
+		flush_tlb_range(vma, haddr, end);
 		folio_ref_sub(folio, nr_mapped_ptes);
 		add_mm_counter(mm, mm_counter_file(folio), -nr_mapped_ptes);
 	}

-- 
2.43.0


  parent reply	other threads:[~2026-10-26 23:23 UTC|newest]

Thread overview: 85+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 23:17 [PATCH 00/46] mm: various (urgent?) fixes David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 01/46] mm/mglru: don't use stack pointers when walking PUD and PMD tables David Hildenbrand (Arm)
2026-10-27  1:32   ` Rik van Riel
2026-10-27  3:05   ` Qi Zheng
2026-10-27  7:20   ` Dev Jain
2026-10-27  8:06   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 02/46] fork: take mmap_lock before copying mm_struct David Hildenbrand (Arm)
2026-10-27  1:38   ` Rik van Riel
2026-10-27  8:18   ` Vlastimil Babka (SUSE)
2026-10-26 23:17 ` [PATCH 03/46] mm/memfd: check for real shmem inodes before accessing shmem state David Hildenbrand (Arm)
2026-10-27  2:04   ` Rik van Riel
2026-10-27  8:13   ` Mike Rapoport
2026-10-26 23:17 ` [PATCH 04/46] mm/memfd: make f_pos accesses atomic David Hildenbrand (Arm)
2026-10-27  2:06   ` Rik van Riel
2026-10-27  3:34   ` Gregory Price
2026-10-27  8:44     ` Lorenzo Stoakes (ARM)
2026-10-27  8:15   ` Mike Rapoport
2026-10-27  8:46   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 05/46] mm/truncate: look again if a folio left the mapping David Hildenbrand (Arm)
2026-10-27  2:17   ` Rik van Riel
2026-10-26 23:17 ` [PATCH 06/46] mm/filemap: read mapping->a_ops->free_folio under i_lock David Hildenbrand (Arm)
2026-10-27  2:43   ` Rik van Riel
2026-10-26 23:17 ` [PATCH 07/46] mm/filemap: don't lose a folio lock wakeup to a signal David Hildenbrand (Arm)
2026-10-27  3:03   ` Rik van Riel
2026-10-26 23:17 ` [PATCH 08/46] mm/filemap: finish fault cleanup before dropping the fault lock David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 09/46] mm/rmap: handle device-exclusive entries in folio_referenced_one() David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 10/46] mm/huge_memory: fix list corruption when deferred_split_scan() races with deferred_split_folio() David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 11/46] mm/huge_memory: properly handle refcounted PUD mappings on fork David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 12/46] mm/huge_memory: fix livelock on device private folio David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 13/46] mm/huge_memory: fix truncate racing with PMD zapping David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 14/46] mm/huge_memory: fix zapping migration entries for device-private THPs David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 15/46] mm/khugepaged: always recheck the pmd in try_collapse_pte_mapped_thp() David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 16/46] mm/khugepaged: fix PMD table lock leak " David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 17/46] mm/khugepaged: preserve dirty bits when collapsing PTE-mapped THPs David Hildenbrand (Arm)
2026-10-26 23:17 ` David Hildenbrand (Arm) [this message]
2026-10-26 23:17 ` [PATCH 19/46] mm/gup: recheck the PUD under PTL in follow_pud_mask() David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 20/46] mm/gup: fix clearing folios in memfd_pin_folios() David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 21/46] mm/gup: fix out-of-bounds write for nr_pages == 0 in GUP-fast David Hildenbrand (Arm)
2026-10-27  9:37   ` Lorenzo Stoakes (ARM)
2026-10-27  9:40   ` Muchun Song
2026-10-26 23:17 ` [PATCH 22/46] mm/gup: check for overflow when converting nr_pages to bytes " David Hildenbrand (Arm)
2026-10-27  9:34   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 23/46] mm/userfaultfd: write-lock the VMA before waking waiters when unregistering David Hildenbrand (Arm)
2026-10-27  8:16   ` Mike Rapoport
2026-10-27  8:43   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 24/46] mm/userfaultfd: wake all waiters on unregister David Hildenbrand (Arm)
2026-10-27  8:17   ` Mike Rapoport
2026-10-27  8:36   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 25/46] mm/userfaultfd: wake waiters when registration modes are removed David Hildenbrand (Arm)
2026-10-27  5:10   ` Gregory Price
2026-10-27  8:24     ` Lorenzo Stoakes (ARM)
2026-10-27  8:21   ` Mike Rapoport
2026-10-27  8:31   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 26/46] mm/mmu_gather: fix flushing ranges with 4 GiB or larger David Hildenbrand (Arm)
2026-10-27  3:06   ` Qi Zheng
2026-10-27  3:40   ` Gregory Price
2026-10-27  7:35   ` Dev Jain
2026-10-26 23:17 ` [PATCH 27/46] mm/mmu_notifier: finish non-blocking interval invalidation on error David Hildenbrand (Arm)
2026-10-27  9:33   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 28/46] mm/mmu_notifier: set ->mm only after mmgrab() David Hildenbrand (Arm)
2026-10-27  3:42   ` Gregory Price
2026-10-27  8:50   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 29/46] mm/mremap: use pudp_huge_get_and_clear() when moving huge PUDs David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 30/46] mm/mremap: recheck PUDs under the page table lock David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 31/46] mm/mremap: fix moving PUD leaves David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 32/46] mm/memory_hotplug: avoid races in pfn_to_online_page() with dying pgmap David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 33/46] mm/madvise: fix data loss from MADV_FREE on a KSM page David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 34/46] mm/memory: reject remote COW faults on PFN mappings David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 35/46] mm: fix unmap_mapping_range() beyond 4 GiB on 32-bit David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 36/46] mm: fix unkillable fault loop on MAP_DROPPABLE memory David Hildenbrand (Arm)
2026-10-26 23:17 ` [PATCH 37/46] mm: fix deadlock of mremap() against UFFDIO_MOVE David Hildenbrand (Arm)
2026-10-27  9:23   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 38/46] mm/ksm: reset the unstable trees of all nodes David Hildenbrand (Arm)
2026-10-27  9:14   ` Lorenzo Stoakes (ARM)
2026-10-26 23:17 ` [PATCH 39/46] mm/ksm: fix use of a freed anon_vma at swap-in David Hildenbrand (Arm)
2026-10-26 23:18 ` [PATCH 40/46] mm/ksm: check large swap-cache folios before mapping them David Hildenbrand (Arm)
2026-10-27  9:00   ` Lorenzo Stoakes (ARM)
2026-10-26 23:18 ` [PATCH 41/46] mm/vma: call ->may_split() before a partial merge David Hildenbrand (Arm)
2026-10-27  3:45   ` Gregory Price
2026-10-26 23:18 ` [PATCH 42/46] mm/huge_memory: split huge PUDs when adjusting VMA boundaries David Hildenbrand (Arm)
2026-10-26 23:18 ` [PATCH 43/46] mm/vma: don't leak the charge for stack growth David Hildenbrand (Arm)
2026-10-26 23:18 ` [PATCH 44/46] mm/cma: don't release CMA pages still in use David Hildenbrand (Arm)
2026-10-27  8:55   ` Lorenzo Stoakes (ARM)
2026-10-26 23:18 ` [PATCH 45/46] mm/memory-tiers: unregister empty tiers outside memory_tier_lock David Hildenbrand (Arm)
2026-10-26 23:18 ` [PATCH 46/46] fs/proc/task_mmu: refuse addresses beyond 32 bits on 32bit David Hildenbrand (Arm)

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=20261027-fixes_core-v1-18-6f74bec9a5bb@kernel.org \
    --to=david@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=aneesh.kumar@kernel.org \
    --cc=apopple@nvidia.com \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=chengming.zhou@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=gourry@gourry.net \
    --cc=hannes@cmpxchg.org \
    --cc=harry@kernel.org \
    --cc=hughd@google.com \
    --cc=jack@suse.cz \
    --cc=jannh@google.com \
    --cc=jgg@ziepe.ca \
    --cc=jhubbard@nvidia.com \
    --cc=joshua.hahnjy@gmail.com \
    --cc=kas@kernel.org \
    --cc=kasong@tencent.com \
    --cc=kees@kernel.org \
    --cc=kexec@lists.infradead.org \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=matthew.brost@intel.com \
    --cc=mhocko@kernel.org \
    --cc=mhocko@suse.com \
    --cc=minchan@kernel.org \
    --cc=muchun.song@linux.dev \
    --cc=nico.pache@linux.dev \
    --cc=nphamcs@gmail.com \
    --cc=npiggin@gmail.com \
    --cc=osalvador@suse.de \
    --cc=pasha.tatashin@soleen.com \
    --cc=peterx@redhat.com \
    --cc=peterz@infradead.org \
    --cc=pfalcato@suse.de \
    --cc=pratyush@kernel.org \
    --cc=qi.zheng@linux.dev \
    --cc=rakie.kim@sk.com \
    --cc=riel@surriel.com \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shakeel.butt@linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=surenb@google.com \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=weixugc@google.com \
    --cc=will@kernel.org \
    --cc=willy@infradead.org \
    --cc=xu.xin@linux.dev \
    --cc=ying.huang@linux.alibaba.com \
    --cc=youngjun.park@lge.com \
    --cc=yuanchu@google.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

all inboxes | Powered by JetHome®