From: sashiko-bot@kernel.org
To: "Baoquan He" <baoquan.he@linux.dev>, linux-mm@kvack.org
Cc: akpm@linux-foundation.org, david@kernel.org, kasong@tencent.com,
baohua@kernel.org, baolin.wang@linux.alibaba.com,
qi.zheng@linux.dev, shakeel.butt@linux.dev,
axelrasmussen@google.com, yuanchu@google.com, weixugc@google.com,
"Baoquan He" <hebaoquan@kylinos.cn>,
"Kairui Song" <ryncsn@gmail.com>
Subject: Re: [PATCH v3 3/4] mm/mglru: skip cold PUD subtrees during aging
Date: Sat, 14 Nov 2026 03:42:21 +0000 [thread overview]
Message-ID: <sashiko-outbox-214125@kernel.org> (raw)
In-Reply-To: <20261113084953.412919-4-baoquan.he@linux.dev>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] mm/mglru: walk_pmd_range() marks PUD young on non-leaf PMD
accessed bit
- [Medium] mm/mglru: walk_pmd_range() marks PUD young for THP before
memcg and notifier checks
- [Medium] mm/mglru: PUDs with PMD-mapped THPs are suppressed
indefinitely once skipped
--
commit 31572808d36982038b4b945e98794001c99a5d21
Author: Baoquan He <hebaoquan@kylinos.cn>
mm/mglru: skip cold PUD subtrees during aging
This commit introduces a PUD-level Bloom filter keyed by (mm, PUD-aligned
address) to skip PUD subtrees that had no young pages in the previous
generation during MGLRU aging walks, and updates it from the rmap feedback
path in lru_gen_look_around().
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index a0332586b3d83..79d533ebfd108 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
[ ... ]
> @@ -3730,10 +3732,11 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
> *first = -1;
> }
>
> -static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
> +static bool walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
> struct mm_walk *args)
> {
> int i;
> + bool young = false;
> pmd_t *pmd;
> unsigned long next;
> unsigned long addr;
> @@ -3770,8 +3773,10 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
>
> walk->mm_stats[MM_LEAF_TOTAL]++;
>
> - if (pfn != -1)
> + if (pfn != -1) {
> walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
> + young = true;
> + }
> continue;
> }
[Severity: Medium]
Does setting young = true as soon as get_pmd_pfn() != -1 mark the PUD young
before checking memcg ownership or MMU notifiers?
get_pmd_pfn() only checks that the PFN is on pgdat and that pmd_young() or
mm_has_notifiers() is true, while the memcg check in get_pfn_folio() and
the notifier young check in pmdp_test_and_clear_young_notify() are deferred
to walk_pmd_range_locked(). A PUD with PMD-mapped THPs belonging to
another memcg on the same node, or cold THPs in an mm with MMU notifiers,
will therefore be recorded in BLOOM_FILTER_PUD on every generation and
never skipped.
> @@ -3781,6 +3786,7 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
> continue;
>
> walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
> + young = true;
> }
>
> if (!walk->force_scan &&
> @@ -3793,6 +3799,7 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
> continue;
>
> walk->mm_stats[MM_NONLEAF_ADDED]++;
> + young = true;
>
> /* carry over to the next generation */
> update_bloom_filter(mm_state, BLOOM_FILTER_PMD, walk->seq + 1, pmd + i);
[Severity: Medium]
Should young = true be omitted from the non-leaf pmd_young(val) block?
When should_clear_pmd_young() is true, setting young = true there happens
before checking BLOOM_FILTER_PMD or calling walk_pte_range(). Because CPU
page table walks set the accessed bit on non-leaf PMDs regardless of which
node or memcg owns the underlying PTEs, any accessed non-leaf PMD will
cause walk_pmd_range() to return true and carry the PUD into
BLOOM_FILTER_PUD even when every PMD in the subtree fails the PMD Bloom
filter or has no folios in walk->lruvec. The young = true after
walk_pte_range() already handles non-leaf PMDs that pass suitable_to_scan().
> @@ -3802,6 +3809,8 @@ static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
>
> if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
> goto restart;
> +
> + return young;
> }
>
> static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,
> @@ -3812,6 +3821,7 @@ static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,
> unsigned long addr;
> unsigned long next;
> struct lru_gen_mm_walk *walk = args->private;
> + struct lru_gen_mm_state *mm_state = get_mm_state(walk->lruvec);
>
> VM_WARN_ON_ONCE(p4d_leaf(*p4d));
>
> @@ -3819,13 +3829,29 @@ static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,
> restart:
> for (i = pud_index(start), addr = start; addr != end; i++, addr = next) {
> pud_t val = pudp_get(pud + i);
> + void *pud_key;
>
> next = pud_addr_end(addr, end);
>
> if (!pud_present(val) || WARN_ON_ONCE(pud_leaf(val)))
> continue;
>
> - walk_pmd_range(pud + i, addr, next, args);
> + /*
> + * Use (mm, PUD-aligned address) as the filter key instead of
> + * the page table pointer. It is stable across the aging walk
> + * and the rmap feedback path, and avoids walking the page
> + * tables in lru_gen_look_around().
> + */
> + pud_key = (void *)((unsigned long)args->mm ^ (addr & PUD_MASK));
> +
> + /* Skip a subtree that had nothing young for this lruvec last gen */
> + if (!walk->force_scan &&
> + !test_bloom_filter(mm_state, BLOOM_FILTER_PUD, walk->seq, pud_key))
> + continue;
> +
> + if (walk_pmd_range(pud + i, addr, next, args))
> + update_bloom_filter(mm_state, BLOOM_FILTER_PUD, walk->seq + 1,
> + pud_key);
[Severity: Medium]
Once a PUD is absent from BLOOM_FILTER_PUD, can PMD-mapped THPs inside it
ever be scanned by page-table aging again without force_scan?
Previously, BLOOM_FILTER_PMD was checked after pmd_trans_huge() in
walk_pmd_range(), so PMD-mapped THPs were never gated by a Bloom filter.
Gating walk_pmd_range() on BLOOM_FILTER_PUD skips the entire PUD when
pud_key is not present, and iterate_mm_list() clears the next generation's
filter via reset_bloom_filter(mm_state, BLOOM_FILTER_PUD, walk->seq + 1).
The only rmap feedback that populates BLOOM_FILTER_PUD is
lru_gen_look_around(), which folio_referenced_one() only calls when
pvmw.pte is non-NULL:
mm/rmap.c:folio_referenced_one() {
...
if (lru_gen_enabled() && !lru_gen_switching() && pvmw.pte &&
pte_present(pteval)) {
if (lru_gen_look_around(&pvmw, nr))
referenced++;
} else if (pvmw.pte) {
...
} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
if (pmdp_clear_flush_young_notify(vma, address,
pvmw.pmd))
referenced++;
...
}
For PMD-mapped THPs (pvmw.pte == NULL), rmap eviction checks never update
BLOOM_FILTER_PUD, so any PUD that goes cold for one generation or receives
newly faulted or collapsed THPs after the initial force_scan will be
skipped by aging indefinitely.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113084953.412919-1-baoquan.he@linux.dev?part=3
next prev parent reply other threads:[~2026-11-14 3:42 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 8:49 [PATCH v3 0/4] skip empty PUD subtrees during aging with a PUD-level Bloom filter Baoquan He
2026-11-13 8:49 ` [PATCH v3 1/4] mm/mglru: add MM_WALK_EMPTY stats and tracepoint Baoquan He
2026-11-13 8:53 ` Baoquan He
2026-11-14 3:42 ` sashiko-bot
2026-11-13 8:49 ` [PATCH v3 2/4] mm/mglru: add PUD-level Bloom filter state and generic helpers Baoquan He
2026-11-13 8:49 ` [PATCH v3 3/4] mm/mglru: skip cold PUD subtrees during aging Baoquan He
2026-11-14 3:42 ` sashiko-bot [this message]
2026-11-13 8:49 ` [PATCH v3 4/4] mm/mglru: count PUD subtrees skipped by the PUD-level filter 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=sashiko-outbox-214125@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=axelrasmussen@google.com \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=baoquan.he@linux.dev \
--cc=david@kernel.org \
--cc=hebaoquan@kylinos.cn \
--cc=kasong@tencent.com \
--cc=linux-mm@kvack.org \
--cc=qi.zheng@linux.dev \
--cc=ryncsn@gmail.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=weixugc@google.com \
--cc=yuanchu@google.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox