From: Nico Pache <npache@redhat.com>
To: linux-mm@kvack.org, linux-kernel@vger.kernel.org
Cc: yuzhao@google.com, usamaarif642@gmail.com, lance.yang@linux.dev,
baohua@kernel.org, dev.jain@arm.com, ryan.roberts@arm.com,
liam@infradead.org, baolin.wang@linux.alibaba.com,
ziy@nvidia.com, ljs@kernel.org, david@kernel.org,
akpm@linux-foundation.org, Nico Pache <npache@redhat.com>
Subject: [RFC] mm: restrict zero-page remapping to underused THP splits
Date: Fri, 8 May 2026 11:05:09 -0600 [thread overview]
Message-ID: <20260508170509.640851-1-npache@redhat.com> (raw)
Since commit b1f202060afe ("mm: remap unused subpages to shared zeropage
when splitting isolated thp"), splitting an anonymous THP remaps all
zero-filled subpages to the shared zeropage via TTU_USE_SHARED_ZEROPAGE.
This flag is set unconditionally for every anonymous folio split,
including splits triggered by KSM.
When KSM is enabled with THP=always, this causes two regressions:
1. use_zero_pages=1: KSM calls try_to_merge_one_page() which triggers
split_huge_page(). The split remaps all 512 zero-filled subpages to
the shared zeropage at once, freeing the entire 2MB THP when KSM only
intended to process a single 4KB page. This bypasses KSM's
pages_to_scan rate limiting, causing ~1GB to be freed almost
instantly.
2. use_zero_pages=0: The same split side-effect occurs through the
stable/unstable tree merge paths. Each pages_to_scan iteration
triggers an expensive split_huge_page() that silently frees 2MB,
while the scanner wastes cycles on tree searches for zero-filled
pages that were already freed as a side-effect.
Fix this by restricting TTU_USE_SHARED_ZEROPAGE to only the deferred
split shrinker path (deferred_split_scan), which is the only caller that
intentionally splits underused THPs to reclaim zero-filled subpages.
Introduce folio_split_underused() as a dedicated entry point that
passes is_underused_thp=true through __folio_split(), and use it from
deferred_split_scan(). All other split callers (KSM, compaction, etc.)
no longer get the zero-page remapping side-effect.
Reviewers notes: this patch is one of two potential approaches. This patch
turns off the zero-page freeing that has been done since the noted commit,
in all the other callers, only leaving the underused shrinker to do such
behavior. We can also take the opposite approach of with something like
split_huge_page_no_zeropage() and call this within KSM.
Fixes: b1f202060afe ("mm: remap unused subpages to shared zeropage when splitting isolated thp")
Signed-off-by: Nico Pache <npache@redhat.com>
---
include/linux/huge_mm.h | 2 +-
mm/huge_memory.c | 17 ++++++++++++-----
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 2949e5acff35..4ae1b52d7411 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -378,7 +378,7 @@ int folio_check_splittable(struct folio *folio, unsigned int new_order,
enum split_type split_type);
int folio_split(struct folio *folio, unsigned int new_order, struct page *page,
struct list_head *list);
-
+int folio_split_underused(struct folio *folio);
static inline int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
unsigned int new_order)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 970e077019b7..91f7fad72c8a 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4045,7 +4045,8 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
*/
static int __folio_split(struct folio *folio, unsigned int new_order,
struct page *split_at, struct page *lock_at,
- struct list_head *list, enum split_type split_type)
+ struct list_head *list, enum split_type split_type,
+ bool is_underused_thp)
{
XA_STATE(xas, &folio->mapping->i_pages, folio->index);
struct folio *end_folio = folio_next(folio);
@@ -4174,7 +4175,7 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
if (nr_shmem_dropped)
shmem_uncharge(mapping->host, nr_shmem_dropped);
- if (!ret && is_anon && !folio_is_device_private(folio))
+ if (!ret && is_anon && !folio_is_device_private(folio) && is_underused_thp)
ttu_flags = TTU_USE_SHARED_ZEROPAGE;
remap_page(folio, 1 << old_order, ttu_flags);
@@ -4309,7 +4310,7 @@ int __split_huge_page_to_list_to_order(struct page *page, struct list_head *list
struct folio *folio = page_folio(page);
return __folio_split(folio, new_order, &folio->page, page, list,
- SPLIT_TYPE_UNIFORM);
+ SPLIT_TYPE_UNIFORM, false);
}
/**
@@ -4340,7 +4341,13 @@ int folio_split(struct folio *folio, unsigned int new_order,
struct page *split_at, struct list_head *list)
{
return __folio_split(folio, new_order, split_at, &folio->page, list,
- SPLIT_TYPE_NON_UNIFORM);
+ SPLIT_TYPE_NON_UNIFORM, false);
+}
+
+int folio_split_underused(struct folio *folio)
+{
+ return __folio_split(folio, 0, &folio->page, &folio->page,
+ NULL, SPLIT_TYPE_NON_UNIFORM, true);
}
/**
@@ -4559,7 +4566,7 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
}
if (!folio_trylock(folio))
goto requeue;
- if (!split_folio(folio)) {
+ if (!folio_split_underused(folio)) {
did_split = true;
if (underused)
count_vm_event(THP_UNDERUSED_SPLIT_PAGE);
--
2.54.0
next reply other threads:[~2026-05-08 17:04 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 17:05 Nico Pache [this message]
2026-05-08 21:32 ` [RFC] mm: restrict zero-page remapping to underused THP splits David Hildenbrand (Arm)
2026-05-09 8:25 ` Lance Yang
2026-05-10 11:39 ` Usama Arif
2026-05-11 6:36 ` David Hildenbrand (Arm)
2026-05-11 13:10 ` Usama Arif
2026-05-11 13:42 ` David Hildenbrand (Arm)
2026-05-11 13:44 ` David Hildenbrand (Arm)
2026-05-11 14:15 ` Usama Arif
2026-05-11 18:40 ` Nico Pache
2026-05-09 3:21 ` Lance Yang
2026-05-11 18:42 ` Nico Pache
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=20260508170509.640851-1-npache@redhat.com \
--to=npache@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=ryan.roberts@arm.com \
--cc=usamaarif642@gmail.com \
--cc=yuzhao@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