Linux-mm Archive on lore.kernel.org
 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,
	 Chris Mason <mason@kernel.org>,
	stable@vger.kernel.org,
	 Muhammad Usama Anjum <usama.anjum@arm.com>,
	 "David Hildenbrand (Arm)" <david@kernel.org>
Subject: [PATCH v3 41/47] mm/ksm: check large swap-cache folios before mapping them
Date: Sat, 31 Oct 2026 10:40:10 +0100	[thread overview]
Message-ID: <20261031-fixes_core-v3-41-d6437171ecc3@kernel.org> (raw)
In-Reply-To: <20261031-fixes_core-v3-0-d6437171ecc3@kernel.org>

From: Chris Mason <mason@kernel.org>

KSM folios are always small, but their shared swap slots can be read
back as part of a large folio in the swap cache. ksm_might_need_to_copy()
skips large folios, so another process can map a page with an incompatible
anon_vma or page index. Reverse mapping then cannot find the new
mapping, preventing migration or reclaim.

Apply the same compatibility checks to large folios as to small ones.
Pass the page corresponding to the swap entry and copy only that page
when necessary. In do_swap_page()'s copy path, decrement only that slot's
swap count, because the source swap-cache folio can now be large.

Fix generated by an LLM and sent by Chris Mason in private.

Fixes: f1879e8a0c60 ("mm, swap: never bypass the swap cache even for SWP_SYNCHRONOUS_IO")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
 include/linux/ksm.h |  6 +++---
 mm/ksm.c            | 22 ++++++++++++----------
 mm/memory.c         |  5 +++--
 mm/swapfile.c       |  3 ++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/include/linux/ksm.h b/include/linux/ksm.h
index d39d0d5483a23..a0d358a1feb63 100644
--- a/include/linux/ksm.h
+++ b/include/linux/ksm.h
@@ -91,8 +91,8 @@ static inline void ksm_exit(struct mm_struct *mm)
  * We'd like to make this conditional on vma->vm_flags & VM_MERGEABLE,
  * but what if the vma was unmerged while the page was swapped out?
  */
-struct folio *ksm_might_need_to_copy(struct folio *folio,
-			struct vm_area_struct *vma, unsigned long addr);
+struct folio *ksm_might_need_to_copy(struct folio *folio, struct page *page,
+		struct vm_area_struct *vma, unsigned long addr);
 
 void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc);
 void folio_migrate_ksm(struct folio *newfolio, struct folio *folio);
@@ -145,7 +145,7 @@ static inline int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
 }
 
 static inline struct folio *ksm_might_need_to_copy(struct folio *folio,
-			struct vm_area_struct *vma, unsigned long addr)
+		struct page *page, struct vm_area_struct *vma, unsigned long addr)
 {
 	return folio;
 }
diff --git a/mm/ksm.c b/mm/ksm.c
index e050b1bdf810d..e95f6b6723602 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -3138,19 +3138,20 @@ void __ksm_exit(struct mm_struct *mm)
 }
 
 static bool folio_anon_rmap_compatible(const struct folio *folio,
-		struct vm_area_struct *vma, unsigned long addr)
+		const struct page *page, struct vm_area_struct *vma,
+		unsigned long addr)
 {
 	const struct anon_vma *anon_vma = folio_anon_vma(folio);
 	struct anon_vma_chain *avc;
 
 	VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
-	VM_WARN_ON_ONCE_FOLIO(folio_test_large(folio), folio);
+	VM_WARN_ON_ONCE_FOLIO(page_folio(page) != folio, folio);
 	VM_WARN_ON_ONCE_FOLIO(folio_test_ksm(folio), folio);
 	vma_assert_stabilised(vma);
 
 	if (!anon_vma)
 		return true;
-	if (folio->index != linear_anon_page_index(vma, addr))
+	if (page_pgoff(folio, page) != linear_anon_page_index(vma, addr))
 		return false;
 
 	/*
@@ -3167,20 +3168,21 @@ static bool folio_anon_rmap_compatible(const struct folio *folio,
 	return false;
 }
 
-struct folio *ksm_might_need_to_copy(struct folio *folio,
-			struct vm_area_struct *vma, unsigned long addr)
+struct folio *ksm_might_need_to_copy(struct folio *folio, struct page *page,
+		struct vm_area_struct *vma, unsigned long addr)
 {
-	struct page *page = folio_page(folio, 0);
 	struct folio *new_folio;
 
-	if (folio_test_large(folio))
-		return folio;
-
+	/*
+	 * A KSM folio is small, but the slot that it left behind can be swapped
+	 * in as a part of a large folio by one of the processes that shared it.
+	 * So test, and copy, the page of the slot.
+	 */
 	if (folio_test_ksm(folio)) {
 		if (folio_stable_node(folio) &&
 		    !(ksm_run & KSM_RUN_UNMERGE))
 			return folio;	/* no need to copy it */
-	} else if (folio_anon_rmap_compatible(folio, vma, addr)) {
+	} else if (folio_anon_rmap_compatible(folio, page, vma, addr)) {
 		return folio;		/* still no need to copy it */
 	}
 	if (PageHWPoison(page))
diff --git a/mm/memory.c b/mm/memory.c
index 77d4bbf444bc2..91836d217d25d 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5096,7 +5096,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
 	 * folio->index of non-ksm folios would be nonlinear inside the
 	 * anon VMA -- the ksm flag is lost on actual swapout.
 	 */
-	folio = ksm_might_need_to_copy(folio, vma, vmf->address);
+	folio = ksm_might_need_to_copy(folio, page, vma, vmf->address);
 	if (unlikely(!folio)) {
 		ret = VM_FAULT_OOM;
 		folio = swapcache;
@@ -5278,7 +5278,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
 	if (unlikely(folio != swapcache)) {
 		folio_add_new_anon_rmap(folio, vma, address, RMAP_EXCLUSIVE);
 		folio_add_lru_vma(folio, vma);
-		folio_put_swap(swapcache, NULL);
+		folio_put_swap(swapcache,
+			       folio_file_page(swapcache, swp_offset(entry)));
 	} else if (!folio_test_anon(folio)) {
 		/*
 		 * We currently only expect !anon folios that are fully
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 254ce86fa923e..71ef9e2efb3a9 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2514,7 +2514,8 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
 		return 0;
 
 	swapcache = folio;
-	folio = ksm_might_need_to_copy(folio, vma, addr);
+	folio = ksm_might_need_to_copy(folio,
+				       folio_file_page(folio, swp_offset(entry)), vma, addr);
 	if (unlikely(!folio))
 		return -ENOMEM;
 	else if (unlikely(folio == ERR_PTR(-EHWPOISON))) {

-- 
2.43.0



  parent reply	other threads:[~2026-10-31  9:51 UTC|newest]

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

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=20261031-fixes_core-v3-41-d6437171ecc3@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=mason@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=stable@vger.kernel.org \
    --cc=surenb@google.com \
    --cc=usama.anjum@arm.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