Re: [PATCH v1 07/10] mm/truncate: use folio_split() in truncate_inode_partial_folio()

Lorenzo Stoakes (Oracle)
2026-03-27T13:05:20Z

On Thu, Mar 26, 2026 at 09:42:52PM -0400, Zi Yan wrote:
> After READ_ONLY_THP_FOR_FS is removed, FS either supports large folio or
> not. folio_split() can be used on a FS with large folio support without
> worrying about getting a THP on a FS without large folio support.
>
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
> include/linux/huge_mm.h | 25 ++-----------------------
> mm/truncate.c | 8 ++++----
> 2 files changed, 6 insertions(+), 27 deletions(-)
>
> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> index 1258fa37e85b..171de8138e98 100644
> --- a/include/linux/huge_mm.h
> +++ b/include/linux/huge_mm.h
> @@ -389,27 +389,6 @@ static inline int split_huge_page_to_order(struct page *page, unsigned int new_o
> return split_huge_page_to_list_to_order(page, NULL, new_order);
> }
>
> -/**
> - * try_folio_split_to_order() - try to split a @folio at @page to @new_order
> - * using non uniform split.
> - * @folio: folio to be split
> - * @page: split to @new_order at the given page
> - * @new_order: the target split order
> - *
> - * Try to split a @folio at @page using non uniform split to @new_order, if
> - * non uniform split is not supported, fall back to uniform split. After-split
> - * folios are put back to LRU list. Use min_order_for_split() to get the lower
> - * bound of @new_order.
> - *
> - * Return: 0 - split is successful, otherwise split failed.
> - */
> -static inline int try_folio_split_to_order(struct folio *folio,
> - struct page *page, unsigned int new_order)
> -{
> - if (folio_check_splittable(folio, new_order, SPLIT_TYPE_NON_UNIFORM))
> - return split_huge_page_to_order(&folio->page, new_order);
> - return folio_split(folio, new_order, page, NULL);
> -}
> static inline int split_huge_page(struct page *page)
> {
> return split_huge_page_to_list_to_order(page, NULL, 0);
> @@ -641,8 +620,8 @@ static inline int split_folio_to_list(struct folio *folio, struct list_head *lis
> return -EINVAL;
> }

Hmm there's nothing in the comment or obvious jumping out at me to explain why
this is R/O thp file-backed only?

This seems like an arbitrary helper that just figures out whether it can split
using the non-uniform approach.

I think you need to explain more in the commit message why this was R/O thp
file-backed only, maybe mention some commits that added it etc., I had a quick
glance and even that didn't indicate why.

I look at folio_check_splittable() for instance and see:

...

} else if (split_type == SPLIT_TYPE_NON_UNIFORM || new_order) {
if (IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) &&
!mapping_large_folio_support(folio->mapping)) {
...
return -EINVAL;
}
}

...

if ((split_type == SPLIT_TYPE_NON_UNIFORM || new_order) && folio_test_swapcache(folio)) {
return -EINVAL;
}

if (is_huge_zero_folio(folio))
return -EINVAL;

if (folio_test_writeback(folio))
return -EBUSY;

return 0;
}

None of which suggest that you couldn't have non-uniform splits for other
cases? This at least needs some more explanation/justification in the
commit msg.

>
> -static inline int try_folio_split_to_order(struct folio *folio,
> - struct page *page, unsigned int new_order)
> +static inline int folio_split(struct folio *folio, unsigned int new_order,
> + struct page *page, struct list_head *list);

Yeah as Lance pointed out that ; probably shouldn't be there :)

> {
> VM_WARN_ON_ONCE_FOLIO(1, folio);
> return -EINVAL;
> diff --git a/mm/truncate.c b/mm/truncate.c
> index 2931d66c16d0..6973b05ec4b8 100644
> --- a/mm/truncate.c
> +++ b/mm/truncate.c
> @@ -177,7 +177,7 @@ int truncate_inode_folio(struct address_space *mapping, struct folio *folio)
> return 0;
> }
>
> -static int try_folio_split_or_unmap(struct folio *folio, struct page *split_at,
> +static int folio_split_or_unmap(struct folio *folio, struct page *split_at,
> unsigned long min_order)

I'm not sure the removal of 'try_' is warranted in general in this patch,
as it seems like it's not guaranteed any of these will succeed? Or am I
wrong?

> {
> enum ttu_flags ttu_flags =
> @@ -186,7 +186,7 @@ static int try_folio_split_or_unmap(struct folio *folio, struct page *split_at,
> TTU_IGNORE_MLOCK;
> int ret;
>
> - ret = try_folio_split_to_order(folio, split_at, min_order);
> + ret = folio_split(folio, min_order, split_at, NULL);
>
> /*
> * If the split fails, unmap the folio, so it will be refaulted
> @@ -252,7 +252,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
>
> min_order = mapping_min_folio_order(folio->mapping);
> split_at = folio_page(folio, PAGE_ALIGN_DOWN(offset) / PAGE_SIZE);
> - if (!try_folio_split_or_unmap(folio, split_at, min_order)) {
> + if (!folio_split_or_unmap(folio, split_at, min_order)) {
> /*
> * try to split at offset + length to make sure folios within
> * the range can be dropped, especially to avoid memory waste
> @@ -279,7 +279,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
> /* make sure folio2 is large and does not change its mapping */
> if (folio_test_large(folio2) &&
> folio2->mapping == folio->mapping)
> - try_folio_split_or_unmap(folio2, split_at2, min_order);
> + folio_split_or_unmap(folio2, split_at2, min_order);
>
> folio_unlock(folio2);
> out:
> --
> 2.43.0
>

Cheers, Lorenzo