Re: [PATCH 6/7] mm/mglru: fix potential generation folio number leak

Baolin Wang <[email protected]>
Newsgroups org.kernel.vger.cgroups,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <[email protected]>

On 8/20/26 11:45 AM, Kairui Song wrote:
> On Thu, Aug 20, 2026 at 9:52 AM Baolin Wang
> <[email protected]> wrote:
>> On 8/18/26 1:38 PM, Kairui Song via B4 Relay wrote:
>>> From: Kairui Song <[email protected]>
>>>
>>> Each generation of MGLRU accounts anon and file folio numbers
>>> separately. The page table walker's update_batch_size() derives the
>>> anon / file type of a folio from its current flags, but the page table
>>> walk holds neither the lruvec lock nor the folio lock, so the type can
>>> change during that period.
>>
>> Right.
>>
>>> MADV_FREE's lazyfree path clears PG_swapbacked under the lruvec lock,
>>> so the folio is no longer considered on the anon LRU list. Lazyfreed
>>> folios can also be changed back to the anon list again. If the flip
>>> lands between folio_update_gen()'s cmpxchg and the type read in
>>> update_batch_size(), the batched delta pair is applied to the wrong
>>> type. The anon and file generation counters then carry phantom deltas
>>> that nothing reconciles, permanently skewing lrugen->nr_pages and the
>>> reclaim budgets derived from it.
>>
>> But I think the problem occurs between update_batch_size() and
>> sort_folio(). update_batch_size() only updates the anon or file folio
>> statistics, while sort_folio() moves promoted folios to the
>> corresponding type's list:
>>
>> /* promoted */
>> if (gen != lru_gen_from_seq(lrugen->min_seq[type])) {
>>          list_move(&folio->lru, &lrugen->folios[gen][type][zone]);
>>          return true;
>> }
>>
>> If the folio's anon/file type changes between these two steps (e.g., a
>> lazyfree folio), it would lead to what you described: "The anon and file
>> generation counters then carry phantom deltas that nothing reconciles,
>> permanently skewing lrugen->nr_pages and the reclaim budgets derived
>> from it."
> 
> Actually no, the counters follow eventual consistency (note the word
> "permanently"), we are fine with a drift as long as it will eventually
> be corrected. Lazy promotions creates counter drift from the physical
> location, but that is actually fixed by the sort_folio.
> 
> Now, for the type issue, use the lazyfree case as example (I think
> that's actually the only place,), lru_lazyfree will remove the folio
> form lruvec before marking it !PG_swapbacked, so during that removal
> period, the gen bits are zero (folio's gen == -1), so any lazy
> promotion CAS won't touch the counter, and only folio_update_gen will
> do it since folio_inc_gen only handles on list folios. The
> PG_swapbacked clearing in lazyfree only happens on folio wich has gen
> == -1 (off-list). And it makes sense since update PG_swapbacked need
> to update the counter and move the folio.
> 
> And if the CAS happends before the list removal, the list removal, the
> folio is on the anon list, so the CAS is moving a folio in the anon
> list, folio_update_gen will call update_batch_size asking it to update
> the anon counters, we are fine after this commit. (Before this commit,
> the CAS is moving a folio in the anon list but update_batch_size may
> occur on file coutners). The list removal will update the anon
> counter, and the subsequent list addition will account for the right
> file counter.
> 
> And if the CAS happens after the list add, we are still fine since the
> file counter is charged and sees a file type here.

Thanks for the expalnation. Now I see the problem and I think your are 
right.

(This involves the combination of the gen counter and the folio's type, 
so it would be better to have a diagram describing the race, otherwise 
it's indeed hard to follow.)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.