From: sashiko-bot@kernel.org
To: "Barry Song (Xiaomi)" <baohua@kernel.org>, akpm@linux-foundation.org
Cc: axelrasmussen@google.com, baolin.wang@linux.alibaba.com,
baoquan.he@linux.dev, hannes@cmpxchg.org, hui.zhu@linux.dev,
kasong@tencent.com, kunwu.chan@gmail.com, liam@infradead.org,
lianux.mm@gmail.com, linux-mm@kvack.org,
liuxiang.277@bytedance.com, lyugaofei@xiaomi.com,
mhocko@kernel.org, mhocko@suse.com, qi.zheng@linux.dev,
ridong.chen@linux.dev, rppt@kernel.org, ryncsn@gmail.com,
shakeel.butt@linux.dev, surenb@google.com, wangzicheng@honor.com,
weixugc@google.com, yuanchu@google.com, zhangbo56@xiaomi.com,
zhuhui@kylinos.cn, sidchintamaneni@gmail.com
Subject: Re: [PATCH v7 11/16] mm/mglru: Run aging when one type has no populatable folios
Date: Thu, 12 Nov 2026 05:01:06 +0000 [thread overview]
Message-ID: <sashiko-outbox-210419@kernel.org> (raw)
In-Reply-To: <20261111232603.9934-12-baohua@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] mm/mglru: skipped aging in try_to_shrink_lruvec() when zero
folios are scanned
- [Medium] mm/mglru: repeated aging in try_to_shrink_lruvec() when
no_populatable persists
- [Low] mm/mglru: stale max_seq passed to should_run_aging() in
try_to_shrink_lruvec()
--
commit b68a220713e0f1b121c4874a48ac05f55108264a
Author: Barry Song (Xiaomi) <baohua@kernel.org>
mm/mglru: Run aging when one type has no populatable folios
This commit moves the aging check in try_to_shrink_lruvec() after
evict_folio_lists() and passes a no_populatable flag to should_run_aging()
so aging runs when one LRU type makes no scan progress despite remaining
scan budget.
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 085450819ee8e..41dd77652c82f 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -5105,7 +5105,8 @@ static bool lru_gen_imbalanced(struct lruvec *lruvec, struct scan_control *sc,
> }
>
> static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
> - struct scan_control *sc, int swappiness)
> + struct scan_control *sc, int swappiness,
> + bool no_populatable)
> {
> DEFINE_MIN_SEQ(lruvec);
>
> @@ -5117,6 +5118,13 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
> if (sc->priority == DEF_PRIORITY)
> return false;
>
> + /*
> + * Run aging if scan budget remains but no folios
> + * in populatable generations are available to scan.
> + */
> + if (no_populatable)
> + return true;
> +
> /* better to run aging even though eviction is still possible */
> if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq)
> return true;
> @@ -5221,20 +5229,17 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
> lru_gen_prepare_scan(lruvec, sc, memcg, swappiness, nr);
>
> while (nr[LRU_GEN_ANON] > 0 || nr[LRU_GEN_FILE] > 0) {
> + unsigned long orig_file, orig_anon;
> DEFINE_MAX_SEQ(lruvec);
> + bool no_populatable;
>
> if (mem_cgroup_below_min(sc->target_mem_cgroup, memcg)) {
> need_rotate = true;
> break;
> }
>
> - if (should_run_aging(lruvec, max_seq, sc, swappiness)) {
> - trace_mm_mglru_aging(lruvec, sc, swappiness);
> - if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false))
> - need_rotate = true;
> - should_age = true;
> - }
> -
> + orig_anon = nr[LRU_GEN_ANON];
> + orig_file = nr[LRU_GEN_FILE];
> if (!evict_folio_lists(nr, lruvec, sc, swappiness, MIN_LRU_BATCH))
> break;
>
[Severity: High]
Does moving should_run_aging() and try_to_inc_max_seq() after
evict_folio_lists() skip aging whenever evict_folio_lists() scans zero
folios?
mm/vmscan.c:scan_folios() {
...
if (sc->priority == DEF_PRIORITY)
seq = lrugen->max_seq - MIN_NR_GENS;
else if (sc->priority > DEF_PRIORITY / 2)
seq = lrugen->max_seq - 1;
else
seq = lrugen->max_seq;
seq = lruvec_populated_min_seq(lruvec, type, sc->reclaim_idx + 1, seq);
...
}
At DEF_PRIORITY, when only MIN_NR_GENS generations remain
(evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq),
scan_folios() caps seq to max_seq - MIN_NR_GENS, which is below min_seq
and already empty. evict_folio_lists() then returns 0 and breaks out of
the loop before reaching should_run_aging(), making the mandatory
evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq aging
check unreachable at DEF_PRIORITY.
Similarly, at DEF_PRIORITY / 2 < sc->priority < DEF_PRIORITY,
scan_folios() caps seq to max_seq - 1. Whenever all evictable folios for
the budgeted type(s) are in max_seq (for example when both types only have
folios in max_seq, or when only one type has budget left),
evict_folio_lists() returns 0 and breaks before aging can run, leaving
need_rotate false as well so shrink_one() fails to rotate a sizable memcg.
Finally, when sc->priority <= DEF_PRIORITY / 2, running
evict_folio_lists() before should_run_aging() scans directly from max_seq
(where min_gen == max_gen in sort_folio() disables tier protection) before
aging can split the youngest generation, and breaks without aging if the
first batch satisfies should_abort_scan().
> @@ -5248,6 +5253,15 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
> if (root_reclaim(sc) && should_age)
> break;
>
> + no_populatable = (nr[LRU_GEN_ANON] && nr[LRU_GEN_ANON] == orig_anon) ||
> + (nr[LRU_GEN_FILE] && nr[LRU_GEN_FILE] == orig_file);
[Severity: Medium]
Can no_populatable stay true across every MIN_LRU_BATCH iteration and
trigger repeated aging when one type still scans 0 folios after aging?
nr[] is computed once in lru_gen_prepare_scan() and is only decremented by
scanned folios. If scan_folios() continues to return 0 for one type while
the other type makes progress (for instance when sc->priority <=
DEF_PRIORITY / 2 already scans up to max_seq, when max_seq has no eligible
folios of that type, or when walk_mm() promotes all referenced folios into
the new youngest generation), nr[type] remains equal to orig_anon or
orig_file. In cgroup reclaim (!root_reclaim(sc)), the loop does not break
on should_age, so should_run_aging() returns true and calls
try_to_inc_max_seq() on every 64-page batch of the other type; in
root_reclaim(sc), it runs unnecessary aging and aborts scanning the
populated type after one extra batch.
> + if (should_run_aging(lruvec, max_seq, sc, swappiness, no_populatable)) {
> + trace_mm_mglru_aging(lruvec, sc, swappiness);
> + if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false))
> + need_rotate = true;
> + should_age = true;
> + }
[Severity: Low]
Can should_run_aging() see a stale max_seq alongside a fresh min_seq here?
DEFINE_MAX_SEQ(lruvec) snapshots max_seq at the start of the loop before
evict_folio_lists(), whereas should_run_aging() reads min_seq after
evict_folio_lists() finishes. If a concurrent reclaimer advances max_seq
and min_seq during eviction, should_run_aging() compares the new min_seq
against the old max_seq, which can falsely trigger the aging branch, fail
try_to_inc_max_seq() on the stale max_seq, and set should_age = true so
root_reclaim(sc) aborts on the next pass.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261111232603.9934-1-baohua@kernel.org?part=11
next prev parent reply other threads:[~2026-11-12 5:01 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 23:25 [PATCH v7 00/16] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-11-11 23:25 ` [PATCH v7 01/16] mm/mglru: report evictable sizes per type Barry Song (Xiaomi)
2026-11-11 23:25 ` [PATCH v7 02/16] mm/mglru: extract lruvec_populated_min_seq() helper Barry Song (Xiaomi)
2026-11-12 5:00 ` sashiko-bot
2026-11-12 5:11 ` Barry Song
2026-11-11 23:25 ` [PATCH v7 03/16] mm/mglru: reclaim from the oldest populated gen Barry Song (Xiaomi)
2026-11-12 5:00 ` sashiko-bot
2026-11-11 23:25 ` [PATCH v7 04/16] mm/mglru: lift the forced protection of reclaimer Barry Song (Xiaomi)
2026-11-12 5:00 ` sashiko-bot
2026-11-11 23:25 ` [PATCH v7 05/16] mm/mglru: skip zones above reclaim_idx instead of rotating them Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-12 6:36 ` Barry Song
2026-11-11 23:25 ` [PATCH v7 06/16] mm/mglru: return for unavailable anon reclaim earlier Barry Song (Xiaomi)
2026-11-11 23:25 ` [PATCH v7 07/16] mm/mglru: balance anon and file scan budget by refault rate Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-11 23:25 ` [PATCH v7 08/16] mm/mglru: lift youngest gen protection for higher priority Barry Song (Xiaomi)
2026-11-11 23:25 ` [PATCH v7 09/16] mm/mglru: Avoid placing rejected folios below populated gen Barry Song (Xiaomi)
2026-11-11 23:25 ` [PATCH v7 10/16] mm/mglru: Run aging when pages are rare outside the youngest gen Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-12 5:47 ` Barry Song
2026-11-11 23:25 ` [PATCH v7 11/16] mm/mglru: Run aging when one type has no populatable folios Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot [this message]
2026-11-12 11:11 ` Barry Song
2026-11-11 23:25 ` [PATCH v7 12/16] mm/mglru: Restrict eviction accounting to eligible zones Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-12 9:24 ` Barry Song
2026-11-11 23:26 ` [PATCH v7 13/16] mm/mglru: Reduce LRU lock contention by shrinking folio batches in inc_min_seq Barry Song (Xiaomi)
2026-11-11 23:26 ` [PATCH v7 14/16] mm/mglru: Avoid prolonged anon reclaim with swappiness=200 Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-12 10:32 ` Barry Song
2026-11-12 10:46 ` Kairui Song
2026-11-11 23:26 ` [PATCH v7 15/16] mm/mglru: Make swappiness=1 file reclaim more aggressive Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-11 23:26 ` [PATCH v7 16/16] mm/mglru: Avoid scan anon while swapcache is low for GFP_NOIO allocation Barry Song (Xiaomi)
2026-11-12 5:01 ` sashiko-bot
2026-11-12 11:29 ` Barry Song
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-210419@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=hannes@cmpxchg.org \
--cc=hui.zhu@linux.dev \
--cc=kasong@tencent.com \
--cc=kunwu.chan@gmail.com \
--cc=liam@infradead.org \
--cc=lianux.mm@gmail.com \
--cc=linux-mm@kvack.org \
--cc=liuxiang.277@bytedance.com \
--cc=lyugaofei@xiaomi.com \
--cc=mhocko@kernel.org \
--cc=mhocko@suse.com \
--cc=qi.zheng@linux.dev \
--cc=ridong.chen@linux.dev \
--cc=rppt@kernel.org \
--cc=ryncsn@gmail.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=sidchintamaneni@gmail.com \
--cc=surenb@google.com \
--cc=wangzicheng@honor.com \
--cc=weixugc@google.com \
--cc=yuanchu@google.com \
--cc=zhangbo56@xiaomi.com \
--cc=zhuhui@kylinos.cn \
/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.