From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794585778; b=qbMgsiFpK/qAwy/+GkcdleTj/hZScJeYdymZ9Vu98a5NTNWbBqHAkUh4x2/127gIyHLM 68wgVELeU/uYzFwQMEWAFPOCh0WO8guNd6Twbba3dj914/VS5Gle0UIZlLfFTkJX2cbhw KD7KQ4f9LKMrgu7RCtIwv4jc8gGXwezMzq0nIDhMdknaeEXz1NAlwNyWylrvMjw9Z7kqy DqWoQSo3HdUpEftaFNUKnHgLHV7bZG6RQpB4JkFXkc4GVsUZE37+NLRpS7W66fyQXyxso 0V2oz+pVhBGh6k9954JVHL0gjr3PKTHGbx3ClazlJ4VcIwYLB64H0IXTgcLW6uLbRZQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794585778; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=fSju/jD9c8Jbwwg2zJACldgfFnwu72kQs5faGjFB/U4=; b=NhyNkQ0yCKpA0n6zPwflFo9Bt3mklgCOgj+14NPzToYV7WbV82bhJYqZhccxL9QNTSnW sElcy39Li+FLSJHBOzumkbkOYK4BK0eiqUGQqxitjiBQB0+Ui9OWHDp4+R/c0tkXEyq+4 c+BMDnJcxK8r0Dztdjd85je9qN23lZmllikIa9MDf/IvIK++nltU48SaQqBWPjkvSb+5z +4sTEHNyqGhbxyrVh14CCDqBNdFdkRuNyL4E3HmFYVcwEjA6qixFmwna/JEql11Lrknd5 mke4dWIyV1XyuR2i8Og6McLuHkUhaMunAtTlTIYRpaDqbbDJLnIDJSozMrBtTiWriAQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=rmy6eOM8; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E0C3B606A883; Fri, 13 Nov 2026 16:02:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=rmy6eOM8 Received: by smtp.kernel.org (Postfix) with ESMTPSA id 911A51F000C4; Fri, 13 Nov 2026 16:02:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1794585777; bh=fSju/jD9c8Jbwwg2zJACldgfFnwu72kQs5faGjFB/U4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=rmy6eOM89rG2pggUFDeGrLicI+sn1e5IeU8MHAqVu2K79qHFp2pfzPWgNiSQvYsyg d3v9R4ZXoi8IchsanfLVE2X0GBgi1EMb8JejVhXwRyHU8BGES4UffKKYcx2qlc0hGC 0PdCbeiQEm2xJFtGAsKazTfmBsPHDlzYXgVSo1xM= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Chris Mason , "Lorenzo Stoakes (ARM)" , Muhammad Usama Anjum , Lance Yang , "David Hildenbrand (Arm)" , Sasha Levin Subject: [PATCH 7.3 326/507] mm/ksm: check large swap-cache folios before mapping them Date: Fri, 13 Nov 2026 10:50:30 -0500 Message-ID: <20261113153806.431314048@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261113153753.358973689@linuxfoundation.org> References: <20261113153753.358973689@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Chris Mason [ Upstream commit 7f2c000ffb1047b476cbf267f741272c389d3e31 ] 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 Reviewed-by: Lorenzo Stoakes (ARM) Tested-by: Muhammad Usama Anjum Link: https://patch.msgid.link/20261031-fixes_core-v3-41-d6437171ecc3@kernel.org Reviewed-by: Lance Yang Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Sasha Levin --- 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 c600efa5396db..a554e5c0ed07a 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -3147,19 +3147,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; /* @@ -3176,20 +3177,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 f6f704d693f4f..77e0a9bf63211 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5063,7 +5063,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; @@ -5244,7 +5244,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 fbb4b3dcf9562..278060e241130 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2434,7 +2434,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.53.0