Re: [PATCH v4 03/17] mm/memcg: add folio-based lruvec live helper
From: Kairui Song
Date: Fri Oct 16 2026 - 03:58:38 EST
On Fri, Oct 16, 2026 at 3:39 PM Muchun Song <muchun.song@xxxxxxxxx> wrote:
>
> > On Oct 16, 2026, at 02:15, Kairui Song via B4 Relay <devnull+kasong.tencent.com@xxxxxxxxxx> wrote:
> >
> > From: Kairui Song <kasong@xxxxxxxxxxx>
> >
> > Add a helper that resolves a stable lruvec for a folio under RCU
> > without taking the lruvec lock. It takes a folio directly so the
> > lruvec lookup happens inside the RCU read-side critical section,
> > which a lruvec-based interface cannot guarantee.
> >
> > No functional change.
> >
> > Tested-by: zhaozhengzhuo <zhaozhengzhuo@xxxxxxxxxxxxx>
> > Signed-off-by: Kairui Song <kasong@xxxxxxxxxxx>
> > ---
> > include/linux/memcontrol.h | 39 +++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 39 insertions(+)
> >
> > diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> > index 74110a324f9e..829ab776ab44 100644
> > --- a/include/linux/memcontrol.h
> > +++ b/include/linux/memcontrol.h
> > @@ -1535,6 +1535,45 @@ static inline void lruvec_lock_irq(struct lruvec *lruvec)
> > spin_lock_irq(&lruvec->lru_lock);
> > }
> >
> > +/**
> > + * folio_lruvec_live_get - get a live lruvec for a folio under RCU
> > + * @folio: the folio
> > + *
> > + * Computes @folio's lruvec and walks up to the nearest live ancestor
> > + * if the folio's memcg is dying. Paired with folio_lruvec_live_put().
> > + * The result may be stale: RCU keeps it alive but does not pin @folio
> > + * to it. That is fine as the counters are fixed up on reparenting.
> > + *
> > + * Return: the live lruvec, with rcu_read_lock held.
> > + */
> > +static inline struct lruvec *folio_lruvec_live_get(struct folio *folio)
> > +{
> > +#ifdef CONFIG_MEMCG
> > + struct lruvec *lruvec;
> > + struct pglist_data *pgdat;
> > + struct mem_cgroup *memcg;
> > +
> > + rcu_read_lock();
> > + lruvec = folio_lruvec(folio);
> > + pgdat = lruvec_pgdat(lruvec);
> > + memcg = lruvec_memcg(lruvec);
> > + while (unlikely(memcg && css_is_dying(&memcg->css))) {
> > + memcg = parent_mem_cgroup(memcg);
> > + lruvec = mem_cgroup_lruvec(memcg, pgdat);
> > + }
> > + return lruvec;
>
> Hi Kairui,
>
Hi Muchun, thanks for the review.
> Actually, we don't have any means to guarantee that the memcg
> to which the returned lruvec belongs will not become dying
> after the return, right?
Yeah but Qi once mentioned that RCU prevents reparenting, right? Even
if the memcg is dead after return, things happended to it
(non-hieratical counter reparenting, ones in __lru_gen_reparent_memcg)
will only occurs after RCU unlock so we can apply counter update to it
without grabbing an ref, just hold the RCU.
> > +#else
> > + return folio_lruvec(folio);
>
> Then, I'm wondering why folio_lruvec() doesn't apply to the
> memcg scenario.
>
> Internally, folio_lruvec() obtains the memcg through
> obj_cgroup_memcg(). So when the memcg becomes dying, it should
> be reparented to the parent memcg within a short time.
I wasn't sure if grabbing RCU can ensure an already dead cgroup has
not been reparented yet. If we apply the counter update to a already
reparented lruvec, then the counter is leaked. This new helper is just
a folio version of lruvec_live_lock_irq.
I got the idea from commit de4660898b7a, which says:
"""
.... check CSS_DYING under RCU before
flushing the pending batch. A non-dying memcg keeps the original lruvec
stable against RCU-delayed offlining; a dying memcg redirects the deltas
to the first non-dying ancestor.
"""
Applying counter update to a dead but not reparented lruvec is fine though.