Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Barry Song <baohua@kernel.org>
To: kunwu.chan@gmail.com
Cc: akpm@linux-foundation.org, axelrasmussen@google.com,
	baohua@kernel.org, baolin.wang@linux.alibaba.com,
	baoquan.he@linux.dev, david@kernel.org, hannes@cmpxchg.org,
	hui.zhu@linux.dev, liam@infradead.org, lianux.mm@gmail.com,
	linux-mm@kvack.org, liuxiang.277@bytedance.com, ljs@kernel.org,
	lyugaofei@xiaomi.com, mhocko@kernel.org, mhocko@suse.com,
	qi.zheng@linux.dev, ridong.chen@linux.dev, rppt@kernel.org,
	ryncsn@gmail.com, sashiko-reviews@lists.linux.dev,
	shakeel.butt@linux.dev, surenb@google.com, wangzicheng@honor.com,
	weixugc@google.com, yuanchu@google.com, zhangbo56@xiaomi.com,
	zhuhui@kylinos.cn
Subject: Re: [RFC PATCH v5 05/14] mm/mglru: skip zones above reclaim_idx instead of rotating them
Date: Mon,  9 Nov 2026 12:23:48 +0800	[thread overview]
Message-ID: <20261109042348.51689-1-baohua@kernel.org> (raw)
In-Reply-To: <CAN_Opa-AScm9=EeXO_Uy8VjbQ=bqY9P++rFP8H1RhUB4dOt6xw@mail.gmail.com>

On Mon, Nov 2, 2026 at 6:00 PM KunWu Chan <kunwu.chan@gmail.com> wrote:
>
> On Sun, Oct 25, 2026 at 12:19 AM Kairui Song <ryncsn@gmail.com> wrote:
> >
> > On Sat, Oct 24, 2026 at 6:03 AM <sashiko-bot@kernel.org> wrote:
> > >
> > > 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?
> >
> > Yeah it can stall aging, but this series lifted the restriction of
> > MIN_NR_GEN on eviction, and eviction will start the the last populated
> > gen of a zone instead of min_seq, and we have more proactive aging
> > when the gens are imbalanced, so this won't be a problem, it won't
> > cause OOM. I'm not sure if it will cause any performance issue though,
> > if this is concerning we can keep the old behavior, or trigger aging
> > when the populated gen that satisfy a reclain is below (MIN_NR_GEN -
> > 1) or imbalanced, this seems a better solution to me, still avoid a
> > lot of redundant work while ensure there is no reclaim effenciency
> > issue. We already have the right helper to do so in this series, so
> > seem not hard or dirty.
>
> Thanks, that makes sense regarding OOM.
> Could lru_gen_imbalanced() trigger aging soon enough when few
> populated generations are eligible for reclaim, or could reclaim
> efficiency suffer while min_seq remains pinned?
>
> Would the additional trigger you suggested for this case be worthwhile?

Hi Kunwu,

Thanks very much for reviewing this series.
I'd really appreciate it if you could keep going :-)

I'm trying an incremental patch like this while rebasing the
series on top of mm-new. It may resolve the problem.

diff --git a/mm/trace_vmscan.h b/mm/trace_vmscan.h
index 565c56428364..9dafdb5bd3ce 100644
--- a/mm/trace_vmscan.h
+++ b/mm/trace_vmscan.h
@@ -488,9 +488,9 @@ TRACE_EVENT(mm_mglru_aging,
 			unsigned long seq = max_seq - MAX_NR_GENS + 1 + i;
 
 			__entry->nr_anon[i] =
-				lruvec_gen_size(lrugen, LRU_GEN_ANON, seq);
+				lruvec_gen_size(lrugen, LRU_GEN_ANON, seq, MAX_NR_ZONES);
 			__entry->nr_file[i] =
-				lruvec_gen_size(lrugen, LRU_GEN_FILE, seq);
+				lruvec_gen_size(lrugen, LRU_GEN_FILE, seq, MAX_NR_ZONES);
 		}
 		__entry->nid = lruvec_pgdat(lruvec)->node_id;
 		__entry->priority = sc->priority;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 4ca04812eea3..fa0245a07caa 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4217,7 +4217,7 @@ static void lruvec_evictable_size(struct lruvec *lruvec, struct scan_control *sc
 		if (type == LRU_GEN_ANON && !can_reclaim_anon_pages(memcg, nid, sc))
 			continue;
 		for (seq = min_seq[type]; seq <= max_seq; seq++)
-			nr[type] += lruvec_gen_size(lrugen, type, seq);
+			nr[type] += lruvec_gen_size(lrugen, type, seq, sc->reclaim_idx + 1);
 	}
 }
 
@@ -5082,9 +5082,9 @@ static bool lru_gen_imbalanced(struct lruvec *lruvec, struct scan_control *sc,
 	for_each_evictable_type(type, swappiness) {
 		unsigned long seq;
 
-		youngest[type] = lruvec_gen_size(lrugen, type, max_seq);
+		youngest[type] = lruvec_gen_size(lrugen, type, max_seq, sc->reclaim_idx + 1);
 		for (seq = min_seq[type]; seq < max_seq; seq++)
-			old[type] += lruvec_gen_size(lrugen, type, seq);
+			old[type] += lruvec_gen_size(lrugen, type, seq, sc->reclaim_idx + 1);
 	}
 
 	/* the vast majority of pages sit in the youngest gen */
@@ -5835,7 +5835,7 @@ static int lru_gen_seq_show(struct seq_file *m, void *v)
 			char mark = full && seq < min_seq[type] ? 'x' : ' ';
 
 			seq_printf(m, " %10lu%c",
-				   lruvec_gen_size(lrugen, type, seq), mark);
+				   lruvec_gen_size(lrugen, type, seq, MAX_NR_ZONES), mark);
 		}
 
 		seq_putc(m, '\n');
diff --git a/mm/vmscan.h b/mm/vmscan.h
index c6b583fc7493..2599d7c614ac 100644
--- a/mm/vmscan.h
+++ b/mm/vmscan.h
@@ -119,12 +119,13 @@ struct scan_control {
 #ifdef CONFIG_LRU_GEN
 /* The number of pages of @type in the generation @seq, summed over all zones */
 static inline unsigned long lruvec_gen_size(struct lru_gen_folio *lrugen,
-					    int type, unsigned long seq)
+					    int type, unsigned long seq,
+					    int max_zone)
 {
 	int gen = lru_gen_from_seq(seq);
 	unsigned long size = 0;
 
-	for (int zone = 0; zone < MAX_NR_ZONES; zone++)
+	for (int zone = 0; zone < max_zone; zone++)
 		size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
 
 	return size;

>
> Thanks,
> Kunwu

Thanks,
Barry


  reply	other threads:[~2026-11-09  4:24 UTC|newest]

Thread overview: 36+ 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
2026-10-24 16:19     ` Kairui Song
2026-11-02 10:00       ` KunWu Chan
2026-11-09  4:23         ` Barry Song [this message]
2026-10-22 22:01 ` [RFC PATCH v5 06/14] mm/mglru: return for unavailable anon reclaim earlier Barry Song (Xiaomi)
2026-11-03  2:19   ` KunWu Chan
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-11-03  8:40       ` KunWu Chan
2026-10-22 22:01 ` [RFC PATCH v5 09/14] mm/mglru: Avoid placing rejected folios below populated gen Barry Song (Xiaomi)
2026-11-03  9:55   ` KunWu Chan
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
2026-11-04 21:53   ` 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=20261109042348.51689-1-baohua@kernel.org \
    --to=baohua@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=axelrasmussen@google.com \
    --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=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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox