From nobody Fri Oct 2 04:40:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0590D4322EE; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; cv=none; b=HClC9uip62WTeWcs8znltc0JOXL0g4/neItZ+OQvd0QDRVkRpf/7cy/wVjVXkdEmXH+ne6fJkXPfN1UhxzDo3EiB64fqzwEfpGCa7etb6/+VTo2Q6sGt4gBk/EmqUw97WxVFdBECbqr/zhZhGNaSIkWxiycHfkss5R0Ji9RpkD4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; c=relaxed/simple; bh=ty3w0m+qsJ+EdUZ0OVSMoR+uIMa3yGQbVbQdJHySOxk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=obv3vfJdtplMLJdn7X3mVemIvqR6XwlBkbcFufN/O8hoX1BgsbZG9lGz+N4hyfvbDpqLoZEPK9X/V4drkjWEunkITvIdB2chjsB8OfZ5+iRgdPeX9yeOLSEVdvW4+QxeWAgbxIsV0yyrtZ9336ADY0ezBqV3ZvNWa5FNemp1y+s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NLkYj5ZC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NLkYj5ZC" Received: by smtp.kernel.org (Postfix) with ESMTPS id 96480C2BCFA; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1785928708; bh=ty3w0m+qsJ+EdUZ0OVSMoR+uIMa3yGQbVbQdJHySOxk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=NLkYj5ZCS2eu1VzZ5NrkLjDOHGMSAmZqWjrVLbHr5u05W1pMfRSHQixyGNk67STBG f2Ybhg0qyToeMfOFJYV2LZtpSFmd8w4SEJg1W/pUlqIbrcbJa7c+OozGxL3YE7p23b v9gx3xpArrvMInBP1lYnJ8vCGSvvb3UTMMFp3zNz8FM/tSTc2fcfVo6dUTnFq8mRPy dUHA0pdK6smL+KuamHpHjaWBmEvYtLQHidZMLhJUooQnhsY8N9a22WLtQxdHLnDIpr WGXC/9sZhT51GBCxNGU3jHrXQewsATYscWjgUvtsDJ6tz5eDB7TeABqmRpm623M64H Eeo4YnuS5cU6g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 801D3C56201; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) From: Shivam Kalra via B4 Relay Date: Wed, 05 Aug 2026 16:48:24 +0530 Subject: [PATCH 1/3] mm/huge_memory: allow splitting mappingless swapcache folios Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-b4-mappingless-swapcache-v1-1-b1d78ba257ca@zohomail.in> References: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> In-Reply-To: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> To: Andrew Morton , David Hildenbrand , Lorenzo Stoakes , Zi Yan , Baolin Wang , "Liam R. Howlett" , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Usama Arif , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Nico Pache Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Shivam Kalra X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1785928706; l=2824; i=shivamkalra98@zohomail.in; s=20260402; h=from:subject:message-id; bh=8FMzJVK2ktfmBA3br5AL94P4EAWSWFZ1bi19CraqMDc=; b=vFVVaOmpFpx2FC4WlxGKq8HT1cGRL2x/naOSV50p6GJ3D5FhoW2agZET2hqyQ2/wsIkR5HfUk holeEC7s6tKAc9aKP5Ewd/xKNVTkdHwvi2HoPABOU3FeEYcq5KCwa8d X-Developer-Key: i=shivamkalra98@zohomail.in; a=ed25519; pk=U8kQSxcte8P8iZ6zB7phIj+Yl+i/5ntifBGuclgypx8= X-Endpoint-Received: by B4 Relay for shivamkalra98@zohomail.in/20260402 with auth_id=716 X-Original-From: Shivam Kalra Reply-To: shivamkalra98@zohomail.in From: Shivam Kalra A shmem folio is removed from its page cache when it is written to swap. While it remains in the swap cache, it has no address_space mapping, so folio_check_splittable() mistakes it for a truncated folio and rejects the split with -EBUSY. Allow a mappingless folio when it is in the swap cache. Initialize the XArray state without an address-space mapping, then assign the XArray only for mapped file folios. The split path already replaces each split folio in the swap cache while holding the swap-cluster lock. Signed-off-by: Shivam Kalra --- mm/huge_memory.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 804b8f6aa557..ecfe40b1400b 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3882,12 +3882,10 @@ int folio_check_splittable(struct folio *folio, uns= igned int new_order, VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio); /* * Folios that just got truncated cannot get split. Signal to the - * caller that there was a race. - * - * TODO: this will also currently refuse folios without a mapping in the - * swapcache (shmem or to-be-anon folios). + * caller that there was a race. A mappingless swapcache folio can be + * either shmem or not yet associated with an anon_vma, and is valid. */ - if (!folio->mapping && !folio_test_anon(folio)) + if (!folio->mapping && !folio_test_swapcache(folio)) return -EBUSY; =20 /* order-1 is not supported for anonymous THP. */ @@ -4022,10 +4020,7 @@ static int __folio_freeze_and_split_unmapped(struct = folio *folio, unsigned int n if (do_lru) lru_add_split_folio(folio, new_folio, lruvec, list); =20 - /* - * Anonymous folio with swap cache. - * NOTE: shmem in swap cache is not supported yet. - */ + /* Folio in the swap cache. */ if (ci) { __swap_cache_replace_folio(ci, folio, new_folio); continue; @@ -4103,7 +4098,7 @@ static int __folio_split(struct folio *folio, unsigne= d int new_order, struct page *split_at, struct page *lock_at, struct list_head *list, enum split_type split_type) { - XA_STATE(xas, &folio->mapping->i_pages, folio->index); + XA_STATE(xas, NULL, folio->index); struct folio *end_folio =3D folio_next(folio); bool is_anon =3D folio_test_anon(folio); struct mem_cgroup *memcg, *old_memcg; @@ -4158,11 +4153,12 @@ static int __folio_split(struct folio *folio, unsig= ned int new_order, } anon_vma_lock_write(anon_vma); mapping =3D NULL; - } else { + } else if (folio->mapping) { unsigned int min_order; gfp_t gfp; =20 mapping =3D folio->mapping; + xas.xa =3D &mapping->i_pages; min_order =3D mapping_min_folio_order(mapping); if (new_order < min_order) { ret =3D -EINVAL; --=20 2.43.0 From nobody Fri Oct 2 04:40:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 05A88432BCD; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; cv=none; b=hug7pdVr2x2TYuiCg4wB6z5wzsr2xolOX2G1pigUNqgBC/rYU+5vGv9/6aJVcWFJ35/k4g1GMbvu4fr8SAtXxBdmSTrOp+yvwpXwbRi36JHKioMfXFp2u0P6QvOzlTUlsDy20OAnMhX5OP6jAYac7L94S4TbAINoVweAV4rhFrM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; c=relaxed/simple; bh=UT1LepNEWbCDWJUaYqv5zlUitw1W3TiARj2cPHvoCUY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gNHIQ5x7SrFQkE1EkTAX0IGIKC/Zi+S/i+UJNolyrQq1te1w6+HED0SE2oSQQ/wq9MGgmDAy5y4y8n8MvFx/ZiKjpP1kwUc11P8PxIGKKEiRgAsJy0Sts6tJTchsPUozR1gN2EJapu0d7Rqx3FCf6KxOoi/71/Q3o/BFXpSXGyg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=t4bnVzSW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="t4bnVzSW" Received: by smtp.kernel.org (Postfix) with ESMTPS id B1C68C4AF09; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1785928708; bh=UT1LepNEWbCDWJUaYqv5zlUitw1W3TiARj2cPHvoCUY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=t4bnVzSW253zztAizs4KD1n/F8LF6BjSGZqoXiLNUHLADW43SyAl+F4vGC3mLRJ+G 1VIsXo/OelgBOP2pLzTSSTfsGRvcaQ3YhdVpRZD0jjtkFz1kI4pROqRNOWDt5Q4x2o ZGcrwwanX4CvtFlLtS0cTDmfxV+MQywibHlqLDQgZqK8tnpRDI0Xu8V2pUnVtBhDBx 1qghbEiFfddqgF+XYph7lFv3JglkGfCol5n0/SYZTF0mTTbQdU/iWK9RSP+RdaPJ82 CsG8jvkgSatiYLI88pIahlTv6hfJq9j+EFdjtAYRkXq9eNAbbyUNMLhv3xGIhfp0f9 bKMe0WZuzKzhA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 910CDC56203; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) From: Shivam Kalra via B4 Relay Date: Wed, 05 Aug 2026 16:48:25 +0530 Subject: [PATCH 2/3] mm: add KUnit coverage for mappingless swapcache folios Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-b4-mappingless-swapcache-v1-2-b1d78ba257ca@zohomail.in> References: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> In-Reply-To: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> To: Andrew Morton , David Hildenbrand , Lorenzo Stoakes , Zi Yan , Baolin Wang , "Liam R. Howlett" , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Usama Arif , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Nico Pache Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Shivam Kalra X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1785928706; l=3587; i=shivamkalra98@zohomail.in; s=20260402; h=from:subject:message-id; bh=3IHZ3BPfu/ApButT5xoLEv/VnC/YC3Wow9MhmnAZMzc=; b=kwJo2F0rrfAhLwyUMaWx9mR8UMYCkW+NJtcJ+vkgWEVRteK7dzLUswl4dzXKcjsjnKaY7gy+o aZ7xqewrI1jABqzhx/4AhTC7/o5MsARKWLGAI5QWdu0WThzRY5Qn8TP X-Developer-Key: i=shivamkalra98@zohomail.in; a=ed25519; pk=U8kQSxcte8P8iZ6zB7phIj+Yl+i/5ntifBGuclgypx8= X-Endpoint-Received: by B4 Relay for shivamkalra98@zohomail.in/20260402 with auth_id=716 X-Original-From: Shivam Kalra Reply-To: shivamkalra98@zohomail.in From: Shivam Kalra Add focused coverage for the folio_check_splittable() eligibility checks. Verify that an ordinary mappingless folio is rejected, a mappingless swapcache folio is accepted, and unsupported target orders and split types remain rejected. Build the test only when KUnit is built in because folio_check_splittable() is intentionally not exported. Signed-off-by: Shivam Kalra --- mm/Kconfig | 14 ++++++++++++ mm/Makefile | 1 + mm/tests/folio_split_kunit.c | 52 ++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 67 insertions(+) diff --git a/mm/Kconfig b/mm/Kconfig index 331daf7fcfab..164dc1438900 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1503,6 +1503,20 @@ config LAZY_MMU_MODE_KUNIT_TEST =20 If unsure, say N. =20 +config FOLIO_SPLIT_KUNIT_TEST + bool "KUnit tests for folio splitting" if !KUNIT_ALL_TESTS + depends on KUNIT=3Dy + depends on TRANSPARENT_HUGEPAGE + depends on SWAP + default KUNIT_ALL_TESTS + help + Enable this option to test folio split eligibility checks. The tests + verify support for mappingless folios in the swap cache and ensure + that unsupported target orders and split types are still rejected. + The tests are built into the kernel and run during KUnit execution. + + If unsure, say N. + source "mm/damon/Kconfig" =20 endmenu diff --git a/mm/Makefile b/mm/Makefile index ab37ef428d98..f09057b40e1a 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -146,4 +146,5 @@ obj-$(CONFIG_SHRINKER_DEBUG) +=3D shrinker_debug.o obj-$(CONFIG_EXECMEM) +=3D execmem.o obj-$(CONFIG_TMPFS_QUOTA) +=3D shmem_quota.o obj-$(CONFIG_LAZY_MMU_MODE_KUNIT_TEST) +=3D tests/lazy_mmu_mode_kunit.o +obj-$(CONFIG_FOLIO_SPLIT_KUNIT_TEST) +=3D tests/folio_split_kunit.o obj-$(CONFIG_MEM_ALLOC_PROFILING) +=3D alloc_tag.o diff --git a/mm/tests/folio_split_kunit.c b/mm/tests/folio_split_kunit.c new file mode 100644 index 000000000000..f91c1a320bbf --- /dev/null +++ b/mm/tests/folio_split_kunit.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +static void folio_check_splittable_mappingless_swapcache(struct kunit *tes= t) +{ + struct folio *folio; + int ret; + + folio =3D folio_alloc(GFP_KERNEL, 2); + KUNIT_ASSERT_NOT_NULL(test, folio); + folio_lock(folio); + + KUNIT_EXPECT_PTR_EQ(test, folio->mapping, NULL); + ret =3D folio_check_splittable(folio, 0, SPLIT_TYPE_UNIFORM); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + + /* Only the eligibility check is exercised here. */ + folio_set_swapbacked(folio); + folio_set_swapcache(folio); + + ret =3D folio_check_splittable(folio, 0, SPLIT_TYPE_UNIFORM); + KUNIT_EXPECT_EQ(test, ret, 0); + ret =3D folio_check_splittable(folio, 1, SPLIT_TYPE_UNIFORM); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + ret =3D folio_check_splittable(folio, 0, SPLIT_TYPE_NON_UNIFORM); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + folio_clear_swapcache(folio); + folio_clear_swapbacked(folio); + folio_unlock(folio); + folio_put(folio); +} + +static struct kunit_case folio_split_test_cases[] =3D { + KUNIT_CASE(folio_check_splittable_mappingless_swapcache), + {} +}; + +static struct kunit_suite folio_split_test_suite =3D { + .name =3D "folio_split", + .test_cases =3D folio_split_test_cases, +}; + +kunit_test_suite(folio_split_test_suite); + +MODULE_DESCRIPTION("Tests for folio splitting"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Fri Oct 2 04:40:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0585E3C3F60; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; cv=none; b=ROTq852r/Nci+vxmVChQYCGPriRTqreHQGQ/0nJ1jDP6UGsjqrG4E0WtWFZ2AZQcgazgNFznezuMvbF010KeuqubbE4aVyUydsD2Nv7MrWFuA652UjDGOE7i9ukDlGV3fu+qB3qrKodVFA1VVQMkvpk1KxxigPl30vGw7CikUEI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785928709; c=relaxed/simple; bh=YavjEdpwFHvPGjobDASf63jDi2KCsdxD16M82VRvqNs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QAmbazM+S6/389VnQ1qhL+JWgjz4oB6IvlqQomRyMh9GrfTZI+H6imPEUsbPFw4PDQruO0SPL6XwmeAQUxwQt1yeaE9W6TwSEIy/vm6lOV3zpuyFshkKg93Cdok0cPA3VAEb4BDdKVjuNrP0GukbTESzEDTjE6sj706gFqukTH8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=peM2spwe; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="peM2spwe" Received: by smtp.kernel.org (Postfix) with ESMTPS id C1952C2BD01; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1785928708; bh=YavjEdpwFHvPGjobDASf63jDi2KCsdxD16M82VRvqNs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=peM2spweHRV4pLfxMnUOjUUzSNvYTz+JeWlZdwLIp93S5B7Xq77XQgyhzBGEe9m6S mdJul/fof90IOMg13cqyvx5A5JjOIytRHstVj/pmEfGPBY5oHa1CDHwvt4C5mlqc22 4IwTQIhMLFaVUTdqhjPcByH2RA7s9da7lnUz5ufYbUppts8faC8StnoxDGLHFtFZPO 644kiMDWOf5cUS+5fKhAjh+L+6+Xf4/NyqBGeOoRCRS6kbR0OHxcywIf9lN4eEsusZ /s2FjPIVBZn877mdQRv6L+L40cZDoox72bzCrdSLJ/Us07zEuhGyJwxCAO8X3Sejy/ SaJeLVDGFhv6Q== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A3F5EC55ABA; Wed, 5 Aug 2026 11:18:28 +0000 (UTC) From: Shivam Kalra via B4 Relay Date: Wed, 05 Aug 2026 16:48:26 +0530 Subject: [PATCH 3/3] selftests/mm: test hwpoison recovery of mappingless swapcache THPs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260805-b4-mappingless-swapcache-v1-3-b1d78ba257ca@zohomail.in> References: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> In-Reply-To: <20260805-b4-mappingless-swapcache-v1-0-b1d78ba257ca@zohomail.in> To: Andrew Morton , David Hildenbrand , Lorenzo Stoakes , Zi Yan , Baolin Wang , "Liam R. Howlett" , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Usama Arif , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Shuah Khan , Nico Pache Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Shivam Kalra X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1785928706; l=11838; i=shivamkalra98@zohomail.in; s=20260402; h=from:subject:message-id; bh=fXyTWQ7RHulDRN52CXEgZSMCHIqkhJ6VQwjeCoCiP+M=; b=CNrFXdSVKLRfA/IsbpuHXTHssZsGOcmCVLpBjZ+t9wedKuPDagGPyNcDBjMYYs4dIEGWANPgR uGGodd2i/C4AaJSWdbVwA3hcsUkWYgyVtxx6H2T7k6y7WCQswhX3ZOE X-Developer-Key: i=shivamkalra98@zohomail.in; a=ed25519; pk=U8kQSxcte8P8iZ6zB7phIj+Yl+i/5ntifBGuclgypx8= X-Endpoint-Received: by B4 Relay for shivamkalra98@zohomail.in/20260402 with auth_id=716 X-Original-From: Shivam Kalra Reply-To: shivamkalra98@zohomail.in From: Shivam Kalra Exercise memory_failure() on a tail page of a shmem THP after MADV_PAGEOUT has made it a mappingless swapcache folio. Verify that the folio is split, only the target PFN is poisoned, and the remaining mapping can be faulted back in with its original data. Use a temporary loop-backed swap device so the test also works when the kselftest directory is on 9p or NFS. Register the wrapper in the memory-failure test suite and clean up the loop device and swap file on every exit path. Signed-off-by: Shivam Kalra --- tools/testing/selftests/mm/Makefile | 2 + tools/testing/selftests/mm/run_vmtests.sh | 1 + .../selftests/mm/split_hwpoison_swapcache.sh | 57 +++++ .../selftests/mm/split_hwpoison_swapcache_test.c | 261 +++++++++++++++++= ++++ 4 files changed, 321 insertions(+) diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/= mm/Makefile index 2d5366196e30..f0ae0b5685a6 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -93,6 +93,7 @@ TEST_GEN_FILES +=3D uffd-stress TEST_GEN_FILES +=3D uffd-unit-tests TEST_GEN_FILES +=3D uffd-wp-mremap TEST_GEN_FILES +=3D split_huge_page_test +TEST_GEN_FILES +=3D split_hwpoison_swapcache_test TEST_GEN_FILES +=3D ksm_tests TEST_GEN_FILES +=3D ksm_functional_tests TEST_GEN_FILES +=3D mdwe_test @@ -177,6 +178,7 @@ TEST_PROGS +=3D ksft_vmalloc.sh TEST_FILES :=3D test_vmalloc.sh TEST_FILES +=3D test_hmm.sh TEST_FILES +=3D va_high_addr_switch.sh +TEST_FILES +=3D split_hwpoison_swapcache.sh TEST_FILES +=3D charge_reserved_hugetlb.sh TEST_FILES +=3D hugetlb_reparenting_test.sh TEST_FILES +=3D test_page_frag.sh diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/self= tests/mm/run_vmtests.sh index 687d115e3bd8..39c8cf92ccc1 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -459,6 +459,7 @@ CATEGORY=3D"page_frag" run_test ./test_page_frag.sh non= aligned CATEGORY=3D"rmap" run_test ./rmap =20 CATEGORY=3D"memory-failure" run_test ./memory-failure +CATEGORY=3D"memory-failure" run_test ./split_hwpoison_swapcache.sh =20 echo "SUMMARY: PASS=3D${count_pass} SKIP=3D${count_skip} FAIL=3D${count_fa= il}" | tap_prefix echo "1..${count_total}" | tap_output diff --git a/tools/testing/selftests/mm/split_hwpoison_swapcache.sh b/tools= /testing/selftests/mm/split_hwpoison_swapcache.sh new file mode 100755 index 000000000000..fe986a09f697 --- /dev/null +++ b/tools/testing/selftests/mm/split_hwpoison_swapcache.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Setup swap and run the mappingless swapcache hwpoison split test + +ksft_skip=3D4 +swap_file=3D +loop_dev=3D + +skip() { + echo "skip: $*" + exit "$ksft_skip" +} + +cleanup() { + if [ -n "$loop_dev" ]; then + swapoff "$loop_dev" 2>/dev/null || true + losetup -d "$loop_dev" 2>/dev/null || true + fi + if [ -n "$swap_file" ]; then + rm -f "$swap_file" + fi +} + +trap cleanup EXIT +trap 'exit 1' HUP INT TERM + +if [ "$(id -u)" -ne 0 ]; then + skip "must run as root" +fi + +if [ ! -w /sys/kernel/debug/hwpoison/corrupt-pfn ]; then + skip "hwpoison injection is not available" +fi + +# Use a loop device because the kselftest directory may be on 9p or NFS. +if ! swap_file=3D$(mktemp /tmp/hwpoison_swap.XXXXXX); then + echo "FAIL: could not create a temporary swap file" + exit 1 +fi +if ! dd if=3D/dev/zero of=3D"$swap_file" bs=3D1M count=3D128 status=3Dnone= ; then + echo "FAIL: could not initialize the temporary swap file" + exit 1 +fi +if ! loop_dev=3D$(losetup --find --show "$swap_file"); then + skip "no loop device is available" +fi +if ! mkswap "$loop_dev" >/dev/null; then + echo "FAIL: could not initialize swap on $loop_dev" + exit 1 +fi +if ! swapon "$loop_dev"; then + echo "FAIL: could not enable swap on $loop_dev" + exit 1 +fi + +"$(dirname "$(readlink -f "$0")")"/split_hwpoison_swapcache_test diff --git a/tools/testing/selftests/mm/split_hwpoison_swapcache_test.c b/t= ools/testing/selftests/mm/split_hwpoison_swapcache_test.c new file mode 100644 index 000000000000..2d956ae517cf --- /dev/null +++ b/tools/testing/selftests/mm/split_hwpoison_swapcache_test.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test that memory failure can split a mappingless shmem THP in swap cach= e. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" +#include "hugepage_settings.h" +#include "vm_util.h" + +#define HWPOISON_PATH "/sys/kernel/debug/hwpoison/corrupt-pfn" + +#define KPF_MMAP BIT_ULL(11) +#define KPF_SWAPCACHE BIT_ULL(13) +#define KPF_SWAPBACKED BIT_ULL(14) + +#define TEST_BYTE 0x5a +#define PAGEOUT_RETRIES 100 +#define PAGEOUT_DELAY_US 100000 + +static bool is_swapcache_thp(uint64_t flags, bool head) +{ + uint64_t required =3D KPF_SWAPCACHE | KPF_SWAPBACKED | KPF_THP; + + required |=3D head ? KPF_COMPOUND_HEAD : KPF_COMPOUND_TAIL; + return (flags & required) =3D=3D required && !(flags & KPF_MMAP); +} + +static int wait_for_swapcache_thp(unsigned long head_pfn, unsigned long ta= rget_pfn, + int kpageflags_fd) +{ + uint64_t head_flags =3D 0; + uint64_t target_flags =3D 0; + int i; + + for (i =3D 0; i < PAGEOUT_RETRIES; i++) { + if (pageflags_get(head_pfn, kpageflags_fd, &head_flags) || + pageflags_get(target_pfn, kpageflags_fd, &target_flags)) + return -1; + + if (is_swapcache_thp(head_flags, true) && + is_swapcache_thp(target_flags, false)) + return 0; + + usleep(PAGEOUT_DELAY_US); + } + + ksft_print_msg("Swapcache THP flags: head=3D%#llx target=3D%#llx\n", + (unsigned long long)head_flags, + (unsigned long long)target_flags); + return 1; +} + +static bool folio_was_split(unsigned long head_pfn, unsigned long target_p= fn, + unsigned long nr_pages, int kpageflags_fd) +{ + const uint64_t compound =3D KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL; + uint64_t flags; + unsigned long i; + + for (i =3D 0; i < nr_pages; i++) { + if (pageflags_get(head_pfn + i, kpageflags_fd, &flags)) + return false; + if (flags & compound) { + ksft_print_msg("PFN %#lx is still compound (flags=3D%#llx)\n", + head_pfn + i, + (unsigned long long)flags); + return false; + } + if ((head_pfn + i =3D=3D target_pfn) !=3D !!(flags & KPF_HWPOISON)) { + ksft_print_msg( + "Unexpected HWPoison state at PFN %#lx (flags=3D%#llx)\n", + head_pfn + i, (unsigned long long)flags); + return false; + } + } + + return true; +} + +static bool mapping_has_expected_data(const unsigned char *addr, size_t si= ze) +{ + size_t i; + + for (i =3D 0; i < size; i++) { + if (addr[i] !=3D TEST_BYTE) { + ksft_print_msg("Data mismatch at offset %#zx: %#x !=3D %#x\n", + i, addr[i], TEST_BYTE); + return false; + } + } + + return true; +} + +static int inject_hwpoison(unsigned long pfn) +{ + char buf[32]; + ssize_t written; + int fd; + int len; + int saved_errno; + + fd =3D open(HWPOISON_PATH, O_WRONLY); + if (fd < 0) + return -errno; + + len =3D snprintf(buf, sizeof(buf), "%#lx\n", pfn); + written =3D write(fd, buf, len); + saved_errno =3D errno; + close(fd); + + if (written !=3D len) + return written < 0 ? -saved_errno : -EIO; + + return 0; +} + +int main(void) +{ + struct thp_settings settings; + unsigned long target_pfn; + unsigned long head_pfn; + unsigned long nr_pages; + unsigned long page_size; + unsigned long pmd_size; + unsigned char *mapping; + unsigned char *addr; + uint64_t flags; + bool poisoned =3D false; + bool pass =3D false; + int kpageflags_fd =3D -1; + int pagemap_fd =3D -1; + int memfd =3D -1; + int ret; + + ksft_print_header(); + ksft_set_plan(1); + + if (geteuid()) + ksft_exit_skip("Please run the test as root\n"); + + pmd_size =3D read_pmd_pagesize(); + if (!thp_available() || !pmd_size) + ksft_exit_skip("Transparent Huge Pages are not available\n"); + + if (access(HWPOISON_PATH, W_OK)) + ksft_exit_skip("HWPoison injection is not available\n"); + + page_size =3D getpagesize(); + if (pmd_size % page_size) + ksft_exit_fail_msg("Invalid PMD page size %#lx\n", pmd_size); + nr_pages =3D pmd_size / page_size; + + thp_save_settings(); + thp_read_settings(&settings); + settings.shmem_enabled =3D SHMEM_ADVISE; + thp_write_settings(&settings); + + memfd =3D memfd_create("split_hwpoison_swapcache", MFD_CLOEXEC); + if (memfd < 0) + ksft_exit_fail_perror("memfd_create"); + if (ftruncate(memfd, pmd_size)) + ksft_exit_fail_perror("ftruncate"); + + /* Reserve enough space to obtain a PMD-aligned file mapping. */ + mapping =3D mmap(NULL, 2 * pmd_size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mapping =3D=3D MAP_FAILED) + ksft_exit_fail_perror("mmap"); + addr =3D (unsigned char *)(((uintptr_t)mapping + pmd_size - 1) & + ~(pmd_size - 1)); + if (mmap(addr, pmd_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, memfd, 0) =3D=3D MAP_FAILED) + ksft_exit_fail_perror("mmap"); + + if (madvise(addr, pmd_size, MADV_HUGEPAGE)) + ksft_exit_fail_perror("madvise(MADV_HUGEPAGE)"); + memset(addr, TEST_BYTE, pmd_size); + + if (!check_huge_shmem(addr, 1, pmd_size)) + ksft_exit_skip("Failed to allocate a PMD-sized shmem THP\n"); + + pagemap_fd =3D open("/proc/self/pagemap", O_RDONLY); + if (pagemap_fd < 0) + ksft_exit_fail_perror("open(/proc/self/pagemap)"); + kpageflags_fd =3D open("/proc/kpageflags", O_RDONLY); + if (kpageflags_fd < 0) + ksft_exit_fail_perror("open(/proc/kpageflags)"); + + head_pfn =3D pagemap_get_pfn(pagemap_fd, (char *)addr); + if (head_pfn =3D=3D -1UL) + ksft_exit_fail_msg("Failed to obtain the shmem THP PFN\n"); + + /* Poison a tail page so success necessarily requires a real split. */ + target_pfn =3D head_pfn + nr_pages / 2; + if (pageflags_get(head_pfn, kpageflags_fd, &flags) || + (flags & (KPF_THP | KPF_COMPOUND_HEAD)) !=3D + (KPF_THP | KPF_COMPOUND_HEAD)) + ksft_exit_fail_msg("PFN %#lx is not a THP head\n", head_pfn); + if (pageflags_get(target_pfn, kpageflags_fd, &flags) || + (flags & (KPF_THP | KPF_COMPOUND_TAIL)) !=3D + (KPF_THP | KPF_COMPOUND_TAIL)) + ksft_exit_fail_msg("PFN %#lx is not a THP tail\n", target_pfn); + + if (madvise(addr, pmd_size, MADV_PAGEOUT)) + ksft_exit_skip("madvise(MADV_PAGEOUT) failed: %s\n", + strerror(errno)); + + ret =3D wait_for_swapcache_thp(head_pfn, target_pfn, kpageflags_fd); + if (ret < 0) + ksft_exit_fail_msg("Failed to read kpageflags\n"); + if (ret > 0) + ksft_exit_skip("Failed to create a mappingless swapcache THP; " + "is swap enabled?\n"); + + ksft_print_msg("Injecting HWPoison into tail PFN %#lx of THP %#lx\n", + target_pfn, head_pfn); + ret =3D inject_hwpoison(target_pfn); + poisoned =3D true; + if (ret) { + ksft_print_msg("HWPoison injection failed: %s\n", strerror(-ret)); + goto out; + } + + if (!folio_was_split(head_pfn, target_pfn, nr_pages, kpageflags_fd)) + goto out; + + /* + * A clean poisoned swapcache page is discarded. Faulting the mapping + * back in must recover the original data from swap. + */ + if (!mapping_has_expected_data(addr, pmd_size)) + goto out; + + pass =3D true; +out: + if (poisoned && unpoison_memory(target_pfn)) { + ksft_print_msg("Failed to unpoison PFN %#lx\n", target_pfn); + pass =3D false; + } + if (kpageflags_fd >=3D 0) + close(kpageflags_fd); + if (pagemap_fd >=3D 0) + close(pagemap_fd); + munmap(mapping, 2 * pmd_size); + close(memfd); + + ksft_test_result(pass, "memory failure splits a mappingless swapcache THP= \n"); + ksft_finished(); +} --=20 2.43.0