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 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.