Re: [PATCH v4 03/17] mm/memcg: add folio-based lruvec live helper

From: Muchun Song

Date: Fri Oct 16 2026 - 03:43:44 EST




> 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,

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?

> +#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.

Likewise, folio_lruvec() may also return an lruvec whose memcg
may also be in a short-lived dying state.

Since the results of the two are similar, why not just use
folio_lruvec() directly? Or is there something I misunderstood?

Thanks,
Muchun


> +#endif
> +}
> +
> +static inline void folio_lruvec_live_put(struct lruvec *lruvec)
> +{
> +#ifdef CONFIG_MEMCG
> + rcu_read_unlock();
> +#endif
> +}
> +
> static inline struct lruvec *lruvec_live_lock_irq(struct lruvec *lruvec)
> {
> #ifdef CONFIG_MEMCG
>
> --
> 2.56.0
>
>