Ten filesystems already opt in to file-backed large folios by calling mapping_set_large_folios() or mapping_set_folio_order_range() from their inode setup, unconditionally. Every accessor that reports those orders is nevertheless compiled out when CONFIG_TRANSPARENT_HUGEPAGE is disabled, so the opt-in silently has no effect today. Gate the page cache order accessors on CONFIG_LARGE_FOLIO in addition to CONFIG_TRANSPARENT_HUGEPAGE. Both conditions are still spelled out even though CONFIG_LARGE_FOLIO defaults to y when TRANSPARENT_HUGEPAGE is enabled, so that file-backed large folios do not silently disappear if that default is ever relaxed. PREFERRED_MAX_PAGECACHE_ORDER is left as it is. Deriving it from CONFIG_PGTABLE_HAS_HUGE_LEAVES would let HUGETLB_PAGE raise the ceiling on its own, to HPAGE_PMD_ORDER capped by MAX_XAS_ORDER. On arm64 with 64K base pages that works out to order 11, a 128M folio, eight times what an LF-only build advertises today; the figure is arithmetic, not a measurement. Without THP such folios cannot be PMD-mapped, so keeping the existing order-8 limit avoids substantially increasing the maximum page-cache allocation size without an established use case. CONFIG_LARGE_FOLIO no longer only builds the split machinery, so its Kconfig help text is updated to match. The default also carries a comment recording why THP must keep pulling this option in: mm/folio_split.o is built from CONFIG_LARGE_FOLIO, while huge_memory.o, built for THP, calls into it. XFS also gates mounting block sizes larger than the page size on mapping_max_folio_size_supported(), which means large-block-size XFS cannot be mounted at all when CONFIG_LARGE_FOLIO is disabled; the rejection now points at the missing option instead of only reporting the block size as unsupported. shmem keeps its THP dependency. shmem_alloc_and_add_folio() forces orders to zero without THP, so the mapping is only opened large-folio capable when CONFIG_TRANSPARENT_HUGEPAGE is enabled. Advertising the larger order while allocating order 0 would not be cosmetic: 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(), while shmem_write_begin() rejects pos + len > i_size outright for F_SEAL_GROW. A cross-EOF write would then fail as a whole instead of being partially satisfied up to i_size. THP=y kernels already behave that way; the point is not to spread that behaviour to a configuration which gains no large folios from it. Enabling shmem's large folios -- allocation, swap and split alike -- is left to a separate series. The stale comment on mapping_large_folio_support() is updated to match: large folios are now gated on CONFIG_LARGE_FOLIO rather than on THP, and CONFIG_LARGE_FOLIO is a default rather than a select from THP. With CONFIG_TRANSPARENT_HUGEPAGE enabled the generated code is unchanged. Without it, file-backed large folios become reachable for the first time. Reviewed-by: Liu Chao Signed-off-by: Fang Xieyan --- fs/xfs/xfs_super.c | 2 ++ include/linux/pagemap.h | 17 +++++++++++------ mm/Kconfig | 15 +++++++++++---- mm/shmem.c | 11 ++++++++++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 5a06132aa..29548faec 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1864,6 +1864,8 @@ xfs_fs_fill_super( xfs_warn(mp, "block size (%u bytes) not supported; Only block size (%zu) or less is supported", mp->m_sb.sb_blocksize, max_folio_size); + if (!IS_ENABLED(CONFIG_LARGE_FOLIO)) + xfs_warn(mp, "CONFIG_LARGE_FOLIO is not set"); error = -ENOSYS; goto out_free_sb; } diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 73af18a37..2102733c1 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -393,7 +393,8 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) */ static inline size_t mapping_max_folio_size_supported(void) { - if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) || + IS_ENABLED(CONFIG_LARGE_FOLIO)) return 1U << (PAGE_SHIFT + MAX_PAGECACHE_ORDER); return PAGE_SIZE; } @@ -418,7 +419,8 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping, unsigned int min, unsigned int max) { - if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && + !IS_ENABLED(CONFIG_LARGE_FOLIO)) return; if (min > MAX_PAGECACHE_ORDER) @@ -459,7 +461,8 @@ static inline void mapping_set_large_folios(struct address_space *mapping) static inline unsigned int mapping_max_folio_order(const struct address_space *mapping) { - if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && + !IS_ENABLED(CONFIG_LARGE_FOLIO)) return 0; return (mapping->flags & AS_FOLIO_ORDER_MAX_MASK) >> AS_FOLIO_ORDER_MAX; } @@ -467,7 +470,8 @@ mapping_max_folio_order(const struct address_space *mapping) static inline unsigned int mapping_min_folio_order(const struct address_space *mapping) { - if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && + !IS_ENABLED(CONFIG_LARGE_FOLIO)) return 0; return (mapping->flags & AS_FOLIO_ORDER_MIN_MASK) >> AS_FOLIO_ORDER_MIN; } @@ -500,8 +504,9 @@ static inline pgoff_t mapping_align_index(const struct address_space *mapping, } /* - * Large folio support currently depends on THP. These dependencies are - * being worked on but are not yet fixed. + * Large folio support is opt-in per mapping and requires CONFIG_LARGE_FOLIO. + * It does not depend on THP; CONFIG_LARGE_FOLIO defaults to y when + * CONFIG_TRANSPARENT_HUGEPAGE is enabled. */ static inline bool mapping_large_folio_support(const struct address_space *mapping) { diff --git a/mm/Kconfig b/mm/Kconfig index 9f97ad698..7bbed71af 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -831,13 +831,20 @@ config LARGE_FOLIO bool "Large folio support for the page cache (without THP)" if !TRANSPARENT_HUGEPAGE depends on MMU select XARRAY_MULTI + # Keep LARGE_FOLIO enabled whenever THP is enabled: + # huge_memory.o is built for TRANSPARENT_HUGEPAGE and calls into + # folio_split.o, which is built for LARGE_FOLIO. default y if TRANSPARENT_HUGEPAGE default n help - Build the folio split implementation for page-cache large folios - even when transparent hugepages are disabled. Transparent - hugepages imply this option; this option alone does not enable - large folio allocation, it only makes splitting them possible. + Enable page-cache large folios even when transparent hugepages + are disabled. This option defaults to y when transparent + hugepages are enabled. + + With this option enabled, filesystems that opt in per mapping get + large folios in the page cache, and those folios can be split. + Without it, page-cache folios stay order-0 even when a filesystem + asks for a larger order. menuconfig TRANSPARENT_HUGEPAGE bool "Transparent Hugepage Support" diff --git a/mm/shmem.c b/mm/shmem.c index 4686875a3..399c8c9f1 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3327,7 +3327,16 @@ static struct inode *__shmem_get_inode(const struct mnt_idmap *idmap, cache_no_acl(inode); if (sbinfo->noswap) mapping_set_unevictable(inode->i_mapping); - mapping_set_large_folios(inode->i_mapping); + /* + * shmem large folios currently depend on THP: + * shmem_alloc_and_add_folio() forces order 0 without it, and + * advertising a larger mapping size makes + * generic_perform_write() pass a length that shmem_write_begin() + * rejects wholesale for F_SEAL_GROW before the folio size is + * known. + */ + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + mapping_set_large_folios(inode->i_mapping); switch (mode & S_IFMT) { default: -- 2.50.1