Re: [PATCH] mm/mglru: Fix young counter undercount for large folios
From: Barry Song
Date: Wed Aug 12 2026 - 20:56:05 EST
On Wed, Aug 12, 2026 at 6:17 PM Baolin Wang
<baolin.wang@xxxxxxxxxxxxxxxxx> wrote:
>
>
>
> On 8/12/26 2:59 PM, Hui Zhu wrote:
> > From: Hui Zhu <zhuhui@xxxxxxxxxx>
> >
> > In lru_gen_look_around(), the young counter tracks the number of young
> > PTEs. The original folio's contribution is represented by the initial
> > value of young: test_and_clear_young_ptes_notify() is called on it at
> > function entry, and the function returns early if it is not young. In
> > the subsequent loop, the original folio is skipped (its accessed bits
> > were already cleared), so it is not double-counted.
> >
> > However, young is initialized to 1 regardless of the folio size. When
> > the original folio is a large folio with nr PTEs, its young count is
> > underestimated by nr - 1. This inconsistency can cause
> > suitable_to_scan() to return false, preventing the PMD from being added
> > to the bloom filter and reducing aging accuracy for mTHP workloads.
> >
> > Initialize young to nr so the original folio is accounted the same way
> > as other young folios in the loop (young += nr).
> >
> > Signed-off-by: Hui Zhu <zhuhui@xxxxxxxxxx>
> > ---
>
> Good catch. Please also add the Fixes tag:
>
> Fixes: 56e5b60b2114 ("mm: support batched checking of the young flag for
> MGLRU")
>
> With that,
> Reviewed-by: Baolin Wang <baolin.wang@xxxxxxxxxxxxxxxxx>
Hi Baolin, Hui,
I am not convinced this is the correct patch. test_and_clear_young_ptes_notify()
only indicates that there is at least one young PTE among the nr PTEs;
it does not mean that all of the PTEs are young.
Am I missing something?
bool contpte_test_and_clear_young_ptes(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep, unsigned int nr)
{
...
unsigned long end = addr + nr * PAGE_SIZE;
bool young = false;
ptep = contpte_align_addr_ptep(&addr, &end, ptep, nr);
for (; addr != end; ptep++, addr += PAGE_SIZE)
young |= __ptep_test_and_clear_young(vma, addr, ptep);
return young;
}
EXPORT_SYMBOL_GPL(contpte_test_and_clear_young_ptes);
>
> > mm/vmscan.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > index bc324e37c5f1..264017850a55 100644
> > --- a/mm/vmscan.c
> > +++ b/mm/vmscan.c
> > @@ -4192,7 +4192,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
> > unsigned long end;
> > struct lru_gen_mm_walk *walk;
> > struct folio *last = NULL;
> > - int young = 1;
> > + int young = nr;
> > pte_t *pte = pvmw->pte;
> > unsigned long addr = pvmw->address;
> > struct vm_area_struct *vma = pvmw->vma;
>
Thanks
Barry