On Wed, 23 Sep 2026 23:32:31 +0200 Kairui Song via B4 Relay <devnull+kasong.tencent.com@kernel.org> wrote:
> The folio split path handles anon, page cache and swap cache folios in
> one routine. That mixing is what makes the swap cache split
> restrictions hard to lift and to review. We now support uniform split to
> order-0 only, and no mappingless swap cache folios. And it has left a fair
> number of dead or redundant checks behind.
>
> This series prepares for lifting those restrictions by cleaning up the
> code first: split the routine into an anon and a file helper, and keep
> all swap cache handling in the anon helper. The file helper never sees
> a swap cache folio, folio_check_splittable() rejects them up front.
>
> Apart from two bug fixes (patch 1 and 2) and a slight adjustment of anon
> splitting (patch 12), this is a pure cleanup.
Thanks, I've updated mm.git's mm-unstable branch to this version.
> Changes in v6:
> - Comments updated and make a few variables const as suggested by
> [David Hildenbrand]
> - Link to v5: https://patch.msgid.link/20260915-swap-thp-cleanup-v5-0-39878b37dfb0@tencent.com
Here's how v6 altered mm.git:
mm/huge_memory.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
--- a/mm/huge_memory.c~b
+++ a/mm/huge_memory.c
@@ -3538,6 +3538,11 @@ void vma_adjust_trans_huge(struct vm_are
split_huge_pmd_if_needed(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 |
@@ -3555,7 +3560,6 @@ static int unmap_folio(struct folio *fol
/*
* Anon pages need migration entries to preserve them, but file
* pages can simply be left unmapped, then faulted back on demand.
- * If that is ever changed (perhaps for mlock), update remap_anon_folio().
*/
if (folio_test_anon(folio))
try_to_migrate(folio, ttu_flags);
@@ -4025,7 +4029,7 @@ static int __folio_freeze_split_anon(str
{
struct folio *end_folio = folio_next(folio);
struct swap_cluster_info *ci = NULL;
- int old_order = folio_order(folio);
+ const int old_order = folio_order(folio);
struct folio *new_folio, *next;
struct anon_vma *anon_vma = NULL;
enum ttu_flags ttu_flags = 0;
@@ -4048,8 +4052,7 @@ static int __folio_freeze_split_anon(str
* the reference nor the lock. Anything else needs a reference
* first and folio_ref_freeze() below catches it.
*
- * Note a swapped-out THP counts as unmapped here as swap PTEs do
- * not contribute mapcount, and they are splittable.
+ * Note that entirely swapped-out THPs are unmapped but can be split.
*/
if (folio_mapped(folio)) {
anon_vma = folio_get_anon_vma(folio);
@@ -4133,10 +4136,10 @@ static int __folio_freeze_split_file(str
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);
- long old_nr_pages = folio_nr_pages(folio);
struct mem_cgroup *memcg, *old_memcg;
struct folio *new_folio, *next;
int nr_shmem_dropped = 0;
@@ -4166,7 +4169,7 @@ static int __folio_freeze_split_file(str
mapping_set_update(&xas, mapping);
if (split_type == SPLIT_TYPE_UNIFORM) {
- int old_order = folio_order(folio);
+ const int old_order = folio_order(folio);
xas_set_order(&xas, folio->index, new_order);
xas_split_alloc(&xas, folio, old_order, gfp);
@@ -4269,10 +4272,6 @@ static int __folio_freeze_split_file(str
folio_ref_unfreeze(folio, folio_nr_pages(folio) + 1);
lruvec_unlock(lruvec);
fail:
- /*
- * If we want to use try_to_migrate() on file in unmap_folio,
- * remember to add remap_anon_folio() and adapt it.
- */
xas_unlock_irq(&xas);
fail_mmap_unlock:
if (nr_shmem_dropped)
_