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

From: Zi Yan

Date: Thu Jul 30 2026 - 07:40:52 EST


On Thu Jul 30, 2026 at 5:28 AM EDT, David Hildenbrand (Arm) wrote:
> 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

Page migration has this issue for a long time.

I wonder if we can re-design per-CPU LRU cache so that we can remove a
folio from per-CPU LRU cache without drainining any to LRU lists.

Something like:
1. have a folio on per-CPU LRU cache to store a pointer to its
fb_batch's folio array position;

2. zero that position when we want to drain that specific folio.

But I have not looked into all the details to make sure that is feasible
yet.


--
Best Regards,
Yan, Zi