Re: [PATCH RFC 08/15] mm/memcg: add folio-based lruvec live helper

Lian Wang <[email protected]> Tue, 4 Aug 2026 15:48:03 +0800
Newsgroups gmane.linux.kernel,gmane.linux.kernel.mm,gmane.linux.kernel.cgroups
Message-ID <[email protected]>
From: "Lian Wang (ProcessMission)" <[email protected]>

Hi Kairui,

I am trying to understand the lifetime and accounting guarantee here, and
would appreciate your guidance. My understanding is that RCU protects the
lruvec lifetime, but by itself does not stabilize the folio->lruvec
association across memcg deletion and reparenting.

Could folio_inc_lru_refs() obtain the child lruvec here, then race with
__lru_gen_reparent_memcg(), and finally account the generation move to the old
child after the folio and its counters have moved to the parent? The opposite
ordering also seems possible: this helper observes css_is_dying() and selects
the parent while the folio is still accounted to the child.

Is there another invariant that closes these races? If my understanding is
correct, it seems the helper guarantees a live object, but not a stable
binding, and the lockless promotion path may need validation/retry or explicit
synchronization with reparenting.

If I have misunderstood the intended synchronization here, please feel free
to ignore this concern.

Thanks,
Lian

On Tue, 04 Aug 2026 03:47:04 +0800 Kairui Song via B4 Relay <[email protected]> wrote:

> From: Kairui Song <[email protected]>
> 
> 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.
> 
> The lock-taking variant now inlines the ancestor walk instead of
> calling a separate helper.
> 
> No functional change.
> 
> Signed-off-by: Kairui Song <[email protected]>
> ---
>  include/linux/memcontrol.h | 38 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 68f363000d7f..ea0111392b9b 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -1506,6 +1506,44 @@ 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.  Must be paired with
> + * folio_lruvec_live_put().
> + *
> + * 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;
> +#else
> +	return folio_lruvec(folio);
> +#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.55.0
> 
> 
> 

Sent using hkml (https://github.com/sjp38/hackermail)