mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Fang Xieyan <fangxy@xiaopeng.com>
To: Andrew Morton <akpm@linux-foundation.org>,
	David Hildenbrand <david@kernel.org>, Zi Yan <ziy@nvidia.com>,
	Matthew Wilcox <willy@infradead.org>, Jan Kara <jack@suse.cz>,
	Hugh Dickins <hughd@google.com>, Carlos Maiolino <cem@kernel.org>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>,
	"Liam R . Howlett" <liam@infradead.org>,
	Nico Pache <nico.pache@linux.dev>,
	Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
	Barry Song <baohua@kernel.org>, Lance Yang <lance.yang@linux.dev>,
	Usama Arif <usama.arif@linux.dev>,
	Kiryl Shutsemau <kas@kernel.org>,
	Lorenzo Stoakes <ljs@kernel.org>,
	Vlastimil Babka <vbabka@kernel.org>,
	Mike Rapoport <rppt@kernel.org>,
	Suren Baghdasaryan <surenb@google.com>,
	Michal Hocko <mhocko@suse.com>,
	Muchun Song <muchun.song@linux.dev>,
	Kairui Song <kasong@tencent.com>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, linux-xfs@vger.kernel.org,
	Liu Chao <liuc63@xiaopeng.com>
Subject: [RFC PATCH 1/3] mm: move folio split code into mm/folio_split.c
Date: Tue, 10 Nov 2026 11:52:03 +0800	[thread overview]
Message-ID: <20261110035205.3036-2-fangxy@xiaopeng.com> (raw)
In-Reply-To: <20261110035205.3036-1-fangxy@xiaopeng.com>

Move the folio split implementation out of mm/huge_memory.c into a new
file, mm/folio_split.c, so that the file-backed split path can later be
built without transparent hugepages.

This is a pure move: the functions are copied verbatim and in their
original order.  The only edits are

 - folio_reset_partially_mapped() is no longer static and gains a
   declaration in mm/internal.h, because deferred_split_scan() in
   huge_memory.c keeps calling it,

 - the new file gets its own include list and is added to
   obj-$(CONFIG_TRANSPARENT_HUGEPAGE), and

 - mm/folio_split.c is added to the THP entry in MAINTAINERS.

The moved functions are unmap_folio(), remap_anon_folio(),
lru_add_split_folio(), page_range_has_hwpoisoned(),
__split_folio_to_order(), __split_frozen_folio(),
folio_check_splittable(), folio_swapcache_ref_count(),
folio_reset_partially_mapped(), __folio_freeze_split_anon(),
__folio_freeze_split_file(), __folio_split(), folio_split_unmapped(),
__split_huge_page_to_list_to_order(), folio_split(),
min_order_for_split() and split_folio_to_list().

The anonymous freeze path moves along with the file one for now,
because both use unmap_folio(), __split_frozen_folio() and
lru_add_split_folio().  Moving only the file path would have required
four cross-file helpers where one is enough today.

No functional change intended.  Verified by building the same
configuration at the parent commit: the symbol sizes of the moved
functions are unchanged, and the disassembly of the moved code only
differs where a jump had to switch to a longer encoding because its
target left the rel8 range.

Reviewed-by: Liu Chao <liuc63@xiaopeng.com>
Signed-off-by: Fang Xieyan <fangxy@xiaopeng.com>
---
 MAINTAINERS      |   1 +
 mm/Makefile      |   2 +-
 mm/folio_split.c | 947 +++++++++++++++++++++++++++++++++++++++++++++++
 mm/huge_memory.c | 920 ---------------------------------------------
 mm/internal.h    |   1 +
 5 files changed, 950 insertions(+), 921 deletions(-)
 create mode 100644 mm/folio_split.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0cf24eee2..d1b1dee6f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17943,6 +17943,7 @@ F:	include/linux/khugepaged.h
 F:	include/trace/events/huge_memory.h
 F:	include/trace/events/thp.h
 F:	mm/collapse.h
+F:	mm/folio_split.c
 F:	mm/huge_memory.c
 F:	mm/khugepaged.c
 F:	mm/mm_slot.h
diff --git a/mm/Makefile b/mm/Makefile
index 277b97f7f..7f7f2c114 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -100,7 +100,7 @@ obj-$(CONFIG_MEMTEST)		+= memtest.o
 obj-$(CONFIG_MIGRATION) += migrate.o
 obj-$(CONFIG_NUMA) += memory-tiers.o
 obj-$(CONFIG_DEVICE_MIGRATION) += migrate_device.o
-obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o khugepaged.o
+obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += folio_split.o huge_memory.o khugepaged.o
 obj-$(CONFIG_PAGE_COUNTER) += page_counter.o
 obj-$(CONFIG_LIVEUPDATE_MEMFD) += memfd_luo.o
 obj-$(CONFIG_MEMCG_V1) += memcontrol-v1.o
diff --git a/mm/folio_split.c b/mm/folio_split.c
new file mode 100644
index 000000000..7770d8215
--- /dev/null
+++ b/mm/folio_split.c
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Folio split implementation, shared by the file-backed and the anonymous
+ * paths.
+ *
+ * Extracted from mm/huge_memory.c without functional changes, so that the
+ * file-backed path can later be built without transparent hugepages.
+ */
+
+#include <linux/mm.h>
+#include <linux/sched/mm.h>
+#include <linux/rmap.h>
+#include <linux/swap.h>
+#include <linux/mm_inline.h>
+#include <linux/swapops.h>
+#include <linux/backing-dev.h>
+#include <linux/dax.h>
+#include <linux/pagemap.h>
+#include <linux/migrate.h>
+#include <linux/memcontrol.h>
+#include <linux/shmem_fs.h>
+#include <linux/page_owner.h>
+#include <linux/pgalloc_tag.h>
+#include <linux/vmstat.h>
+
+#include "internal.h"
+#include "swap.h"
+
+/*
+ * A return value of 0 does not mean that unmapping succeeded. It might
+ * still have failed, but remap_anon_folio() must be called afterwards,
+ * for anon folios.
+ */
+static int unmap_folio(struct folio *folio)
+{
+	enum ttu_flags ttu_flags = TTU_RMAP_LOCKED | TTU_SYNC |
+		TTU_BATCH_FLUSH;
+
+	VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
+
+	/* Racy check if we can split the page, before we split PMDs */
+	if (folio_expected_ref_count(folio) != folio_ref_count(folio) - 1)
+		return -EAGAIN;
+
+	if (folio_test_pmd_mappable(folio))
+		ttu_flags |= TTU_SPLIT_HUGE_PMD;
+
+	/*
+	 * Anon pages need migration entries to preserve them, but file
+	 * pages can simply be left unmapped, then faulted back on demand.
+	 */
+	if (folio_test_anon(folio))
+		try_to_migrate(folio, ttu_flags);
+	else
+		try_to_unmap(folio, ttu_flags | TTU_IGNORE_MLOCK);
+
+	try_to_unmap_flush();
+
+	return 0;
+}
+
+static void remap_anon_folio(struct folio *folio, unsigned long nr, int flags)
+{
+	int i = 0;
+
+	/*
+	 * unmap_folio() installs migration entries only for anon folios,
+	 * so currently only anon folios need to be remapped. File folios
+	 * stay unmapped after the split and are faulted back on demand.
+	 */
+	VM_WARN_ON_FOLIO(!folio_test_anon(folio), folio);
+
+	for (;;) {
+		remove_migration_ptes(folio, folio, TTU_RMAP_LOCKED | flags);
+		i += folio_nr_pages(folio);
+		if (i >= nr)
+			break;
+		folio = folio_next(folio);
+	}
+}
+
+static void lru_add_split_folio(struct folio *folio, struct folio *new_folio,
+		struct lruvec *lruvec, struct list_head *list)
+{
+	VM_BUG_ON_FOLIO(folio_test_lru(new_folio), folio);
+	lockdep_assert_held(&lruvec->lru_lock);
+
+	if (folio_is_device_private(folio))
+		return;
+
+	if (list) {
+		/* page reclaim is reclaiming a huge page */
+		VM_WARN_ON(folio_test_lru(folio));
+		folio_get(new_folio);
+		list_add_tail(&new_folio->lru, list);
+	} else {
+		/* head is still on lru (and we have it frozen) */
+		VM_WARN_ON(!folio_test_lru(folio));
+		if (folio_test_unevictable(folio))
+			new_folio->mlock_count = 0;
+		else
+			list_add_tail(&new_folio->lru, &folio->lru);
+		folio_set_lru(new_folio);
+	}
+}
+
+static bool page_range_has_hwpoisoned(struct page *page, long nr_pages)
+{
+	for (; nr_pages; page++, nr_pages--)
+		if (PageHWPoison(page))
+			return true;
+	return false;
+}
+
+/*
+ * It splits @folio into @new_order folios and copies the @folio metadata to
+ * all the resulting folios.
+ */
+static void __split_folio_to_order(struct folio *folio, int old_order,
+		int new_order)
+{
+	/* Scan poisoned pages when split a poisoned folio to large folios */
+	const bool handle_hwpoison = folio_test_has_hwpoisoned(folio) && new_order;
+	long new_nr_pages = 1 << new_order;
+	long nr_pages = 1 << old_order;
+	long i;
+
+	folio_clear_has_hwpoisoned(folio);
+
+	/* Check first new_nr_pages since the loop below skips them */
+	if (handle_hwpoison &&
+	    page_range_has_hwpoisoned(folio_page(folio, 0), new_nr_pages))
+		folio_set_has_hwpoisoned(folio);
+	/*
+	 * Skip the first new_nr_pages, since the new folio from them have all
+	 * the flags from the original folio.
+	 */
+	for (i = new_nr_pages; i < nr_pages; i += new_nr_pages) {
+		struct page *new_head = &folio->page + i;
+		/*
+		 * Careful: new_folio is not a "real" folio before we cleared PageTail.
+		 * Don't pass it around before clear_compound_head().
+		 */
+		struct folio *new_folio = (struct folio *)new_head;
+
+		VM_BUG_ON_PAGE(atomic_read(&new_folio->_mapcount) != -1, new_head);
+
+		/*
+		 * Clone page flags before unfreezing refcount.
+		 *
+		 * After successful get_page_unless_zero() might follow flags change,
+		 * for example lock_page() which set PG_waiters.
+		 *
+		 * Note that for mapped sub-pages of an anonymous THP,
+		 * PG_anon_exclusive has been cleared in unmap_folio() and is stored in
+		 * the migration entry instead from where remap_anon_folio() will restore it.
+		 * We can still have PG_anon_exclusive set on effectively unmapped and
+		 * unreferenced sub-pages of an anonymous THP: we can simply drop
+		 * PG_anon_exclusive (-> PG_mappedtodisk) for these here.
+		 */
+		new_folio->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
+		new_folio->flags.f |= (folio->flags.f &
+				((1L << PG_referenced) |
+				 (1L << PG_swapbacked) |
+				 (1L << PG_swapcache) |
+				 (1L << PG_mlocked) |
+				 (1L << PG_uptodate) |
+				 (1L << PG_active) |
+				 (1L << PG_workingset) |
+				 (1L << PG_locked) |
+				 (1L << PG_unevictable) |
+#ifdef CONFIG_ARCH_USES_PG_ARCH_2
+				 (1L << PG_arch_2) |
+#endif
+#ifdef CONFIG_ARCH_USES_PG_ARCH_3
+				 (1L << PG_arch_3) |
+#endif
+				 (1L << PG_dirty) |
+				 (1L << PG_dropbehind) |
+				 LRU_GEN_MASK | LRU_REFS_MASK));
+
+		new_folio->mapping = folio->mapping;
+		new_folio->index = folio->index + i;
+
+		/*
+		 * page->private should not be set in tail pages. Warn once
+		 * if private is unexpectedly set. Do it before swap.val assignment
+		 * since private overlaps with swap.val.
+		 */
+		VM_WARN_ON_ONCE_PAGE(new_folio->private, new_head);
+
+		/*
+		 * Not all folio fields are valid during a split, so open-code
+		 * the swap entry rather than using folio_swap_entry().
+		 */
+		if (folio_test_swapcache(folio))
+			new_folio->swap.val = folio->swap.val + i;
+
+		/* Page flags must be visible before we make the page non-compound. */
+		smp_wmb();
+
+		/*
+		 * Clear PageTail before unfreezing page refcount.
+		 *
+		 * After successful get_page_unless_zero() might follow put_page()
+		 * which needs correct compound_head().
+		 */
+		clear_compound_head(new_head);
+		if (new_order) {
+			prep_compound_page(new_head, new_order);
+			folio_set_large_rmappable(new_folio);
+		}
+
+		/*
+		 * PG_has_hwpoisoned is on the 2nd page, so set it after
+		 * the compound head is prepped.
+		 */
+		if (handle_hwpoison &&
+		    page_range_has_hwpoisoned(new_head, new_nr_pages))
+			folio_set_has_hwpoisoned(new_folio);
+
+		if (folio_test_young(folio))
+			folio_set_young(new_folio);
+		if (folio_test_idle(folio))
+			folio_set_idle(new_folio);
+#ifdef CONFIG_MEMCG
+		new_folio->memcg_data = folio->memcg_data;
+#endif
+
+		folio_xchg_last_cpupid(new_folio, folio_last_cpupid(folio));
+	}
+
+	if (new_order)
+		folio_set_order(folio, new_order);
+	else
+		ClearPageCompound(&folio->page);
+}
+
+/**
+ * __split_frozen_folio() - splits a frozen @folio to lower order folios
+ * in two ways: uniform split or non-uniform split.
+ * @folio: the to-be-split folio
+ * @new_order: the smallest order of the after split folios (since buddy
+ *             allocator like split generates folios with orders from @folio's
+ *             order - 1 to new_order).
+ * @split_at: in buddy allocator like split, the folio containing @split_at
+ *            will be split until its order becomes @new_order.
+ * @xas: xa_state pointing to folio->mapping->i_pages and locked by caller
+ * @split_type: if the split is uniform or not (buddy allocator like split)
+ *
+ *
+ * 1. uniform split: the given @folio into multiple @new_order small folios,
+ *    where all small folios have the same order. This is done when
+ *    split_type is SPLIT_TYPE_UNIFORM.
+ * 2. buddy allocator like (non-uniform) split: the given @folio is split into
+ *    half and one of the half (containing the given page) is split into half
+ *    until the given @folio's order becomes @new_order. This is done when
+ *    split_type is SPLIT_TYPE_NON_UNIFORM.
+ *
+ * The high level flow for these two methods are:
+ *
+ * 1. uniform split: @xas is split with no expectation of failure and a single
+ *    __split_folio_to_order() is called to split the @folio into @new_order
+ *    along with stats update.
+ * 2. non-uniform split: folio_order - @new_order calls to
+ *    __split_folio_to_order() are expected to be made in a for loop to split
+ *    the @folio to one lower order at a time. The folio containing @split_at
+ *    is split in each iteration. @xas is split into half in each iteration and
+ *    can fail. A failed @xas split leaves split folios as is without merging
+ *    them back.
+ *
+ * After splitting, the caller's folio reference will be transferred to the
+ * folio containing @split_at. The caller needs to unlock and/or free
+ * after-split folios if necessary.
+ *
+ * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
+ * split but not to @new_order, the caller needs to check)
+ */
+static int __split_frozen_folio(struct folio *folio, int new_order,
+		struct page *split_at, struct xa_state *xas,
+		enum split_type split_type)
+{
+	const bool is_anon = folio_test_anon(folio);
+	int old_order = folio_order(folio);
+	int start_order = split_type == SPLIT_TYPE_UNIFORM ? new_order : old_order - 1;
+	struct folio *old_folio = folio;
+	int split_order;
+
+	/* Frozen implies unmapped, callers unmap before splitting. */
+	VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
+
+	/*
+	 * split to new_order one order at a time. For uniform split,
+	 * folio is split to new_order directly.
+	 */
+	for (split_order = start_order;
+	     split_order >= new_order;
+	     split_order--) {
+		int nr_new_folios = 1UL << (old_order - split_order);
+
+		/* order-1 anonymous folio is not supported */
+		if (is_anon && split_order == 1)
+			continue;
+
+		if (xas) {
+			/*
+			 * uniform split has xas_split_alloc() called before
+			 * irq is disabled to allocate enough memory, whereas
+			 * non-uniform split can handle ENOMEM.
+			 * Use the to-be-split folio, so that a parallel
+			 * folio_try_get() waits on it until xarray is updated
+			 * with after-split folios and the original one is
+			 * unfrozen.
+			 */
+			if (split_type == SPLIT_TYPE_UNIFORM) {
+				xas_split(xas, old_folio, old_order);
+			} else {
+				xas_set_order(xas, folio->index, split_order);
+				xas_try_split(xas, old_folio, old_order);
+				if (xas_error(xas))
+					return xas_error(xas);
+			}
+		}
+
+		folio_split_memcg_refs(folio, old_order, split_order);
+		split_page_owner(&folio->page, old_order, split_order);
+		pgalloc_tag_split(folio, old_order, split_order);
+		__split_folio_to_order(folio, old_order, split_order);
+
+		if (is_anon) {
+			mod_mthp_stat(old_order, MTHP_STAT_NR_ANON, -1);
+			mod_mthp_stat(split_order, MTHP_STAT_NR_ANON, nr_new_folios);
+		}
+		/*
+		 * If uniform split, the process is complete.
+		 * If non-uniform, continue splitting the folio at @split_at
+		 * as long as the next @split_order is >= @new_order.
+		 */
+		folio = page_folio(split_at);
+		old_order = split_order;
+	}
+
+	return 0;
+}
+
+/**
+ * folio_check_splittable() - check if a folio can be split to a given order
+ * @folio: folio to be split
+ * @new_order: the smallest order of the after split folios (since buddy
+ *             allocator like split generates folios with orders from @folio's
+ *             order - 1 to new_order).
+ * @split_type: uniform or non-uniform split
+ *
+ * folio_check_splittable() checks if @folio can be split to @new_order using
+ * @split_type method. The truncated folio check must come first.
+ *
+ * Context: folio must be locked.
+ *
+ * Return: 0 - @folio can be split to @new_order, otherwise an error number is
+ * returned.
+ */
+int folio_check_splittable(struct folio *folio, unsigned int new_order,
+			   enum split_type split_type)
+{
+	const bool is_anon = folio_test_anon(folio);
+	const bool is_swapcache = folio_test_swapcache(folio);
+
+	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).
+	 */
+	if (!folio->mapping && !is_anon)
+		return -EBUSY;
+
+	/* order-1 is not supported for anonymous THP. */
+	if (is_anon && new_order == 1)
+		return -EINVAL;
+
+	/*
+	 * swapcache folio could only be split to order 0
+	 *
+	 * non-uniform split creates after-split folios with orders from
+	 * folio_order(folio) - 1 to new_order, making it not suitable for any
+	 * swapcache folio split. Only uniform split to order-0 can be used
+	 * here.
+	 */
+	if ((split_type == SPLIT_TYPE_NON_UNIFORM || new_order) && is_swapcache)
+		return -EINVAL;
+
+	if (is_huge_zero_folio(folio))
+		return -EINVAL;
+
+	if (folio_test_writeback(folio))
+		return -EBUSY;
+
+	/*
+	 * A non-anon swapcache folio that still has a mapping can only be a
+	 * shmem folio under SWAP IO, it's removed from either swap cache or
+	 * shmem mapping afterward. There is little benefit in splitting them
+	 * hence reject it here up front before touching anything.
+	 */
+	if (!is_anon && is_swapcache && folio->mapping)
+		return -EBUSY;
+
+	return 0;
+}
+
+/* Number of folio references from the swapcache. */
+static unsigned int folio_swapcache_ref_count(const struct folio *folio)
+{
+	if (!folio_test_swapcache(folio))
+		return 0;
+	return folio_nr_pages(folio);
+}
+
+void folio_reset_partially_mapped(struct folio *folio)
+{
+	/* Folio must be frozen. */
+	VM_WARN_ON_FOLIO(folio_ref_count(folio), folio);
+
+	if (!folio_test_partially_mapped(folio))
+		return;
+
+	/*
+	 * Order-1 folios have no _deferred_list. The flag is only ever set
+	 * on folios that do, so the list can be checked after the flag.
+	 */
+	VM_WARN_ON_FOLIO(!list_empty(&folio->_deferred_list), folio);
+
+	folio_clear_partially_mapped(folio);
+	mod_mthp_stat(folio_order(folio),
+		      MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
+}
+
+static int __folio_freeze_split_anon(struct folio *folio,
+		unsigned int new_order, struct page *split_at, bool do_lru,
+		struct list_head *list, enum split_type split_type)
+{
+	struct folio *end_folio = folio_next(folio);
+	struct swap_cluster_info *ci = NULL;
+	const int old_order = folio_order(folio);
+	struct folio *new_folio, *next;
+	struct anon_vma *anon_vma = NULL;
+	enum ttu_flags ttu_flags = 0;
+	struct lruvec *lruvec;
+	int ret = 0;
+
+	/*
+	 * Unmap/remap needs the anon_vma, so we first take a reference on
+	 * it to prevent it from disappearing, and lock it for write here,
+	 * letting unmap_folio() walk the rmap with TTU_RMAP_LOCKED.
+	 *
+	 * folio_mapped() is not stable here, but it can only change in
+	 * one direction while the folio is locked. The mapcount can drop
+	 * to zero at any time, zap_pte_range() takes no folio lock. It
+	 * cannot go up: swapin, migration and uffd move all lock the folio
+	 * before mapping it, and fork only copies PTEs that already exist.
+	 *
+	 * So if we see the folio mapped, the worst case is an empty rmap
+	 * walk. If we see it unmapped, it stays unmapped and needs neither
+	 * the reference nor the lock. Anything else needs a reference
+	 * first and folio_ref_freeze() below catches it.
+	 *
+	 * Note that entirely swapped-out THPs are unmapped but can be split.
+	 */
+	if (folio_mapped(folio)) {
+		anon_vma = folio_get_anon_vma(folio);
+		if (!anon_vma)
+			return -EBUSY;
+		anon_vma_lock_write(anon_vma);
+		ret = unmap_folio(folio);
+		if (ret)
+			goto out_unlock;
+	}
+
+	local_irq_disable();
+
+	if (!folio_ref_freeze(folio, folio_swapcache_ref_count(folio) + 1)) {
+		ret = -EAGAIN;
+		goto out_no_split;
+	}
+
+	/* Take off the deferred split queue while frozen and memcg set */
+	folio_unqueue_deferred_split(folio);
+
+	/*
+	 * deferred_split_scan() takes the folio off the queue before it
+	 * splits it, so the unqueue above finds an empty list and
+	 * leaves PG_partially_mapped set.
+	 * Clear it here: the flag does not survive the split.
+	 */
+	folio_reset_partially_mapped(folio);
+
+	if (folio_test_swapcache(folio))
+		ci = swap_cluster_get_and_lock(folio);
+
+	if (do_lru)
+		lruvec = folio_lruvec_lock(folio);
+
+	ret = __split_frozen_folio(folio, new_order, split_at, NULL, split_type);
+
+	/*
+	 * Unfreeze the after-split folios and put them back to the right
+	 * place. Keep the head @folio frozen until the end: sub entries
+	 * in swap cache must be updated first, so a concurrent
+	 * swap_cache_get_folio() cannot return the head folio for a sub
+	 * entry (folio_try_get() will fail on the head @folio until unfreeze).
+	 */
+	for (new_folio = folio_next(folio); new_folio != end_folio;
+	     new_folio = next) {
+		next = folio_next(new_folio);
+		zone_device_private_split_cb(folio, new_folio);
+		folio_ref_unfreeze(new_folio,
+				   folio_swapcache_ref_count(new_folio) + 1);
+		if (do_lru)
+			lru_add_split_folio(folio, new_folio, lruvec, list);
+		if (ci)
+			__swap_cache_replace_folio(ci, folio, new_folio);
+	}
+
+	zone_device_private_split_cb(folio, NULL);
+	folio_ref_unfreeze(folio, folio_swapcache_ref_count(folio) + 1);
+
+	if (do_lru)
+		lruvec_unlock(lruvec);
+	if (ci)
+		swap_cluster_unlock(ci);
+out_no_split:
+	local_irq_enable();
+	if (anon_vma) {
+		if (!ret && !folio_is_device_private(folio))
+			ttu_flags = TTU_USE_SHARED_ZEROPAGE;
+		remap_anon_folio(folio, 1 << old_order, ttu_flags);
+	}
+out_unlock:
+	if (anon_vma) {
+		anon_vma_unlock_write(anon_vma);
+		put_anon_vma(anon_vma);
+	}
+
+	return ret;
+}
+
+static int __folio_freeze_split_file(struct folio *folio,
+		unsigned int new_order, struct page *split_at,
+		struct list_head *list, enum split_type split_type)
+{
+	const long old_nr_pages = folio_nr_pages(folio);
+	struct address_space *mapping = folio->mapping;
+	XA_STATE(xas, &mapping->i_pages, folio->index);
+	struct folio *end_folio = folio_next(folio);
+	struct mem_cgroup *memcg, *old_memcg;
+	struct folio *new_folio, *next;
+	int nr_shmem_dropped = 0;
+	unsigned int min_order;
+	struct lruvec *lruvec;
+	pgoff_t end;
+	gfp_t gfp;
+	int ret = 0;
+
+	min_order = mapping_min_folio_order(mapping);
+	if (new_order < min_order)
+		return -EINVAL;
+
+	/*
+	 * Switch to folio's memcg as xarray node allocation can happen and
+	 * needs to charge to it.
+	 */
+	memcg = get_mem_cgroup_from_folio(folio);
+	old_memcg = set_active_memcg(memcg);
+
+	gfp = current_gfp_context(mapping_gfp_mask(mapping) & GFP_RECLAIM_MASK);
+	if (!filemap_release_folio(folio, gfp)) {
+		ret = -EBUSY;
+		goto fail_free;
+	}
+
+	mapping_set_update(&xas, mapping);
+
+	if (split_type == SPLIT_TYPE_UNIFORM) {
+		const int old_order = folio_order(folio);
+
+		xas_set_order(&xas, folio->index, new_order);
+		xas_split_alloc(&xas, folio, old_order, gfp);
+		if (xas_error(&xas)) {
+			ret = xas_error(&xas);
+			goto fail_free;
+		}
+	}
+
+	i_mmap_lock_read(mapping);
+
+	/* Currently device private folios can only back anonymous memory. */
+	VM_WARN_ON_ONCE_FOLIO(folio_is_device_private(folio), folio);
+
+	/*
+	 * The loop below may need to trim off pages beyond
+	 * EOF: but on 32-bit, i_size_read() takes an irq-unsafe
+	 * seqlock, which cannot be nested inside the page tree lock.
+	 * So note end now: i_size itself may be changed at any moment,
+	 * but folio lock is good enough to serialize the trimming.
+	 */
+	end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
+	if (shmem_mapping(mapping))
+		end = shmem_fallocend(mapping->host, end);
+
+	ret = unmap_folio(folio);
+	if (ret)
+		goto fail_mmap_unlock;
+
+	xas_lock_irq(&xas);
+
+	/*
+	 * Check if the folio is present in page cache.
+	 * We assume all tail are present too, if folio is there.
+	 */
+	if (xas_load(&xas) != folio) {
+		ret = -EAGAIN;
+		goto fail;
+	}
+
+	if (!folio_ref_freeze(folio, old_nr_pages + 1)) {
+		ret = -EAGAIN;
+		goto fail;
+	}
+
+	if (folio_test_pmd_mappable(folio) && new_order < HPAGE_PMD_ORDER) {
+		if (folio_test_swapbacked(folio))
+			lruvec_stat_mod_folio(folio, NR_SHMEM_THPS, -old_nr_pages);
+		else
+			lruvec_stat_mod_folio(folio, NR_FILE_THPS, -old_nr_pages);
+	}
+
+	/* lock lru list/PageCompound, ref frozen by page_ref_freeze */
+	lruvec = folio_lruvec_lock(folio);
+	ret = __split_frozen_folio(folio, new_order, split_at, &xas, split_type);
+
+	/*
+	 * Unfreeze after-split folios and put them back to the right
+	 * list. @folio should be kept frozen until page cache
+	 * entries are updated with all the other after-split folios
+	 * to prevent others seeing stale page cache entries.
+	 * As a result, new_folio starts from the next folio of
+	 * @folio.
+	 */
+	for (new_folio = folio_next(folio); new_folio != end_folio;
+	     new_folio = next) {
+		unsigned long nr_pages = folio_nr_pages(new_folio);
+
+		/* compute next before the folio can be freed below */
+		next = folio_next(new_folio);
+
+		folio_ref_unfreeze(new_folio,
+				   folio_nr_pages(new_folio) + 1);
+
+		lru_add_split_folio(folio, new_folio, lruvec, list);
+
+		/* Add the new folio to the page cache. */
+		if (new_folio->index < end) {
+			__xa_store(&mapping->i_pages, new_folio->index,
+				   new_folio, 0);
+			continue;
+		}
+
+		/* Drop folio beyond EOF: ->index >= end */
+		if (shmem_mapping(mapping))
+			nr_shmem_dropped += nr_pages;
+		else if (folio_test_clear_dirty(new_folio))
+			folio_account_cleaned(
+				new_folio, inode_to_wb(mapping->host));
+		__filemap_remove_folio(new_folio, NULL);
+		folio_put_refs(new_folio, nr_pages);
+	}
+
+	/*
+	 * Unfreeze @folio only after all page cache entries, which
+	 * used to point to it, have been updated with new folios.
+	 * Otherwise, a parallel folio_try_get() can grab @folio
+	 * and its caller can see stale page cache entries.
+	 */
+	folio_ref_unfreeze(folio, folio_nr_pages(folio) + 1);
+	lruvec_unlock(lruvec);
+fail:
+	xas_unlock_irq(&xas);
+fail_mmap_unlock:
+	if (nr_shmem_dropped)
+		shmem_uncharge(mapping->host, nr_shmem_dropped);
+	/*
+	 * Drop the mapping while the inode is still pinned. @folio stays
+	 * locked and present in the page cache, so eviction cannot free
+	 * the inode yet, nothing past this point may touch the inode or
+	 * the mapping.
+	 */
+	i_mmap_unlock_read(mapping);
+fail_free:
+	/* Restore the previously active memcg */
+	set_active_memcg(old_memcg);
+	mem_cgroup_put(memcg);
+	xas_destroy(&xas);
+	return ret;
+}
+
+/**
+ * __folio_split() - split a folio at @split_at to a @new_order folio
+ * @folio: folio to split
+ * @new_order: the order of the new folio
+ * @split_at: a page within the new folio
+ * @lock_at: a page within @folio to be left locked to caller
+ * @list: after-split folios will be put on it if non NULL
+ * @split_type: perform uniform split or not (non-uniform split)
+ *
+ * It calls __split_frozen_folio() to perform uniform and non-uniform split.
+ * It is in charge of checking whether the split is supported or not and
+ * preparing @folio for __split_frozen_folio().
+ *
+ * After splitting, the after-split folio containing @lock_at remains locked
+ * and others are unlocked:
+ * 1. for uniform split, @lock_at points to one of @folio's subpages;
+ * 2. for buddy allocator like (non-uniform) split, @lock_at points to @folio.
+ *
+ * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
+ * split but not to @new_order, the caller needs to check)
+ */
+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 folio *end_folio = folio_next(folio);
+	const bool is_anon = folio_test_anon(folio);
+	const bool is_swapcache = folio_test_swapcache(folio);
+	int old_order = folio_order(folio);
+	struct folio *new_folio, *next;
+	int ret;
+
+	VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+	VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio);
+
+	if (folio != page_folio(split_at) || folio != page_folio(lock_at)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (new_order >= old_order) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = folio_check_splittable(folio, new_order, split_type);
+	if (ret) {
+		VM_WARN_ONCE(ret == -EINVAL, "Tried to split an unsplittable folio");
+		goto out;
+	}
+
+	if (is_anon)
+		ret = __folio_freeze_split_anon(folio, new_order, split_at,
+						true, list, split_type);
+	else
+		ret = __folio_freeze_split_file(folio, new_order, split_at,
+						list, split_type);
+
+	/*
+	 * Unlock all after-split folios except the one containing
+	 * @lock_at page. If @folio is not split, it will be kept locked.
+	 */
+	for (new_folio = folio; new_folio != end_folio; new_folio = next) {
+		next = folio_next(new_folio);
+		if (new_folio == page_folio(lock_at))
+			continue;
+
+		/*
+		 * Subpages whose mapping has been zapped may be freed
+		 * earlier, but freeing them requires taking the
+		 * lru_lock, so we defer folio_put() on tail pages until
+		 * after the split completes.
+		 */
+		if (is_swapcache && !folio_mapped(new_folio))
+			folio_free_swap(new_folio);
+		folio_unlock(new_folio);
+		folio_put(new_folio);
+	}
+
+out:
+	if (is_pmd_order(old_order))
+		count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
+	count_mthp_stat(old_order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
+	return ret;
+}
+
+/**
+ * folio_split_unmapped() - split a large anon folio that is already unmapped
+ * @folio: folio to split
+ * @new_order: the order of folios after split
+ *
+ * This function is a helper for splitting folios that have already been
+ * unmapped. The use case is that the device or the CPU can refuse to migrate
+ * THP pages in the middle of migration, due to allocation issues on either
+ * side.
+ *
+ * The anon rmap lock is not required to be held, mmap_read_lock() or
+ * mmap_write_lock() should be held. @folio is expected to be locked by the
+ * caller. device-private and non device-private folios are supported along
+ * with folios that are in the swapcache. @folio should also be unmapped and
+ * isolated from LRU (if applicable)
+ *
+ * Upon return, the folio is not remapped, split folios are not added to LRU,
+ * folio_free_swap() is not called, and new folios remain locked.
+ *
+ * Return: 0 on success, -EAGAIN if the folio cannot be split (e.g., due to
+ *         insufficient reference count or extra pins).
+ */
+int folio_split_unmapped(struct folio *folio, unsigned int new_order)
+{
+	VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
+	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(!folio_test_anon(folio), folio);
+
+	return __folio_freeze_split_anon(folio, new_order, &folio->page,
+					 false, NULL, SPLIT_TYPE_UNIFORM);
+}
+
+/*
+ * This function splits a large folio into smaller folios of order @new_order.
+ * @page can point to any page of the large folio to split. The split operation
+ * does not change the position of @page.
+ *
+ * Prerequisites:
+ *
+ * 1) The caller must hold a reference on the @page's owning folio, also known
+ *    as the large folio.
+ *
+ * 2) The large folio must be locked.
+ *
+ * 3) The folio must not be pinned. Any unexpected folio references, including
+ *    GUP pins, will result in the folio not getting split; instead, the caller
+ *    will receive an -EAGAIN.
+ *
+ * 4) @new_order > 1, usually. Splitting to order-1 anonymous folios is not
+ *    supported for non-file-backed folios, because folio->_deferred_list, which
+ *    is used by partially mapped folios, is stored in subpage 2, but an order-1
+ *    folio only has subpages 0 and 1. File-backed order-1 folios are supported,
+ *    since they do not use _deferred_list.
+ *
+ * After splitting, the caller's folio reference will be transferred to @page,
+ * resulting in a raised refcount of @page after this call. The other pages may
+ * be freed if they are not mapped.
+ *
+ * If @list is null, tail pages will be added to LRU list, otherwise, to @list.
+ *
+ * Pages in @new_order will inherit the mapping, flags, and so on from the
+ * huge page.
+ *
+ * Returns 0 if the huge page was split successfully.
+ *
+ * Returns -EAGAIN if the folio has unexpected reference (e.g., GUP) or if
+ * the folio was concurrently removed from the page cache.
+ *
+ * Returns -EBUSY when trying to split the huge zeropage, if the folio is
+ * under writeback, if fs-specific folio metadata cannot currently be
+ * released, or if some unexpected race happened (e.g., anon VMA disappeared,
+ * truncation).
+ *
+ * Callers should ensure that the order respects the address space mapping
+ * min-order if one is set for non-anonymous folios.
+ *
+ * Returns -EINVAL when trying to split to an order that is incompatible
+ * with the folio. Splitting to order 0 is compatible with all folios.
+ */
+int __split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
+				     unsigned int new_order)
+{
+	struct folio *folio = page_folio(page);
+
+	return __folio_split(folio, new_order, &folio->page, page, list,
+			     SPLIT_TYPE_UNIFORM);
+}
+
+/**
+ * folio_split() - split a folio at @split_at to a @new_order folio
+ * @folio: folio to split
+ * @new_order: the order of the new folio
+ * @split_at: a page within the new folio
+ * @list: after-split folios are added to @list if not null, otherwise to LRU
+ *        list
+ *
+ * It has the same prerequisites and returns as
+ * split_huge_page_to_list_to_order().
+ *
+ * Split a folio at @split_at to a new_order folio, leave the
+ * remaining subpages of the original folio as large as possible. For example,
+ * in the case of splitting an order-9 folio at its third order-3 subpages to
+ * an order-3 folio, there are 2^(9-3)=64 order-3 subpages in the order-9 folio.
+ * After the split, there will be a group of folios with different orders and
+ * the new folio containing @split_at is marked in bracket:
+ * [order-4, {order-3}, order-3, order-5, order-6, order-7, order-8].
+ *
+ * After split, folio is left locked for caller.
+ *
+ * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
+ * split but not to @new_order, the caller needs to check)
+ */
+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);
+}
+
+/**
+ * min_order_for_split() - get the minimum order @folio can be split to
+ * @folio: folio to split
+ *
+ * min_order_for_split() tells the minimum order @folio can be split to.
+ * If a file-backed folio is truncated, 0 will be returned. Any subsequent
+ * split attempt should get -EBUSY from split checking code.
+ *
+ * Context: @folio must be locked.
+ *
+ * Return: @folio's minimum order for split
+ */
+unsigned int min_order_for_split(struct folio *folio)
+{
+	VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
+	if (folio_test_anon(folio))
+		return 0;
+
+	/*
+	 * If the folio got truncated, we don't know the previous mapping and
+	 * consequently the old min order. But it doesn't matter, as any split
+	 * attempt will immediately fail with -EBUSY as the folio cannot get
+	 * split until freed.
+	 */
+	if (!folio->mapping)
+		return 0;
+
+	return mapping_min_folio_order(folio->mapping);
+}
+
+int split_folio_to_list(struct folio *folio, struct list_head *list)
+{
+	return split_huge_page_to_list_to_order(&folio->page, list, 0);
+}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 70ad60936..5ef28984b 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3582,39 +3582,6 @@ void vma_adjust_trans_huge(struct vm_area_struct *vma,
 		split_huge_leaf(next, end);
 }
 
-/*
- * A return value of 0 does not mean that unmapping succeeded. It might
- * still have failed, but remap_anon_folio() must be called afterwards,
- * for anon folios.
- */
-static int unmap_folio(struct folio *folio)
-{
-	enum ttu_flags ttu_flags = TTU_RMAP_LOCKED | TTU_SYNC |
-		TTU_BATCH_FLUSH;
-
-	VM_BUG_ON_FOLIO(!folio_test_large(folio), folio);
-
-	/* Racy check if we can split the page, before we split PMDs */
-	if (folio_expected_ref_count(folio) != folio_ref_count(folio) - 1)
-		return -EAGAIN;
-
-	if (folio_test_pmd_mappable(folio))
-		ttu_flags |= TTU_SPLIT_HUGE_PMD;
-
-	/*
-	 * Anon pages need migration entries to preserve them, but file
-	 * pages can simply be left unmapped, then faulted back on demand.
-	 */
-	if (folio_test_anon(folio))
-		try_to_migrate(folio, ttu_flags);
-	else
-		try_to_unmap(folio, ttu_flags | TTU_IGNORE_MLOCK);
-
-	try_to_unmap_flush();
-
-	return 0;
-}
-
 static bool __discard_anon_folio_pmd_locked(struct vm_area_struct *vma,
 					    unsigned long addr, pmd_t *pmdp,
 					    struct folio *folio)
@@ -3692,893 +3659,6 @@ bool unmap_huge_pmd_locked(struct vm_area_struct *vma, unsigned long addr,
 	return __discard_anon_folio_pmd_locked(vma, addr, pmdp, folio);
 }
 
-static void remap_anon_folio(struct folio *folio, unsigned long nr, int flags)
-{
-	int i = 0;
-
-	/*
-	 * unmap_folio() installs migration entries only for anon folios,
-	 * so currently only anon folios need to be remapped. File folios
-	 * stay unmapped after the split and are faulted back on demand.
-	 */
-	VM_WARN_ON_FOLIO(!folio_test_anon(folio), folio);
-
-	for (;;) {
-		remove_migration_ptes(folio, folio, TTU_RMAP_LOCKED | flags);
-		i += folio_nr_pages(folio);
-		if (i >= nr)
-			break;
-		folio = folio_next(folio);
-	}
-}
-
-static void lru_add_split_folio(struct folio *folio, struct folio *new_folio,
-		struct lruvec *lruvec, struct list_head *list)
-{
-	VM_BUG_ON_FOLIO(folio_test_lru(new_folio), folio);
-	lockdep_assert_held(&lruvec->lru_lock);
-
-	if (folio_is_device_private(folio))
-		return;
-
-	if (list) {
-		/* page reclaim is reclaiming a huge page */
-		VM_WARN_ON(folio_test_lru(folio));
-		folio_get(new_folio);
-		list_add_tail(&new_folio->lru, list);
-	} else {
-		/* head is still on lru (and we have it frozen) */
-		VM_WARN_ON(!folio_test_lru(folio));
-		if (folio_test_unevictable(folio))
-			new_folio->mlock_count = 0;
-		else
-			list_add_tail(&new_folio->lru, &folio->lru);
-		folio_set_lru(new_folio);
-	}
-}
-
-static bool page_range_has_hwpoisoned(struct page *page, long nr_pages)
-{
-	for (; nr_pages; page++, nr_pages--)
-		if (PageHWPoison(page))
-			return true;
-	return false;
-}
-
-/*
- * It splits @folio into @new_order folios and copies the @folio metadata to
- * all the resulting folios.
- */
-static void __split_folio_to_order(struct folio *folio, int old_order,
-		int new_order)
-{
-	/* Scan poisoned pages when split a poisoned folio to large folios */
-	const bool handle_hwpoison = folio_test_has_hwpoisoned(folio) && new_order;
-	long new_nr_pages = 1 << new_order;
-	long nr_pages = 1 << old_order;
-	long i;
-
-	folio_clear_has_hwpoisoned(folio);
-
-	/* Check first new_nr_pages since the loop below skips them */
-	if (handle_hwpoison &&
-	    page_range_has_hwpoisoned(folio_page(folio, 0), new_nr_pages))
-		folio_set_has_hwpoisoned(folio);
-	/*
-	 * Skip the first new_nr_pages, since the new folio from them have all
-	 * the flags from the original folio.
-	 */
-	for (i = new_nr_pages; i < nr_pages; i += new_nr_pages) {
-		struct page *new_head = &folio->page + i;
-		/*
-		 * Careful: new_folio is not a "real" folio before we cleared PageTail.
-		 * Don't pass it around before clear_compound_head().
-		 */
-		struct folio *new_folio = (struct folio *)new_head;
-
-		VM_BUG_ON_PAGE(atomic_read(&new_folio->_mapcount) != -1, new_head);
-
-		/*
-		 * Clone page flags before unfreezing refcount.
-		 *
-		 * After successful get_page_unless_zero() might follow flags change,
-		 * for example lock_page() which set PG_waiters.
-		 *
-		 * Note that for mapped sub-pages of an anonymous THP,
-		 * PG_anon_exclusive has been cleared in unmap_folio() and is stored in
-		 * the migration entry instead from where remap_anon_folio() will restore it.
-		 * We can still have PG_anon_exclusive set on effectively unmapped and
-		 * unreferenced sub-pages of an anonymous THP: we can simply drop
-		 * PG_anon_exclusive (-> PG_mappedtodisk) for these here.
-		 */
-		new_folio->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
-		new_folio->flags.f |= (folio->flags.f &
-				((1L << PG_referenced) |
-				 (1L << PG_swapbacked) |
-				 (1L << PG_swapcache) |
-				 (1L << PG_mlocked) |
-				 (1L << PG_uptodate) |
-				 (1L << PG_active) |
-				 (1L << PG_workingset) |
-				 (1L << PG_locked) |
-				 (1L << PG_unevictable) |
-#ifdef CONFIG_ARCH_USES_PG_ARCH_2
-				 (1L << PG_arch_2) |
-#endif
-#ifdef CONFIG_ARCH_USES_PG_ARCH_3
-				 (1L << PG_arch_3) |
-#endif
-				 (1L << PG_dirty) |
-				 (1L << PG_dropbehind) |
-				 LRU_GEN_MASK | LRU_REFS_MASK));
-
-		new_folio->mapping = folio->mapping;
-		new_folio->index = folio->index + i;
-
-		/*
-		 * page->private should not be set in tail pages. Warn once
-		 * if private is unexpectedly set. Do it before swap.val assignment
-		 * since private overlaps with swap.val.
-		 */
-		VM_WARN_ON_ONCE_PAGE(new_folio->private, new_head);
-
-		/*
-		 * Not all folio fields are valid during a split, so open-code
-		 * the swap entry rather than using folio_swap_entry().
-		 */
-		if (folio_test_swapcache(folio))
-			new_folio->swap.val = folio->swap.val + i;
-
-		/* Page flags must be visible before we make the page non-compound. */
-		smp_wmb();
-
-		/*
-		 * Clear PageTail before unfreezing page refcount.
-		 *
-		 * After successful get_page_unless_zero() might follow put_page()
-		 * which needs correct compound_head().
-		 */
-		clear_compound_head(new_head);
-		if (new_order) {
-			prep_compound_page(new_head, new_order);
-			folio_set_large_rmappable(new_folio);
-		}
-
-		/*
-		 * PG_has_hwpoisoned is on the 2nd page, so set it after
-		 * the compound head is prepped.
-		 */
-		if (handle_hwpoison &&
-		    page_range_has_hwpoisoned(new_head, new_nr_pages))
-			folio_set_has_hwpoisoned(new_folio);
-
-		if (folio_test_young(folio))
-			folio_set_young(new_folio);
-		if (folio_test_idle(folio))
-			folio_set_idle(new_folio);
-#ifdef CONFIG_MEMCG
-		new_folio->memcg_data = folio->memcg_data;
-#endif
-
-		folio_xchg_last_cpupid(new_folio, folio_last_cpupid(folio));
-	}
-
-	if (new_order)
-		folio_set_order(folio, new_order);
-	else
-		ClearPageCompound(&folio->page);
-}
-
-/**
- * __split_frozen_folio() - splits a frozen @folio to lower order folios
- * in two ways: uniform split or non-uniform split.
- * @folio: the to-be-split folio
- * @new_order: the smallest order of the after split folios (since buddy
- *             allocator like split generates folios with orders from @folio's
- *             order - 1 to new_order).
- * @split_at: in buddy allocator like split, the folio containing @split_at
- *            will be split until its order becomes @new_order.
- * @xas: xa_state pointing to folio->mapping->i_pages and locked by caller
- * @split_type: if the split is uniform or not (buddy allocator like split)
- *
- *
- * 1. uniform split: the given @folio into multiple @new_order small folios,
- *    where all small folios have the same order. This is done when
- *    split_type is SPLIT_TYPE_UNIFORM.
- * 2. buddy allocator like (non-uniform) split: the given @folio is split into
- *    half and one of the half (containing the given page) is split into half
- *    until the given @folio's order becomes @new_order. This is done when
- *    split_type is SPLIT_TYPE_NON_UNIFORM.
- *
- * The high level flow for these two methods are:
- *
- * 1. uniform split: @xas is split with no expectation of failure and a single
- *    __split_folio_to_order() is called to split the @folio into @new_order
- *    along with stats update.
- * 2. non-uniform split: folio_order - @new_order calls to
- *    __split_folio_to_order() are expected to be made in a for loop to split
- *    the @folio to one lower order at a time. The folio containing @split_at
- *    is split in each iteration. @xas is split into half in each iteration and
- *    can fail. A failed @xas split leaves split folios as is without merging
- *    them back.
- *
- * After splitting, the caller's folio reference will be transferred to the
- * folio containing @split_at. The caller needs to unlock and/or free
- * after-split folios if necessary.
- *
- * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
- * split but not to @new_order, the caller needs to check)
- */
-static int __split_frozen_folio(struct folio *folio, int new_order,
-		struct page *split_at, struct xa_state *xas,
-		enum split_type split_type)
-{
-	const bool is_anon = folio_test_anon(folio);
-	int old_order = folio_order(folio);
-	int start_order = split_type == SPLIT_TYPE_UNIFORM ? new_order : old_order - 1;
-	struct folio *old_folio = folio;
-	int split_order;
-
-	/* Frozen implies unmapped, callers unmap before splitting. */
-	VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
-
-	/*
-	 * split to new_order one order at a time. For uniform split,
-	 * folio is split to new_order directly.
-	 */
-	for (split_order = start_order;
-	     split_order >= new_order;
-	     split_order--) {
-		int nr_new_folios = 1UL << (old_order - split_order);
-
-		/* order-1 anonymous folio is not supported */
-		if (is_anon && split_order == 1)
-			continue;
-
-		if (xas) {
-			/*
-			 * uniform split has xas_split_alloc() called before
-			 * irq is disabled to allocate enough memory, whereas
-			 * non-uniform split can handle ENOMEM.
-			 * Use the to-be-split folio, so that a parallel
-			 * folio_try_get() waits on it until xarray is updated
-			 * with after-split folios and the original one is
-			 * unfrozen.
-			 */
-			if (split_type == SPLIT_TYPE_UNIFORM) {
-				xas_split(xas, old_folio, old_order);
-			} else {
-				xas_set_order(xas, folio->index, split_order);
-				xas_try_split(xas, old_folio, old_order);
-				if (xas_error(xas))
-					return xas_error(xas);
-			}
-		}
-
-		folio_split_memcg_refs(folio, old_order, split_order);
-		split_page_owner(&folio->page, old_order, split_order);
-		pgalloc_tag_split(folio, old_order, split_order);
-		__split_folio_to_order(folio, old_order, split_order);
-
-		if (is_anon) {
-			mod_mthp_stat(old_order, MTHP_STAT_NR_ANON, -1);
-			mod_mthp_stat(split_order, MTHP_STAT_NR_ANON, nr_new_folios);
-		}
-		/*
-		 * If uniform split, the process is complete.
-		 * If non-uniform, continue splitting the folio at @split_at
-		 * as long as the next @split_order is >= @new_order.
-		 */
-		folio = page_folio(split_at);
-		old_order = split_order;
-	}
-
-	return 0;
-}
-
-/**
- * folio_check_splittable() - check if a folio can be split to a given order
- * @folio: folio to be split
- * @new_order: the smallest order of the after split folios (since buddy
- *             allocator like split generates folios with orders from @folio's
- *             order - 1 to new_order).
- * @split_type: uniform or non-uniform split
- *
- * folio_check_splittable() checks if @folio can be split to @new_order using
- * @split_type method. The truncated folio check must come first.
- *
- * Context: folio must be locked.
- *
- * Return: 0 - @folio can be split to @new_order, otherwise an error number is
- * returned.
- */
-int folio_check_splittable(struct folio *folio, unsigned int new_order,
-			   enum split_type split_type)
-{
-	const bool is_anon = folio_test_anon(folio);
-	const bool is_swapcache = folio_test_swapcache(folio);
-
-	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).
-	 */
-	if (!folio->mapping && !is_anon)
-		return -EBUSY;
-
-	/* order-1 is not supported for anonymous THP. */
-	if (is_anon && new_order == 1)
-		return -EINVAL;
-
-	/*
-	 * swapcache folio could only be split to order 0
-	 *
-	 * non-uniform split creates after-split folios with orders from
-	 * folio_order(folio) - 1 to new_order, making it not suitable for any
-	 * swapcache folio split. Only uniform split to order-0 can be used
-	 * here.
-	 */
-	if ((split_type == SPLIT_TYPE_NON_UNIFORM || new_order) && is_swapcache)
-		return -EINVAL;
-
-	if (is_huge_zero_folio(folio))
-		return -EINVAL;
-
-	if (folio_test_writeback(folio))
-		return -EBUSY;
-
-	/*
-	 * A non-anon swapcache folio that still has a mapping can only be a
-	 * shmem folio under SWAP IO, it's removed from either swap cache or
-	 * shmem mapping afterward. There is little benefit in splitting them
-	 * hence reject it here up front before touching anything.
-	 */
-	if (!is_anon && is_swapcache && folio->mapping)
-		return -EBUSY;
-
-	return 0;
-}
-
-/* Number of folio references from the swapcache. */
-static unsigned int folio_swapcache_ref_count(const struct folio *folio)
-{
-	if (!folio_test_swapcache(folio))
-		return 0;
-	return folio_nr_pages(folio);
-}
-
-static void folio_reset_partially_mapped(struct folio *folio)
-{
-	/* Folio must be frozen. */
-	VM_WARN_ON_FOLIO(folio_ref_count(folio), folio);
-
-	if (!folio_test_partially_mapped(folio))
-		return;
-
-	/*
-	 * Order-1 folios have no _deferred_list. The flag is only ever set
-	 * on folios that do, so the list can be checked after the flag.
-	 */
-	VM_WARN_ON_FOLIO(!list_empty(&folio->_deferred_list), folio);
-
-	folio_clear_partially_mapped(folio);
-	mod_mthp_stat(folio_order(folio),
-		      MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
-}
-
-static int __folio_freeze_split_anon(struct folio *folio,
-		unsigned int new_order, struct page *split_at, bool do_lru,
-		struct list_head *list, enum split_type split_type)
-{
-	struct folio *end_folio = folio_next(folio);
-	struct swap_cluster_info *ci = NULL;
-	const int old_order = folio_order(folio);
-	struct folio *new_folio, *next;
-	struct anon_vma *anon_vma = NULL;
-	enum ttu_flags ttu_flags = 0;
-	struct lruvec *lruvec;
-	int ret = 0;
-
-	/*
-	 * Unmap/remap needs the anon_vma, so we first take a reference on
-	 * it to prevent it from disappearing, and lock it for write here,
-	 * letting unmap_folio() walk the rmap with TTU_RMAP_LOCKED.
-	 *
-	 * folio_mapped() is not stable here, but it can only change in
-	 * one direction while the folio is locked. The mapcount can drop
-	 * to zero at any time, zap_pte_range() takes no folio lock. It
-	 * cannot go up: swapin, migration and uffd move all lock the folio
-	 * before mapping it, and fork only copies PTEs that already exist.
-	 *
-	 * So if we see the folio mapped, the worst case is an empty rmap
-	 * walk. If we see it unmapped, it stays unmapped and needs neither
-	 * the reference nor the lock. Anything else needs a reference
-	 * first and folio_ref_freeze() below catches it.
-	 *
-	 * Note that entirely swapped-out THPs are unmapped but can be split.
-	 */
-	if (folio_mapped(folio)) {
-		anon_vma = folio_get_anon_vma(folio);
-		if (!anon_vma)
-			return -EBUSY;
-		anon_vma_lock_write(anon_vma);
-		ret = unmap_folio(folio);
-		if (ret)
-			goto out_unlock;
-	}
-
-	local_irq_disable();
-
-	if (!folio_ref_freeze(folio, folio_swapcache_ref_count(folio) + 1)) {
-		ret = -EAGAIN;
-		goto out_no_split;
-	}
-
-	/* Take off the deferred split queue while frozen and memcg set */
-	folio_unqueue_deferred_split(folio);
-
-	/*
-	 * deferred_split_scan() takes the folio off the queue before it
-	 * splits it, so the unqueue above finds an empty list and
-	 * leaves PG_partially_mapped set.
-	 * Clear it here: the flag does not survive the split.
-	 */
-	folio_reset_partially_mapped(folio);
-
-	if (folio_test_swapcache(folio))
-		ci = swap_cluster_get_and_lock(folio);
-
-	if (do_lru)
-		lruvec = folio_lruvec_lock(folio);
-
-	ret = __split_frozen_folio(folio, new_order, split_at, NULL, split_type);
-
-	/*
-	 * Unfreeze the after-split folios and put them back to the right
-	 * place. Keep the head @folio frozen until the end: sub entries
-	 * in swap cache must be updated first, so a concurrent
-	 * swap_cache_get_folio() cannot return the head folio for a sub
-	 * entry (folio_try_get() will fail on the head @folio until unfreeze).
-	 */
-	for (new_folio = folio_next(folio); new_folio != end_folio;
-	     new_folio = next) {
-		next = folio_next(new_folio);
-		zone_device_private_split_cb(folio, new_folio);
-		folio_ref_unfreeze(new_folio,
-				   folio_swapcache_ref_count(new_folio) + 1);
-		if (do_lru)
-			lru_add_split_folio(folio, new_folio, lruvec, list);
-		if (ci)
-			__swap_cache_replace_folio(ci, folio, new_folio);
-	}
-
-	zone_device_private_split_cb(folio, NULL);
-	folio_ref_unfreeze(folio, folio_swapcache_ref_count(folio) + 1);
-
-	if (do_lru)
-		lruvec_unlock(lruvec);
-	if (ci)
-		swap_cluster_unlock(ci);
-out_no_split:
-	local_irq_enable();
-	if (anon_vma) {
-		if (!ret && !folio_is_device_private(folio))
-			ttu_flags = TTU_USE_SHARED_ZEROPAGE;
-		remap_anon_folio(folio, 1 << old_order, ttu_flags);
-	}
-out_unlock:
-	if (anon_vma) {
-		anon_vma_unlock_write(anon_vma);
-		put_anon_vma(anon_vma);
-	}
-
-	return ret;
-}
-
-static int __folio_freeze_split_file(struct folio *folio,
-		unsigned int new_order, struct page *split_at,
-		struct list_head *list, enum split_type split_type)
-{
-	const long old_nr_pages = folio_nr_pages(folio);
-	struct address_space *mapping = folio->mapping;
-	XA_STATE(xas, &mapping->i_pages, folio->index);
-	struct folio *end_folio = folio_next(folio);
-	struct mem_cgroup *memcg, *old_memcg;
-	struct folio *new_folio, *next;
-	int nr_shmem_dropped = 0;
-	unsigned int min_order;
-	struct lruvec *lruvec;
-	pgoff_t end;
-	gfp_t gfp;
-	int ret = 0;
-
-	min_order = mapping_min_folio_order(mapping);
-	if (new_order < min_order)
-		return -EINVAL;
-
-	/*
-	 * Switch to folio's memcg as xarray node allocation can happen and
-	 * needs to charge to it.
-	 */
-	memcg = get_mem_cgroup_from_folio(folio);
-	old_memcg = set_active_memcg(memcg);
-
-	gfp = current_gfp_context(mapping_gfp_mask(mapping) & GFP_RECLAIM_MASK);
-	if (!filemap_release_folio(folio, gfp)) {
-		ret = -EBUSY;
-		goto fail_free;
-	}
-
-	mapping_set_update(&xas, mapping);
-
-	if (split_type == SPLIT_TYPE_UNIFORM) {
-		const int old_order = folio_order(folio);
-
-		xas_set_order(&xas, folio->index, new_order);
-		xas_split_alloc(&xas, folio, old_order, gfp);
-		if (xas_error(&xas)) {
-			ret = xas_error(&xas);
-			goto fail_free;
-		}
-	}
-
-	i_mmap_lock_read(mapping);
-
-	/* Currently device private folios can only back anonymous memory. */
-	VM_WARN_ON_ONCE_FOLIO(folio_is_device_private(folio), folio);
-
-	/*
-	 * The loop below may need to trim off pages beyond
-	 * EOF: but on 32-bit, i_size_read() takes an irq-unsafe
-	 * seqlock, which cannot be nested inside the page tree lock.
-	 * So note end now: i_size itself may be changed at any moment,
-	 * but folio lock is good enough to serialize the trimming.
-	 */
-	end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
-	if (shmem_mapping(mapping))
-		end = shmem_fallocend(mapping->host, end);
-
-	ret = unmap_folio(folio);
-	if (ret)
-		goto fail_mmap_unlock;
-
-	xas_lock_irq(&xas);
-
-	/*
-	 * Check if the folio is present in page cache.
-	 * We assume all tail are present too, if folio is there.
-	 */
-	if (xas_load(&xas) != folio) {
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	if (!folio_ref_freeze(folio, old_nr_pages + 1)) {
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	if (folio_test_pmd_mappable(folio) && new_order < HPAGE_PMD_ORDER) {
-		if (folio_test_swapbacked(folio))
-			lruvec_stat_mod_folio(folio, NR_SHMEM_THPS, -old_nr_pages);
-		else
-			lruvec_stat_mod_folio(folio, NR_FILE_THPS, -old_nr_pages);
-	}
-
-	/* lock lru list/PageCompound, ref frozen by page_ref_freeze */
-	lruvec = folio_lruvec_lock(folio);
-	ret = __split_frozen_folio(folio, new_order, split_at, &xas, split_type);
-
-	/*
-	 * Unfreeze after-split folios and put them back to the right
-	 * list. @folio should be kept frozen until page cache
-	 * entries are updated with all the other after-split folios
-	 * to prevent others seeing stale page cache entries.
-	 * As a result, new_folio starts from the next folio of
-	 * @folio.
-	 */
-	for (new_folio = folio_next(folio); new_folio != end_folio;
-	     new_folio = next) {
-		unsigned long nr_pages = folio_nr_pages(new_folio);
-
-		/* compute next before the folio can be freed below */
-		next = folio_next(new_folio);
-
-		folio_ref_unfreeze(new_folio,
-				   folio_nr_pages(new_folio) + 1);
-
-		lru_add_split_folio(folio, new_folio, lruvec, list);
-
-		/* Add the new folio to the page cache. */
-		if (new_folio->index < end) {
-			__xa_store(&mapping->i_pages, new_folio->index,
-				   new_folio, 0);
-			continue;
-		}
-
-		/* Drop folio beyond EOF: ->index >= end */
-		if (shmem_mapping(mapping))
-			nr_shmem_dropped += nr_pages;
-		else if (folio_test_clear_dirty(new_folio))
-			folio_account_cleaned(
-				new_folio, inode_to_wb(mapping->host));
-		__filemap_remove_folio(new_folio, NULL);
-		folio_put_refs(new_folio, nr_pages);
-	}
-
-	/*
-	 * Unfreeze @folio only after all page cache entries, which
-	 * used to point to it, have been updated with new folios.
-	 * Otherwise, a parallel folio_try_get() can grab @folio
-	 * and its caller can see stale page cache entries.
-	 */
-	folio_ref_unfreeze(folio, folio_nr_pages(folio) + 1);
-	lruvec_unlock(lruvec);
-fail:
-	xas_unlock_irq(&xas);
-fail_mmap_unlock:
-	if (nr_shmem_dropped)
-		shmem_uncharge(mapping->host, nr_shmem_dropped);
-	/*
-	 * Drop the mapping while the inode is still pinned. @folio stays
-	 * locked and present in the page cache, so eviction cannot free
-	 * the inode yet, nothing past this point may touch the inode or
-	 * the mapping.
-	 */
-	i_mmap_unlock_read(mapping);
-fail_free:
-	/* Restore the previously active memcg */
-	set_active_memcg(old_memcg);
-	mem_cgroup_put(memcg);
-	xas_destroy(&xas);
-	return ret;
-}
-
-/**
- * __folio_split() - split a folio at @split_at to a @new_order folio
- * @folio: folio to split
- * @new_order: the order of the new folio
- * @split_at: a page within the new folio
- * @lock_at: a page within @folio to be left locked to caller
- * @list: after-split folios will be put on it if non NULL
- * @split_type: perform uniform split or not (non-uniform split)
- *
- * It calls __split_frozen_folio() to perform uniform and non-uniform split.
- * It is in charge of checking whether the split is supported or not and
- * preparing @folio for __split_frozen_folio().
- *
- * After splitting, the after-split folio containing @lock_at remains locked
- * and others are unlocked:
- * 1. for uniform split, @lock_at points to one of @folio's subpages;
- * 2. for buddy allocator like (non-uniform) split, @lock_at points to @folio.
- *
- * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
- * split but not to @new_order, the caller needs to check)
- */
-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 folio *end_folio = folio_next(folio);
-	const bool is_anon = folio_test_anon(folio);
-	const bool is_swapcache = folio_test_swapcache(folio);
-	int old_order = folio_order(folio);
-	struct folio *new_folio, *next;
-	int ret;
-
-	VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
-	VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio);
-
-	if (folio != page_folio(split_at) || folio != page_folio(lock_at)) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (new_order >= old_order) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	ret = folio_check_splittable(folio, new_order, split_type);
-	if (ret) {
-		VM_WARN_ONCE(ret == -EINVAL, "Tried to split an unsplittable folio");
-		goto out;
-	}
-
-	if (is_anon)
-		ret = __folio_freeze_split_anon(folio, new_order, split_at,
-						true, list, split_type);
-	else
-		ret = __folio_freeze_split_file(folio, new_order, split_at,
-						list, split_type);
-
-	/*
-	 * Unlock all after-split folios except the one containing
-	 * @lock_at page. If @folio is not split, it will be kept locked.
-	 */
-	for (new_folio = folio; new_folio != end_folio; new_folio = next) {
-		next = folio_next(new_folio);
-		if (new_folio == page_folio(lock_at))
-			continue;
-
-		/*
-		 * Subpages whose mapping has been zapped may be freed
-		 * earlier, but freeing them requires taking the
-		 * lru_lock, so we defer folio_put() on tail pages until
-		 * after the split completes.
-		 */
-		if (is_swapcache && !folio_mapped(new_folio))
-			folio_free_swap(new_folio);
-		folio_unlock(new_folio);
-		folio_put(new_folio);
-	}
-
-out:
-	if (is_pmd_order(old_order))
-		count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED);
-	count_mthp_stat(old_order, !ret ? MTHP_STAT_SPLIT : MTHP_STAT_SPLIT_FAILED);
-	return ret;
-}
-
-/**
- * folio_split_unmapped() - split a large anon folio that is already unmapped
- * @folio: folio to split
- * @new_order: the order of folios after split
- *
- * This function is a helper for splitting folios that have already been
- * unmapped. The use case is that the device or the CPU can refuse to migrate
- * THP pages in the middle of migration, due to allocation issues on either
- * side.
- *
- * The anon rmap lock is not required to be held, mmap_read_lock() or
- * mmap_write_lock() should be held. @folio is expected to be locked by the
- * caller. device-private and non device-private folios are supported along
- * with folios that are in the swapcache. @folio should also be unmapped and
- * isolated from LRU (if applicable)
- *
- * Upon return, the folio is not remapped, split folios are not added to LRU,
- * folio_free_swap() is not called, and new folios remain locked.
- *
- * Return: 0 on success, -EAGAIN if the folio cannot be split (e.g., due to
- *         insufficient reference count or extra pins).
- */
-int folio_split_unmapped(struct folio *folio, unsigned int new_order)
-{
-	VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
-	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(!folio_test_anon(folio), folio);
-
-	return __folio_freeze_split_anon(folio, new_order, &folio->page,
-					 false, NULL, SPLIT_TYPE_UNIFORM);
-}
-
-/*
- * This function splits a large folio into smaller folios of order @new_order.
- * @page can point to any page of the large folio to split. The split operation
- * does not change the position of @page.
- *
- * Prerequisites:
- *
- * 1) The caller must hold a reference on the @page's owning folio, also known
- *    as the large folio.
- *
- * 2) The large folio must be locked.
- *
- * 3) The folio must not be pinned. Any unexpected folio references, including
- *    GUP pins, will result in the folio not getting split; instead, the caller
- *    will receive an -EAGAIN.
- *
- * 4) @new_order > 1, usually. Splitting to order-1 anonymous folios is not
- *    supported for non-file-backed folios, because folio->_deferred_list, which
- *    is used by partially mapped folios, is stored in subpage 2, but an order-1
- *    folio only has subpages 0 and 1. File-backed order-1 folios are supported,
- *    since they do not use _deferred_list.
- *
- * After splitting, the caller's folio reference will be transferred to @page,
- * resulting in a raised refcount of @page after this call. The other pages may
- * be freed if they are not mapped.
- *
- * If @list is null, tail pages will be added to LRU list, otherwise, to @list.
- *
- * Pages in @new_order will inherit the mapping, flags, and so on from the
- * huge page.
- *
- * Returns 0 if the huge page was split successfully.
- *
- * Returns -EAGAIN if the folio has unexpected reference (e.g., GUP) or if
- * the folio was concurrently removed from the page cache.
- *
- * Returns -EBUSY when trying to split the huge zeropage, if the folio is
- * under writeback, if fs-specific folio metadata cannot currently be
- * released, or if some unexpected race happened (e.g., anon VMA disappeared,
- * truncation).
- *
- * Callers should ensure that the order respects the address space mapping
- * min-order if one is set for non-anonymous folios.
- *
- * Returns -EINVAL when trying to split to an order that is incompatible
- * with the folio. Splitting to order 0 is compatible with all folios.
- */
-int __split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
-				     unsigned int new_order)
-{
-	struct folio *folio = page_folio(page);
-
-	return __folio_split(folio, new_order, &folio->page, page, list,
-			     SPLIT_TYPE_UNIFORM);
-}
-
-/**
- * folio_split() - split a folio at @split_at to a @new_order folio
- * @folio: folio to split
- * @new_order: the order of the new folio
- * @split_at: a page within the new folio
- * @list: after-split folios are added to @list if not null, otherwise to LRU
- *        list
- *
- * It has the same prerequisites and returns as
- * split_huge_page_to_list_to_order().
- *
- * Split a folio at @split_at to a new_order folio, leave the
- * remaining subpages of the original folio as large as possible. For example,
- * in the case of splitting an order-9 folio at its third order-3 subpages to
- * an order-3 folio, there are 2^(9-3)=64 order-3 subpages in the order-9 folio.
- * After the split, there will be a group of folios with different orders and
- * the new folio containing @split_at is marked in bracket:
- * [order-4, {order-3}, order-3, order-5, order-6, order-7, order-8].
- *
- * After split, folio is left locked for caller.
- *
- * Return: 0 - successful, <0 - failed (if -ENOMEM is returned, @folio might be
- * split but not to @new_order, the caller needs to check)
- */
-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);
-}
-
-/**
- * min_order_for_split() - get the minimum order @folio can be split to
- * @folio: folio to split
- *
- * min_order_for_split() tells the minimum order @folio can be split to.
- * If a file-backed folio is truncated, 0 will be returned. Any subsequent
- * split attempt should get -EBUSY from split checking code.
- *
- * Context: @folio must be locked.
- *
- * Return: @folio's minimum order for split
- */
-unsigned int min_order_for_split(struct folio *folio)
-{
-	VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
-
-	if (folio_test_anon(folio))
-		return 0;
-
-	/*
-	 * If the folio got truncated, we don't know the previous mapping and
-	 * consequently the old min order. But it doesn't matter, as any split
-	 * attempt will immediately fail with -EBUSY as the folio cannot get
-	 * split until freed.
-	 */
-	if (!folio->mapping)
-		return 0;
-
-	return mapping_min_folio_order(folio->mapping);
-}
-
-int split_folio_to_list(struct folio *folio, struct list_head *list)
-{
-	return split_huge_page_to_list_to_order(&folio->page, list, 0);
-}
-
 /*
  * __folio_unqueue_deferred_split() is not to be called directly:
  * the folio_unqueue_deferred_split() inline wrapper in mm/internal.h
diff --git a/mm/internal.h b/mm/internal.h
index 3a1643afe..ca549ad5e 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -779,6 +779,7 @@ static inline void folio_set_order(struct folio *folio, unsigned int order)
 #endif
 }
 
+void folio_reset_partially_mapped(struct folio *folio);
 bool __folio_unqueue_deferred_split(struct folio *folio);
 static inline bool folio_unqueue_deferred_split(struct folio *folio)
 {
-- 
2.50.1


  reply	other threads:[~2026-11-10  3:52 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10  3:52 [RFC PATCH 0/3] mm: enable filesystem page-cache large folios without THP Fang Xieyan
2026-11-10  3:52 ` Fang Xieyan [this message]
2026-11-10  3:52 ` [RFC PATCH 2/3] mm: build file-backed folio splitting with CONFIG_LARGE_FOLIO Fang Xieyan
2026-11-10  3:52 ` [RFC PATCH 3/3] mm: enable filesystem page-cache large folios without THP Fang Xieyan
2026-11-10  4:01 ` [RFC PATCH 0/3] " Zi Yan
2026-11-10  4:10   ` Zi Yan
2026-11-10  4:11     ` Zi Yan

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=20261110035205.3036-2-fangxy@xiaopeng.com \
    --to=fangxy@xiaopeng.com \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=cem@kernel.org \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=hughd@google.com \
    --cc=jack@suse.cz \
    --cc=kas@kernel.org \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=liuc63@xiaopeng.com \
    --cc=ljs@kernel.org \
    --cc=mhocko@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=nico.pache@linux.dev \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=surenb@google.com \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=willy@infradead.org \
    --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®