From: Baolin Wang <baolin.wang@linux.alibaba.com>
To: kasong@tencent.com, linux-mm@kvack.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
Barry Song <baohua@kernel.org>,
Axel Rasmussen <axelrasmussen@google.com>,
Yuanchu Xie <yuanchu@google.com>, Wei Xu <weixugc@google.com>,
Baoquan He <baoquan.he@linux.dev>,
Shakeel Butt <shakeel.butt@linux.dev>,
Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@kernel.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Muchun Song <muchun.song@linux.dev>, Chris Li <chrisl@kernel.org>,
David Hildenbrand <david@kernel.org>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>, Yu Zhao <yuzhao@google.com>,
Zi Yan <ziy@nvidia.com>, Qi Zheng <qi.zheng@linux.dev>,
cgroups@vger.kernel.org, linux-kernel@vger.kernel.org,
Kairui Song <ryncsn@gmail.com>
Subject: Re: [PATCH 6/7] mm/mglru: fix potential generation folio number leak
Date: Thu, 20 Aug 2026 09:52:02 +0800 [thread overview]
Message-ID: <5db7dbfe-ec22-4a25-a85d-497e6ed1f8b1@linux.alibaba.com> (raw)
In-Reply-To: <20260818-mglru-flags-cleanup-v1-6-8dbbdac0d28c@tencent.com>
On 8/18/26 1:38 PM, Kairui Song via B4 Relay wrote:
> From: Kairui Song <kasong@tencent.com>
>
> 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."
If you agree that this is where the problem lies, I don't see a good way
to fix it, since the state of a lazyfree folio can change between
update_batch_size() and sort_folio().
A simple approach would be to skip checking the access flag for lazyfree
folios during the page table walk, and let shrink_folio_list()
reactivate accessed lazyfree folios instead. What do you think?
> Fix it by capturing the type from the flags snapshot the cmpxchg
> linearized against: folio_update_gen() returns the type of the state
> it transitioned from, and update_batch_size() accounts with it instead
> of re-reading the live flags. The batched deltas then always match the
> type of the state the cmpxchg transitioned from.
>
> Fixes: 018ee47f1489 ("mm: multi-gen LRU: exploit locality in rmap")
> Signed-off-by: Kairui Song <kasong@tencent.com>
> ---
> include/linux/mm_inline.h | 7 ++++++-
> mm/vmscan.c | 13 +++++++------
> 2 files changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
> index df62daaa2ee7..4bb390d9516e 100644
> --- a/include/linux/mm_inline.h
> +++ b/include/linux/mm_inline.h
> @@ -10,6 +10,11 @@
> #include <linux/userfaultfd_k.h>
> #include <linux/leafops.h>
>
> +static inline int folio_flags_is_file_lru(const unsigned long *flags)
> +{
> + return !test_bit(PG_swapbacked, flags);
> +}
> +
> /**
> * folio_is_file_lru - Should the folio be on a file LRU or anon LRU?
> * @folio: The folio to test.
> @@ -27,7 +32,7 @@
> */
> static inline int folio_is_file_lru(const struct folio *folio)
> {
> - return !folio_test_swapbacked(folio);
> + return folio_flags_is_file_lru(const_folio_flags(folio, 0));
> }
>
> static __always_inline void __update_lru_size(struct lruvec *lruvec,
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index a613bb8d7271..7169cac60869 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -3269,7 +3269,8 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
> ******************************************************************************/
>
> /* promote pages accessed through page tables */
> -static int folio_update_gen(struct folio *folio, int new_gen, const vma_flags_t *vma_flags)
> +static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
> + const vma_flags_t *vma_flags)
> {
> unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
> int old_gen;
> @@ -3298,6 +3299,7 @@ static int folio_update_gen(struct folio *folio, int new_gen, const vma_flags_t
> new_flags |= BIT(PG_workingset);
> } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
>
> + *is_file = folio_flags_is_file_lru(&old_flags);
> return old_gen;
> }
>
> @@ -3328,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
> }
>
> static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
> - int old_gen, int new_gen)
> + int old_gen, int new_gen, int type)
> {
> - int type = folio_is_file_lru(folio);
> int zone = folio_zonenum(folio);
> int delta = folio_nr_pages(folio);
>
> @@ -3519,7 +3520,7 @@ static bool suitable_to_scan(int total, int young)
> static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
> struct lruvec *lruvec, struct folio *folio, bool dirty)
> {
> - int new_gen, old_gen;
> + int new_gen, old_gen, file;
>
> if (!folio)
> return;
> @@ -3532,9 +3533,9 @@ static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struc
> folio_mark_dirty(folio);
>
> if (walk) {
> - old_gen = folio_update_gen(folio, new_gen, &vma->flags);
> + old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
> if (old_gen >= 0 && old_gen != new_gen)
> - update_batch_size(walk, folio, old_gen, new_gen);
> + update_batch_size(walk, folio, old_gen, new_gen, file);
> } else if (lru_gen_set_refs(folio, &vma->flags)) {
> old_gen = folio_lru_gen(folio);
> if (old_gen >= 0 && old_gen != new_gen)
>
next prev parent reply other threads:[~2026-08-20 1:52 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-18 5:38 [PATCH 0/7] mm/mglru: clean up folio counters and flag usage Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-18 5:38 ` [PATCH 1/7] mm/memcontrol: make lru_zone_size atomic and simplify sanity check Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 2:05 ` Ridong Chen
2026-08-18 5:38 ` [PATCH 2/7] mm/mglru: introduce helpers for manipulating gen and refs flags Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 9:03 ` Baolin Wang
2026-08-19 9:37 ` Kairui Song
2026-08-19 9:46 ` Baolin Wang
2026-08-19 9:49 ` Kairui Song
2026-08-20 1:43 ` Ridong Chen
2026-08-20 2:04 ` Ridong Chen
2026-08-20 2:05 ` Ridong Chen
2026-08-18 5:38 ` [PATCH 3/7] mm/migrate: copy the referenced state via folio_migrate_refs() Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 10:12 ` Baoquan He
2026-08-18 5:38 ` [PATCH 4/7] mm/mglru: move max_seq read into walk_update_folio Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 9:18 ` Baolin Wang
2026-08-20 2:13 ` Ridong Chen
2026-08-18 5:38 ` [PATCH 5/7] mm/mglru: use explicit tier range in read_ctrl_pos() Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 9:25 ` Baolin Wang
2026-08-19 10:16 ` Baoquan He
2026-08-19 21:24 ` Barry Song
2026-08-20 2:33 ` Ridong Chen
2026-08-20 3:22 ` Kairui Song
2026-08-18 5:38 ` [PATCH 6/7] mm/mglru: fix potential generation folio number leak Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-20 1:52 ` Baolin Wang [this message]
2026-08-20 3:45 ` Kairui Song
2026-08-18 5:38 ` [PATCH 7/7] mm/mglru: improve code readability and harden folio_inc_gen Kairui Song via B4 Relay
2026-08-18 5:38 ` Kairui Song
2026-08-19 21:30 ` Barry Song
2026-08-20 0:53 ` Baoquan He
2026-08-20 0:57 ` Baoquan He
2026-08-20 1:02 ` Baolin Wang
2026-08-20 2:11 ` Kairui Song
2026-08-20 2:27 ` Baoquan He
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5db7dbfe-ec22-4a25-a85d-497e6ed1f8b1@linux.alibaba.com \
--to=baolin.wang@linux.alibaba.com \
--cc=akpm@linux-foundation.org \
--cc=axelrasmussen@google.com \
--cc=baohua@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=cgroups@vger.kernel.org \
--cc=chrisl@kernel.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=kasong@tencent.com \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=qi.zheng@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=ryncsn@gmail.com \
--cc=shakeel.butt@linux.dev \
--cc=vbabka@kernel.org \
--cc=weixugc@google.com \
--cc=yuanchu@google.com \
--cc=yuzhao@google.com \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.