All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Barry Song (Xiaomi)" <baohua@kernel.org>,
	akpm@linux-foundation.org, linux-mm@kvack.org
Cc: hui.zhu@linux.dev, axelrasmussen@google.com,
	baolin.wang@linux.alibaba.com, david@kernel.org,
	hannes@cmpxchg.org, ljs@kernel.org, mhocko@kernel.org,
	qi.zheng@linux.dev, ryncsn@gmail.com, shakeel.butt@linux.dev,
	weixugc@google.com, yuanchu@google.com, zhuhui@kylinos.cn,
	baoquan.he@linux.dev, liuxiang.277@bytedance.com,
	wangzicheng@honor.com, lianux.mm@gmail.com, kasong@tencent.com,
	liam@infradead.org, surenb@google.com, mhocko@suse.com,
	rppt@kernel.org, zhangbo56@xiaomi.com, lyugaofei@xiaomi.com,
	ridong.chen@linux.dev, kunwu.chan@gmail.com
Subject: Re: [RFC PATCH v5 05/14] mm/mglru: skip zones above reclaim_idx instead of rotating them
Date: Fri, 23 Oct 2026 22:03:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-183271@kernel.org> (raw)
In-Reply-To: <20261022220203.91911-6-baohua@kernel.org>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] mm/mglru: pinned min_seq in higher zones stalls aging in scan_folios()
--

commit 1b2f683d6b8bd1e171eb9677315ba8a3d9431b9b
Author: Kairui Song <kasong@tencent.com>

mm/mglru: skip zones above reclaim_idx instead of rotating them

This commit restricts scan_folios() and lruvec_populated_min_seq() during
eviction to zones at or below sc->reclaim_idx, and removes the ineligible-zone
rotation branch from sort_folio(). try_to_inc_min_seq() continues to check all
zones up to MAX_NR_ZONES before advancing min_seq.

> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index a365a4bc8c988..09ea72b354a56 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -4132,7 +4132,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)
>  
>  /* Similar to lruvec_evictable_size, return the oldest populated seq of the type */
>  static unsigned long lruvec_populated_min_seq(struct lruvec *lruvec, int type,
> -					      unsigned long max_seq)
> +					      int zone_limit, unsigned long max_seq)
>  {
>  	int gen, zone;
>  	unsigned long min_seq;
> @@ -4143,7 +4143,7 @@ static unsigned long lruvec_populated_min_seq(struct lruvec *lruvec, int type,
>  	min_seq = READ_ONCE(lrugen->min_seq[type]);
>  	while (min_seq <= max_seq) {
>  		gen = lru_gen_from_seq(min_seq);
> -		for (zone = 0; zone < MAX_NR_ZONES; zone++) {
> +		for (zone = 0; zone < zone_limit; zone++) {
>  			if (!list_empty(&lrugen->folios[gen][type][zone]))
>  				return min_seq;
>  		}
> @@ -4166,7 +4166,7 @@ static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)
>  	/* Requires at least MIN_NR_GENS gens for basic functionality */
>  	seq = lrugen->max_seq - MIN_NR_GENS;
>  	for_each_evictable_type(type, swappiness) {
> -		min_seq[type] = lruvec_populated_min_seq(lruvec, type, seq + 1);
> +		min_seq[type] = lruvec_populated_min_seq(lruvec, type, MAX_NR_ZONES, seq + 1);
>  		if (min_seq[type] > lrugen->min_seq[type])
>  			seq_inc_flag = true;
>  	}

[ ... ]

> @@ -4863,10 +4864,8 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
>  	 * longer be promoted or protected, so just isolate them. The one
>  	 * exception is lru_gen_reparent_memcg(), which is handled above.
>  	 */
> -	if (min_gen == max_gen) {
> -		VM_WARN_ON_ONCE(zone > sc->reclaim_idx);
> +	if (min_gen == max_gen)
>  		return false;
> -	}
>  
>  	/* protected */
>  	if (tier > tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) {
> @@ -4883,13 +4882,6 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
>  		return true;
>  	}
>  
> -	/* ineligible */
> -	if (zone > sc->reclaim_idx) {
> -		gen = folio_inc_gen(lruvec, folio, min_gen);
> -		list_move_tail(&folio->lru, &lrugen->folios[gen][type][zone]);
> -		return true;
> -	}
> -
>  	return false;
>  }
>  
> @@ -4921,16 +4913,17 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
>  		       struct scan_control *sc, int type, int tier,
>  		       struct list_head *list, int *isolatedp)
>  {
> -	int i;
>  	enum node_stat_item item;
> +	int zone_idx;
>  	int sorted = 0;
>  	int scanned = 0;
>  	int isolated = 0;
>  	int skipped = 0;
>  	int gen, max_gen;
> -	unsigned long min_seq;
> +	unsigned long seq;
>  	unsigned long remaining = nr_to_scan;
>  	struct lru_gen_folio *lrugen = &lruvec->lrugen;
> +	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
>  	DEFINE_MAX_SEQ(lruvec);
>  
>  	VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
> @@ -4942,24 +4935,22 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
>  	 * when under pressure to respect swappiness more and reduce IO cost.
>  	 */
>  	if (sc->priority == DEF_PRIORITY)
> -		min_seq = lrugen->min_seq[type];
> +		seq = lrugen->min_seq[type];
>  	else
> -		min_seq = lruvec_populated_min_seq(lruvec, type, lrugen->max_seq);
> +		seq = lrugen->max_seq;
>  
> -	gen = lru_gen_from_seq(min_seq);
> +	seq = lruvec_populated_min_seq(lruvec, type, sc->reclaim_idx + 1, seq);
> +	gen = lru_gen_from_seq(seq);
>  	max_gen = lru_gen_from_seq(max_seq);
>  
> -	for (i = MAX_NR_ZONES; i > 0; i--) {
> +	/* Scan eligible zones in descending order. */
> +	for (zone_idx = sc->reclaim_idx; zone_idx >= 0; zone_idx--) {

[Severity: High]
During zone-constrained reclaim in mm/vmscan.c (where sc->reclaim_idx is below
MAX_NR_ZONES - 1 and zones above sc->reclaim_idx are populated, such as
GFP_DMA32 reclaim with a populated ZONE_NORMAL or GFP_KERNEL reclaim with a
populated ZONE_MOVABLE), can keeping MAX_NR_ZONES in try_to_inc_min_seq()
while removing the ineligible-zone rotation from sort_folio() stall aging and
memcg rotation?

Previously, scan_folios() visited all zones and sort_folio() rotated folios in
zones above sc->reclaim_idx from min_gen to min_gen + 1. Once the oldest
generation was emptied across all zones, try_to_inc_min_seq() incremented
lrugen->min_seq[type] from max_seq - 3 (when MAX_NR_GENS generations exist) to
max_seq - 2, allowing should_run_aging() to trigger try_to_inc_max_seq().

With scan_folios() restricted to zones 0..sc->reclaim_idx, folios in zones
above sc->reclaim_idx remain in min_seq[type]. Because try_to_inc_min_seq()
still passes MAX_NR_ZONES to lruvec_populated_min_seq(), lrugen->min_seq[type]
stays pinned at max_seq - 3 even after min_seq[type] is completely drained in
eligible zones 0..sc->reclaim_idx.

In this commit, at sc->priority == DEF_PRIORITY, scan_folios() caps the
lruvec_populated_min_seq() search bound at seq = lrugen->min_seq[type]. Once
min_seq[type] is empty in zones 0..sc->reclaim_idx, lruvec_populated_min_seq()
still returns the pinned min_seq[type], so scan_folios() scans empty lists and
returns 0 at DEF_PRIORITY.

A later commit in this series ("mm/mglru: lift youngest gen protection for
higher priority") changes the DEF_PRIORITY search bound in scan_folios() to
lrugen->max_seq - 1, and another commit ("mm/mglru: Run aging when pages are
rare outside the youngest gen") adds lru_gen_imbalanced() to
should_run_aging(). However, both should_run_aging() and shrink_one() still
rely on state across all zones:

mm/vmscan.c:should_run_aging() {
	DEFINE_MIN_SEQ(lruvec);

	/* have to run aging, since eviction is not possible anymore */
	if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq)
		return true;

	/* try to avoid aging, do gentle reclaim at the default priority */
	if (sc->priority == DEF_PRIORITY)
		return false;

	/* better to run aging even though eviction is still possible */
	return evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq;
}

mm/vmscan.c:shrink_one() {
	...
	need_rotate = try_to_shrink_lruvec(lruvec, sc);
	...
	if (need_rotate && mem_cgroup_online(memcg))
		return MEMCG_LRU_YOUNG;

	if (!need_rotate && lruvec_is_sizable(lruvec, sc))
		return 0;
}

When lrugen->min_seq[type] remains pinned at max_seq - 3 by folios in zones
above sc->reclaim_idx (and lru_gen_imbalanced() / lruvec_is_sizable() sum
nr_pages across all MAX_NR_ZONES):

1. should_run_aging() sees min_seq + MIN_NR_GENS < max_seq and does not run
   try_to_inc_max_seq() (or inc_min_seq() inside inc_max_seq()), stalling
   page-table aging.

2. Once eligible zones 0..sc->reclaim_idx drain their older generations and
   lruvec_populated_min_seq() advances seq to max_seq in scan_folios(),
   sort_folio() hits min_gen == max_gen and isolates folios from the youngest
   generation without tier or reference protection.

3. If a memcg has no folios in eligible zones 0..sc->reclaim_idx but has
   folios in zones above sc->reclaim_idx, try_to_shrink_lruvec() returns
   need_rotate = false while lruvec_is_sizable() returns true, so shrink_one()
   returns 0 instead of rotating the memcg.

Should should_run_aging() and the size checks also account for sc->reclaim_idx,
or how should min_seq and max_seq advance when older generations are only
populated in zones above sc->reclaim_idx?

[ ... ]

([PVF1])

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261022220203.91911-1-baohua@kernel.org?part=5


  reply	other threads:[~2026-10-23 22:03 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22 22:01 [RFC PATCH v5 00/14] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-10-22 22:01 ` [RFC PATCH v5 01/14] mm/mglru: report evictable sizes per type Barry Song (Xiaomi)
2026-10-27  8:51   ` KunWu Chan
2026-10-22 22:01 ` [RFC PATCH v5 02/14] mm/mglru: extract lruvec_populated_min_seq() helper Barry Song (Xiaomi)
2026-10-27  9:39   ` KunWu Chan
2026-10-22 22:01 ` [RFC PATCH v5 03/14] mm/mglru: reclaim from the oldest populated gen Barry Song (Xiaomi)
2026-10-27  9:56   ` KunWu Chan
2026-10-22 22:01 ` [RFC PATCH v5 04/14] mm/mglru: lift the forced protection of reclaimer Barry Song (Xiaomi)
2026-10-28  4:14   ` KunWu Chan
2026-10-22 22:01 ` [RFC PATCH v5 05/14] mm/mglru: skip zones above reclaim_idx instead of rotating them Barry Song (Xiaomi)
2026-10-23 22:03   ` sashiko-bot [this message]
2026-10-24 16:19     ` Kairui Song
2026-10-22 22:01 ` [RFC PATCH v5 06/14] mm/mglru: return for unavailable anon reclaim earlier Barry Song (Xiaomi)
2026-10-22 22:01 ` [RFC PATCH v5 07/14] mm/mglru: balance anon and file scan budget by refault rate Barry Song (Xiaomi)
2026-10-23 22:03   ` sashiko-bot
2026-10-24 16:24     ` Kairui Song
2026-10-26  7:34       ` Barry Song
2026-10-22 22:01 ` [RFC PATCH v5 08/14] mm/mglru: lift youngest gen protection for higher priority Barry Song (Xiaomi)
2026-10-23 22:03   ` sashiko-bot
2026-10-24 18:08     ` Kairui Song
2026-10-22 22:01 ` [RFC PATCH v5 09/14] mm/mglru: Avoid placing rejected folios below populated gen Barry Song (Xiaomi)
2026-10-22 22:01 ` [RFC PATCH v5 10/14] mm/mglru: Run aging when pages are rare outside the youngest gen Barry Song (Xiaomi)
2026-10-22 22:02 ` [RFC PATCH v5 11/14] mm/mglru: Reduce LRU lock contention by shrinking folio batches in inc_min_seq Barry Song (Xiaomi)
2026-10-22 22:02 ` [RFC PATCH v5 12/14] mm/mglru: Avoid prolonged anon reclaim with swappiness=200 Barry Song (Xiaomi)
2026-10-22 22:02 ` [RFC PATCH v5 13/14] mm/mglru: Make swappiness=1 file reclaim more aggressive Barry Song (Xiaomi)
2026-10-22 22:02 ` [RFC PATCH v5 14/14] mm/mglru: Avoid scan anon while swapcache is low for GFP_NOIO allocation Barry Song (Xiaomi)
2026-10-23 22:03   ` sashiko-bot
2026-10-25  1:31     ` Bo Zhang
2026-10-26  8:43       ` KunWu Chan
2026-10-24 16:10 ` [RFC PATCH v5 00/14] mm: mglru: fix swappiness behavior Kairui 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-183271@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=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=ljs@kernel.org \
    --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=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.