Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Barry Song (Xiaomi)" <baohua@kernel.org>
To: ryncsn@gmail.com
Cc: akpm@linux-foundation.org, axelrasmussen@google.com,
	baohua@kernel.org, baolin.wang@linux.alibaba.com,
	baoquan.he@linux.dev, hannes@cmpxchg.org, hui.zhu@linux.dev,
	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,
	sashiko-bot@kernel.org, sashiko-reviews@lists.linux.dev,
	shakeel.butt@linux.dev, sidchintamaneni@gmail.com,
	surenb@google.com, wangzicheng@honor.com, weixugc@google.com,
	yuanchu@google.com, zhangbo56@xiaomi.com, zhuhui@kylinos.cn
Subject: Re: [PATCH v9 11/16] mm/mglru: Run aging when one type is drained
Date: Mon, 16 Nov 2026 06:00:07 +0800	[thread overview]
Message-ID: <20261115220007.89063-1-baohua@kernel.org> (raw)
In-Reply-To: <CAMgjq7DiH1SJCR0DMwf=16cxXO2PZcjSuD41sO7_2hOcXuPUQQ@mail.gmail.com>


On Mon, Nov 16, 2026 at 2:07 AM Kairui Song <ryncsn@gmail.com> wrote:
>
> On Sun, Nov 15, 2026 at 5:44 AM Barry Song (Xiaomi) <baohua@kernel.org> wrote:
> >
> > On Sun, Nov 15, 2026 at 4:50 AM <sashiko-bot@kernel.org> wrote:
> > >
> > > Thank you for your contribution! Sashiko AI review found 2 potential
> > > issues to consider:
> > > - [Medium] mm/mglru: scan_folios() scans seq below min_seq at
> > >   DEF_PRIORITY
> >
> > I guess reparenting could be a corner case where it's difficult to
> > properly preserve the cold/hot ordering.
>
> I think we don't need to care about hotness for reparenting, MGLRU's
> reparent just forcefully squashes the old gen into the parent's gen,
> and most likely in the wrong window order, the hotness info is already
> severely skewed; there's no way to cover that until we run the aging.
> In fact I think we should patch reparent instead, maybe later,
> squashing everything into the oldest generation seems a more
> reasonable solution for reparenting?

Right. The current reparenting behavior is already quite odd,
given that generation numbers wrap around. A larger generation
number doesn't necessarily mean a younger or older generation,
so the ordering can already be completely disrupted.

static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec *parent_lruvec,
                                     int zone, int type)
{
        child_lrugen = &child_lruvec->lrugen;
        parent_lrugen = &parent_lruvec->lrugen;

        for (gen = 0; gen < MAX_NR_GENS; gen++) {
                long nr_pages = child_lrugen->nr_pages[gen][type][zone];
                int child_lru_active = lru_gen_is_active(child_lruvec, gen) ? LRU_ACTIVE : 0;
                int parent_lru_active = lru_gen_is_active(parent_lruvec, gen) ? LRU_ACTIVE : 0;

                /* Assuming that child pages are colder than parent pages */
                list_splice_tail_init(&child_lrugen->folios[gen][type][zone],
                                      &parent_lrugen->folios[gen][type][zone]);
                ... 
        }   
}

I think it makes perfect sense to simplify the code by moving
all child folios to the parent's min_seq, especially since
there's already a comment saying:

"Assuming that child pages are colder than parent pages"

And I think the current active/inactive counting is absolutely
wrong for reparenting:

static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
{
        unsigned long max_seq = lruvec->lrugen.max_seq;

        VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);

        /* see the comment on MIN_NR_GENS */
        return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1); 
}

Considering that the parent memcg has only two generations,
some child folios may end up in lru_gen_from_seq(max_seq + 1),
which is also lru_gen_from_seq(min_seq - 2), while others may
end up in lru_gen_from_seq(max_seq + 2), which is also
lru_gen_from_seq(min_seq - 1), due to the wraparound.

In that case, wouldn't max_seq and max_seq - 1 effectively
represent "inactive", while lru_gen_from_seq(max_seq + 1)
and lru_gen_from_seq(max_seq + 2) represent "active"?

This makes your idea of moving all child folios to the
parent memcg's oldest generation even more compelling!

I drafted the following code based on your idea. It's unrelated
to this series and would be a separate change:
(also I found another bug, __update_lru_size() is only called
while parent/child active status is different -  this will
make both parent/child lru statistics wrong, so i dropped
it)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index a5b8244d4768..582bce575cec 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4620,28 +4620,27 @@ static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec
 {
        struct lru_gen_folio *child_lrugen, *parent_lrugen;
        enum lru_list lru = type * LRU_INACTIVE_FILE;
-       int gen;
+       int gen, parent_gen;
 
        child_lrugen = &child_lruvec->lrugen;
        parent_lrugen = &parent_lruvec->lrugen;
+       parent_gen = lru_gen_from_seq(READ_ONCE(parent_lrugen->min_seq[type]));
+       int parent_lru_active = lru_gen_is_active(parent_lruvec, parent_gen) ? LRU_ACTIVE : 0;
 
        for (gen = 0; gen < MAX_NR_GENS; gen++) {
                long nr_pages = child_lrugen->nr_pages[gen][type][zone];
                int child_lru_active = lru_gen_is_active(child_lruvec, gen) ? LRU_ACTIVE : 0;
-               int parent_lru_active = lru_gen_is_active(parent_lruvec, gen) ? LRU_ACTIVE : 0;
 
                /* Assuming that child pages are colder than parent pages */
                list_splice_tail_init(&child_lrugen->folios[gen][type][zone],
-                                     &parent_lrugen->folios[gen][type][zone]);
+                                     &parent_lrugen->folios[parent_gen][type][zone]);
 
                WRITE_ONCE(child_lrugen->nr_pages[gen][type][zone], 0);
                WRITE_ONCE(parent_lrugen->nr_pages[gen][type][zone],
-                          parent_lrugen->nr_pages[gen][type][zone] + nr_pages);
+                          parent_lrugen->nr_pages[parent_gen][type][zone] + nr_pages);
 
-               if (lru_gen_is_active(child_lruvec, gen) != lru_gen_is_active(parent_lruvec, gen)) {
-                       __update_lru_size(child_lruvec, lru + child_lru_active, zone, -nr_pages);
-                       __update_lru_size(parent_lruvec, lru + parent_lru_active, zone, nr_pages);
-               }
+               __update_lru_size(child_lruvec, lru + child_lru_active, zone, -nr_pages);
+               __update_lru_size(parent_lruvec, lru + parent_lru_active, zone, nr_pages);
        }
 }


>
> And if the child's old generation was squashed into an out-of-window
> generation, these out-of-window cold folios will get evicted much
> later than the hot folios. In that case we even somehow "fixed" that
> by this patch :P, as long there is no leak, I think it's hard to
> define what is the right behavior.

Yes, agreed.

Another thing we could do is add the change I mentioned in my
reply to Sashiko [1], which Andrew has already picked up.

@@ -4788,6 +4788,9 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
  seq = lrugen->max_seq;

  seq = lruvec_populated_min_seq(lruvec, type, sc->reclaim_idx + 1, seq);
+ if (seq < lrugen->min_seq[type])
+            return 0;
+

This would avoid iterating over each zone's list just to check
whether it is empty. That's the case most of the time, except
for corner cases involving reparenting.

[1] https://lore.kernel.org/linux-mm/20261114214438.70148-1-baohua@kernel.org/

Thanks
Barry




  reply	other threads:[~2026-11-15 22:00 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 20:07 [PATCH v9 00/16] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 01/16] mm/mglru: report evictable sizes per type Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 02/16] mm/mglru: extract lruvec_populated_min_seq() helper Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 03/16] mm/mglru: reclaim from the oldest populated gen Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 04/16] mm/mglru: lift the forced protection of reclaimer Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 05/16] mm/mglru: skip zones above reclaim_idx instead of rotating them Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 06/16] mm/mglru: return for unavailable anon reclaim earlier Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 07/16] mm/mglru: balance anon and file scan budget by refault rate Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 08/16] mm/mglru: lift youngest gen protection for higher priority Barry Song (Xiaomi)
2026-11-14 20:07 ` [PATCH v9 09/16] mm/mglru: Avoid placing rejected folios below populated gen Barry Song (Xiaomi)
2026-11-15 15:15   ` Kairui Song
2026-11-14 20:07 ` [PATCH v9 10/16] mm/mglru: Run aging when pages are rare outside the youngest gen Barry Song (Xiaomi)
2026-11-15 15:54   ` Kairui Song
2026-11-14 20:07 ` [PATCH v9 11/16] mm/mglru: Run aging when one type is drained Barry Song (Xiaomi)
2026-11-14 20:50   ` sashiko-bot
2026-11-14 21:44     ` Barry Song (Xiaomi)
2026-11-15 18:07       ` Kairui Song
2026-11-15 22:00         ` Barry Song (Xiaomi) [this message]
2026-11-14 20:07 ` [PATCH v9 12/16] mm/mglru: Restrict eviction accounting to eligible zones Barry Song (Xiaomi)
2026-11-14 20:08 ` [PATCH v9 13/16] mm/mglru: Reduce LRU lock contention by shrinking folio batches in inc_min_seq Barry Song (Xiaomi)
2026-11-15 15:52   ` Kairui Song
2026-11-14 20:08 ` [PATCH v9 14/16] mm/mglru: Avoid prolonged anon reclaim with swappiness=200 Barry Song (Xiaomi)
2026-11-14 20:08 ` [PATCH v9 15/16] mm/mglru: Make swappiness=1 file reclaim more aggressive Barry Song (Xiaomi)
2026-11-14 20:08 ` [PATCH v9 16/16] mm/mglru: Avoid scan anon while swapcache is low for GFP_NOIO allocation Barry Song (Xiaomi)
2026-11-14 22:09 ` [PATCH v9 00/16] mm: mglru: fix swappiness behavior Andrew Morton
2026-11-15  1:10   ` 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=20261115220007.89063-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=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=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-bot@kernel.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox