Re: [RFC PATCH v2 1/2] mm: allow smaller large folios to use lru_cache

From: David Hildenbrand (Arm)

Date: Thu Jul 30 2026 - 05:41:37 EST


On 7/30/26 00:26, Barry Song (Xiaomi) wrote:
> On Wed, Jul 29, 2026 at 8:13 PM David Hildenbrand (Arm) <david@xxxxxxxxxx> wrote:
> [...]
>>> diff --git a/include/linux/swap.h b/include/linux/swap.h
>>> index 8d19be675baf..9c5f7a11c7b0 100644
>>> --- a/include/linux/swap.h
>>> +++ b/include/linux/swap.h
>>> @@ -316,9 +316,9 @@ static inline bool folio_may_be_lru_cached(struct folio *folio)
>>> /*
>>> * Holding PMD-sized folios in per-CPU LRU cache unbalances accounting.
>>> * Holding small numbers of low-order mTHP folios in per-CPU LRU cache
>>> - * will be sensible, but nobody has implemented and tested that yet.
>>> + * will be sensible.
>>> */
>>> - return !folio_test_large(folio);
>>> + return folio_order(folio) < PAGE_ALLOC_COSTLY_ORDER;
>>> }
>>>
>>> extern atomic_t lru_disable_count;
>>
>> Sashiko rightfully raises that split_folio() can now fail more easily.
>>
>> So we might want to proactively drain (earlier?) on some more of the split paths.
>
> Yes, this is a pain. I haven't tested it, but perhaps a
> conceptual model could be something like this?
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 04e8a6b55343..fcd449dec96b 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -4069,6 +4069,8 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
> struct folio *new_folio, *next;
> int nr_shmem_dropped = 0;
> enum ttu_flags ttu_flags = 0;
> + bool maybe_in_lru_cache;
> + int expected_ref_count;
> int ret;
> pgoff_t end = 0;
>
> @@ -4154,7 +4156,15 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
> * Racy check if we can split the page, before unmap_folio() will
> * split PMDs
> */
> - if (folio_expected_ref_count(folio) != folio_ref_count(folio) - 1) {
> + maybe_in_lru_cache = folio_may_be_lru_cached(folio) && !folio_test_lru(folio);
> + expected_ref_count = folio_expected_ref_count(folio);
> + if (expected_ref_count + maybe_in_lru_cache < folio_ref_count(folio) - 1) {
> + ret = -EAGAIN;
> + goto out_unlock;
> + }
> + if (maybe_in_lru_cache)
> + lru_add_drain_all();

The drain-all is nasty. In collect_longterm_unpinnable_folios() we escalate the
drainining.

I think we recently stumbled into a similar problem in guest_memfd.

Ah, yes, there it is:

https://lore.kernel.org/r/20260728-gmem-inplace-conversion-v9-14-35f9aec2aed2@xxxxxxxxxx


--
Cheers,

David