mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] mm: enable filesystem page-cache large folios without THP
@ 2026-11-10  3:52 Fang Xieyan
  2026-11-10  3:52 ` [RFC PATCH 1/3] mm: move folio split code into mm/folio_split.c Fang Xieyan
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Fang Xieyan @ 2026-11-10  3:52 UTC (permalink / raw)
  To: Andrew Morton, David Hildenbrand, Zi Yan, Matthew Wilcox,
	Jan Kara, Hugh Dickins, Carlos Maiolino
  Cc: Baolin Wang, Liam R . Howlett, Nico Pache, Ryan Roberts,
	Dev Jain, Barry Song, Lance Yang, Usama Arif, Kiryl Shutsemau,
	Lorenzo Stoakes, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Muchun Song, Kairui Song,
	linux-mm, linux-kernel, linux-fsdevel, linux-xfs

This series makes filesystem page-cache large folios work on kernels
built with CONFIG_TRANSPARENT_HUGEPAGE=n, and keeps them splittable
there.

1. Why this is needed

TRANSPARENT_HUGEPAGE depends on !PREEMPT_RT (mm/Kconfig), so a
PREEMPT_RT kernel always has THP off.  Today that also removes
file-backed large folios entirely: the helpers that split them live in
mm/huge_memory.c under CONFIG_TRANSPARENT_HUGEPAGE, and the order
accessors in include/linux/pagemap.h short-circuit to "no large
folios" when THP is off, so every filesystem that opts in silently
ends up with order-0 folios.

The consequence is not only throughput.  XFS allows block sizes up to
64K and fs/xfs/xfs_super.c refuses to mount anything larger than
mapping_max_folio_size_supported().  With THP=n that helper reports
PAGE_SIZE, so a 64K-block filesystem cannot be mounted at all:

    XFS (vda): block size (65536 bytes) not supported; Only block size (4096) or less is supported

There is no degraded mode to fall back to, because a block has to fit
in a single page-cache folio - which is why
fs/xfs/libxfs/xfs_ialloc.c derives min_folio_order from
sb_blocklog - PAGE_SHIFT.  Either page-cache large folios work without
THP, or the filesystem is unusable.

2. What the series does

The anon and file split helpers were already separated by Kairui
Song's "mm/huge_memory: clean up and decouple the anon and file split
helpers" (v6 04/17, in mm-unstable); that is the base this series
starts from.

1/3 moves the folio-splitting implementation out of mm/huge_memory.c
into mm/folio_split.c without changing any behaviour.  The new object
is still built by CONFIG_TRANSPARENT_HUGEPAGE, so a THP build keeps
exactly the code it had before; a pure move is also the easiest thing
to rebase on top of further churn in this area.

2/3 builds that object for CONFIG_LARGE_FOLIO instead, and drops the
anon, PMD and khugepaged dependencies a file-only build cannot have.
The symbol comes from Matthew Wilcox's "[PATCH v9 12/15]"; if that
series is in first, this patch shrinks to its Kconfig default and the
accessors it already rewrites.

3/3 enables filesystem page-cache large folios when THP is off.  It
lifts the compile-time short circuits in pagemap.h for
mapping_max_folio_size_supported(), mapping_set_folio_order_range(),
mapping_max_folio_order() and mapping_min_folio_order().
PREFERRED_MAX_PAGECACHE_ORDER is left alone: an LF-only build keeps
the existing cap of order 8 instead of following HPAGE_PMD_ORDER.
Without THP such folios cannot be PMD-mapped, so raising the ceiling
would mainly increase the maximum page-cache allocation size.  The
changelog has the arm64 64K-page arithmetic, which is a calculation
and not a measurement.

The split side is not rewritten: truncate, migration and memory-failure
already call the helpers outside any THP ifdef, so with 2/3 in place
they simply stop resolving to the -EINVAL stubs once 3/3 turns
allocation on.  madvise is inside a THP ifdef and does not take part.

3/3 also makes the XFS mount failure above say why it failed: when
CONFIG_LARGE_FOLIO is not set, the warning now names the missing
option instead of only reporting the block size as unsupported.  With
LF=y the added warning is compiled out completely.

3. Testing

Six configurations build clean as full kernels, bzImage and the
vmlinux link included, with no new warnings: A (THP=y), B (THP=n,
LF=y), C (B plus HUGETLBFS), D (THP=n, LF=n), and RT-B and RT-C with
PREEMPT_RT=y.  The PREEMPT_RT configurations were build-tested only.
No runtime PREEMPT_RT stress test or livelock validation was
performed.

THP=y is unchanged: the .text sections of mm/filemap.o,
mm/readahead.o and mm/shmem.o are byte-identical before and after the
series (md5 equal, 45896, 7139 and 35314 bytes).  With THP=n and LF=n
mm/filemap.o and mm/readahead.o stay identical (44584 and 6259 bytes)
and every function in mm/shmem.o keeps its size; the only difference
there is one dead argument load that the folded-out
mapping_set_large_folios() call leaves behind, so the section keeps
its 29778 bytes while its md5 does not match.

In the new LF=y, THP=n configuration mm/shmem.o keeps its size as well,
48 bytes shorter at 29730.  That difference is not from 3/3: 40 of the
bytes are shmem_writeout(), which shrinks because 2/3 turns
split_folio_to_list() from an inline -EINVAL stub into a real call, and
the other 8 are the padding in front of the following function; the
same 29730 bytes appear with 2/3 alone, and 3/3's own shmem hunk
changes no function size.  The new call is unreachable in this
configuration anyway, since shmem_alloc_and_add_folio() still forces
order 0 without THP.

Where the generated code really changes is the allocation policy:
mm/readahead.o grows by 880 bytes of .text (6259 to 7139) and
mm/filemap.o by 134 (44584 to 44718).  nm -u mm/folio_split.o lists no
anon, pmd or khugepaged symbols.

Runtime, x86_64, 4K pages, THP=n, QEMU/KVM, on a 64K-block XFS image
(mkfs.xfs -b size=64k -s size=4k), with kprobes on
filemap_alloc_folio, filemap_add_folio and __folio_split:

  - writing 64M: 84 allocations - 59 at order-8, 5 at order-7, 5 at
    order-6, 5 at order-5, 10 at order-4, i.e. exactly 64M;
  - reading the same 64M back after drop_caches: 72 allocations,
    62 at order-8, 1 at order-7, 5 at order-6, 4 at order-4;
  - truncating 1M to 700K: one __folio_split, landing at order-4,
    which is the min_folio_order XFS asked for; order-0 never appears
    in any of the three phases;
  - tmpfs writing 32M: 8192 shmem_alloc_folio calls, all order-0;
  - shmem write vs F_SEAL_GROW, reproducible as: memfd_create(),
    ftruncate() to exactly 4096 bytes, F_ADD_SEALS with F_SEAL_GROW,
    then a single pwrite() of 8192 bytes at offset 0, comparing the
    syscall's return value.  It returns 4096, the same as the
    pre-series THP=n kernel.  A THP=y kernel returns -EPERM here, and
    so did the LF-only build before shmem's mapping was put back
    behind THP; that -EPERM is pre-existing behaviour of THP kernels,
    this series neither introduces it nor extends it to other
    configurations.

Same image on the pre-series kernel, for reference: it fails to mount
with -ENOSYS before any of the above can run.  The whole probe was
re-run on the final revision of the series with identical numbers.

4. What this turns on for existing filesystems

LF=y makes the existing opt-in take effect for every filesystem that
already asks for large folios, and that list is not ext4 only: ext4,
xfs, btrfs, f2fs, fuse, afs, erofs, nfs, cifs and zonefs all call
mapping_set_folio_order_range() or mapping_set_large_folios() from
their inode or superblock setup, unconditionally, today.  Those paths
run on any THP=y kernel; the new configuration is only THP=n with
LF=y.  With THP off the ceiling is PREFERRED_MAX_PAGECACHE_ORDER,
which stays at order 8.

On the tested 4K-page configuration, 4K-block ext4 gets min order 0
and max order 8, while 64K-block XFS gets min order 4.  Btrfs derives
both ends from the mkfs sectorsize and nodesize.  A THP=y kernel keeps
the order-9 ceiling (HPAGE_PMD_ORDER) it has today on 4K pages.

shmem keeps its THP dependency: its mapping is only marked large-folio
capable when THP is enabled, because shmem_alloc_and_add_folio() still
forces orders to zero without it.  This matters beyond cosmetics.
generic_perform_write() sizes its write chunk from
mapping_max_folio_size() and passes that length to ->write_begin()
before clamping it to folio_size(), and shmem_write_begin() rejects
pos + len > i_size for F_SEAL_GROW.  On a 4K memfd sealed with
F_SEAL_GROW, an 8K write at offset 0 returns 4096 on the pre-series
THP=n kernel and -EPERM on a THP=y kernel; advertising order 8 while
allocating order 0 reproduced the -EPERM on an LF-only build.  Tying
shmem's policy to LARGE_FOLIO means the allocation, swap and split
sides together, which is a separate series.

5. What is not covered

Since PREEMPT_RT is the motivation, the RT side needs to be explicit.
The freeze-to-xarray-update window in __folio_freeze_split_file() is
the fourth freeze site named by Matthew Wilcox in the RT livelock
thread started by Dong Hui
(<c2ff0c66422c4e2d869499a1186ff0ae@xiaomi.com>):

    There's also __folio_freeze_split_file().  I know PREEMPT_RT
    disables TRANSPARENT_HUGEPAGE at the moment, but hopefully that
    restriction will be lifted.

This series does not fix that problem and does not claim to be
RT-safe.  It only makes a code path that already exists on THP kernels
reachable in a configuration that has no THP, which is the same
exposure Kairui described in 06/17 as "should be fixed generically".
One detail that may matter if a reader-side fix is chosen: the
function already holds the xarray lock until the folio is no longer
frozen and visible, since folio_ref_unfreeze() happens before
xas_unlock_irq(), so a generic fix of that shape would cover this path
without touching it.

Also not tested here: the migration, memory-failure and madvise split
entries at runtime (only truncate was exercised end to end), arm64
with 64K pages, nommu, KASAN and lockdep, xfstests, and the btrfs
order range.

6. Questions

(a) Non-THP split accounting.  count_mthp_stat() is a no-op without
THP, so in an LF-only build a file split updates no counter at all and
THP_SPLIT_PAGE does not even appear in /proc/vmstat.  I have a
follow-up patch that gives the file path its own counter.  Would you
rather see a new vmstat item, an existing one reused, or nothing?

(b) Should XFS select LARGE_FOLIO itself?  Three lines of
"select LARGE_FOLIO if MMU" in fs/xfs/Kconfig make 64K-block XFS
mountable on a THP=n kernel with no manual configuration; I built such
a kernel and mounted the 64K image.  The cost is mm/folio_split.o: 3.2K
of .text in a THP=n build, code that every THP=y build already carries,
paid also by XFS builds that will never use a block size above the page
size.  Kconfig cannot express "only when a 64K filesystem is mounted".
I left it out because it changes default coverage for a decision XFS
maintainers should make, but I can add it as 4/4 if that is preferred.

Comments welcome.

Fang Xieyan (3):
  mm: move folio split code into mm/folio_split.c
  mm: build file-backed folio splitting with CONFIG_LARGE_FOLIO
  mm: enable filesystem page-cache large folios without THP

 MAINTAINERS                |   1 +
 fs/xfs/xfs_super.c         |   2 +
 include/linux/huge_mm.h    |  60 ++-
 include/linux/page-flags.h |   5 +-
 include/linux/pagemap.h    |  17 +-
 mm/Kconfig                 |  19 +
 mm/Makefile                |   1 +
 mm/folio_split.c           | 969 +++++++++++++++++++++++++++++++++++++
 mm/huge_memory.c           | 920 -----------------------------------
 mm/internal.h              |   1 +
 mm/shmem.c                 |  11 +-
 11 files changed, 1054 insertions(+), 952 deletions(-)
 create mode 100644 mm/folio_split.c


base-commit: b024a51b0184b1efb6f55b11aaf7b15a490a6307
-- 
2.50.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-10  4:11 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [RFC PATCH 1/3] mm: move folio split code into mm/folio_split.c Fang Xieyan
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

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®