Re: [PATCH 1/2] mm/vmscan: remove redundant !mem_cgroup_online() checks
From: T.J. Mercier
Date: Wed Oct 28 2026 - 17:07:44 EST
On Tue, Oct 27, 2026 at 7:32 PM Balbir Singh <balbirs@xxxxxxxxxx> wrote:
>
> On Tue, Oct 27, 2026 at 06:06:05PM -0700, T.J. Mercier wrote:
> > Since commit f1cf8d2f36dc ("mm: memcontrol: eliminate the problem of
> > dying memory cgroup for LRU folios") and commit 0e0ac326c511 ("memcg:
> > move LRU size accounting on reparenting instead of copying it"),
> > mem_cgroup_css_offline() reparents all LRU folios to the parent memcg
> > and zeroes the child's lrugen->nr_pages and lru_zone_size before the
> > memcg is offlined. Nothing can repopulate the offlined memcg's lruvec
> > afterwards. A folio that is off the LRU during reparenting looks up its
> > lruvec through its objcg, which now points to the parent so it is put
> > back on the parent's LRU.
> >
> > As a result, lruvec_lru_size() and lruvec_evictable_size() are always 0
> > when !mem_cgroup_online(memcg) is true, making the offlined memcg special
> > cases in get_scan_count(), lruvec_is_sizable(), and shrink_one() dead
> > code. Remove them.
> >
> > With the get_nr_to_scan() special case removed, an offlined memcg that
> > still has stale effective protection now has
> > apply_proportional_protection() applied like get_scan_count() does, so
> > the MGLRU and traditional LRU behavior is now the same.
> >
> > Signed-off-by: T.J. Mercier <tjmercier@xxxxxxxxxx>
> > ---
> > include/linux/mmzone.h | 8 ++++----
> > mm/vmscan.c | 28 +++++-----------------------
> > 2 files changed, 9 insertions(+), 27 deletions(-)
> >
> > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> > index 3b96d6c7123b..7f078ff0dd76 100644
> > --- a/include/linux/mmzone.h
> > +++ b/include/linux/mmzone.h
> > @@ -638,10 +638,10 @@ struct lru_gen_mm_walk {
> > * The events that trigger the above operations are:
> > * 1. The first attempt to reclaim a memcg below low, which triggers
> > * MEMCG_LRU_TAIL;
> > - * 2. The first attempt to reclaim a memcg offlined or below reclaimable size
> > - * threshold, which triggers MEMCG_LRU_TAIL;
> > - * 3. The second attempt to reclaim a memcg offlined or below reclaimable size
> > - * threshold, which triggers MEMCG_LRU_YOUNG;
> > + * 2. The first attempt to reclaim a memcg below reclaimable size threshold,
> > + * which triggers MEMCG_LRU_TAIL;
> > + * 3. The second attempt to reclaim a memcg below reclaimable size threshold,
> > + * which triggers MEMCG_LRU_YOUNG;
> > * 4. Attempting to reclaim a memcg below min, which triggers MEMCG_LRU_YOUNG;
> > * 5. Finishing the aging on the eviction path, which triggers MEMCG_LRU_YOUNG;
> > * 6. Offlining a memcg, which triggers MEMCG_LRU_OLD.
> > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > index 9959ca158ce8..636152d8c94d 100644
> > --- a/mm/vmscan.c
> > +++ b/mm/vmscan.c
> > @@ -2754,13 +2754,6 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
> > scan = apply_proportional_protection(memcg, sc, lruvec_size);
> > scan >>= sc->priority;
> >
> > - /*
> > - * If the cgroup's already been deleted, make sure to
> > - * scrape out the remaining cache.
> > - */
> > - if (!scan && !mem_cgroup_online(memcg))
> > - scan = min(lruvec_size, SWAP_CLUSTER_MAX);
> > -
> > switch (scan_balance) {
> > case SCAN_EQUAL:
> > /* Scan lists relative to size */
> > @@ -2769,14 +2762,8 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
> > /*
> > * Scan types proportional to swappiness and
> > * their relative recent reclaim efficiency.
> > - * Make sure we don't miss the last page on
> > - * the offlined memory cgroups because of a
> > - * round-off error.
> > */
> > - scan = mem_cgroup_online(memcg) ?
> > - div64_u64(scan * fraction[file], denominator) :
> > - DIV64_U64_ROUND_UP(scan * fraction[file],
> > - denominator);
> > + scan = div64_u64(scan * fraction[file], denominator);
> > break;
> > case SCAN_FILE:
> > case SCAN_ANON:
> > @@ -4355,12 +4342,11 @@ static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
> > {
> > unsigned long total;
> > int swappiness = get_swappiness(lruvec, sc);
> > - struct mem_cgroup *memcg = lruvec_memcg(lruvec);
> >
> > total = lruvec_evictable_size(lruvec, swappiness);
> >
> > /* whether the size is big enough to be helpful */
> > - return mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
> > + return total >> sc->priority;
> > }
> >
> > static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,
> > @@ -5199,10 +5185,6 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
> >
> > evictable = lruvec_evictable_size(lruvec, swappiness);
> >
> > - /* try to scrape all its memory if this memcg was deleted */
> > - if (!mem_cgroup_online(memcg))
> > - return evictable;
> > -
> > nr_to_scan = apply_proportional_protection(memcg, sc, evictable);
> > nr_to_scan >>= sc->priority;
> >
> > @@ -5325,13 +5307,13 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)
> >
> > flush_reclaim_state(sc);
> >
> > - if (need_rotate && mem_cgroup_online(memcg))
> > + if (need_rotate)
> > return MEMCG_LRU_YOUNG;
> >
> > - if (!need_rotate && lruvec_is_sizable(lruvec, sc))
> > + if (lruvec_is_sizable(lruvec, sc))
> > return 0;
> >
> > - /* one retry if offlined or too small */
> > + /* one retry if too small */
> > return READ_ONCE(lruvec->lrugen.seg) != MEMCG_LRU_TAIL ?
> > MEMCG_LRU_TAIL : MEMCG_LRU_YOUNG;
> > }
> > --
>
> In some of the places do you want to consider adding a VM_WARN_ON_ONCE
> for memcg_online not being true?
>
> Acked-by: Balbir Singh <balbirs@xxxxxxxxxx>
Thanks, does this look ok to you? We could also put it on the read
side in lruvec_lru_size(), but that backtrace will just show the
reclaim stack rather than whoever is trying to mutate the size of an
offline memcg.
@@ -49,6 +49,7 @@ static __always_inline void __update_lru_size(struct
lruvec *lruvec,
lockdep_assert_held(&lruvec->lru_lock);
WARN_ON_ONCE(nr_pages != (int)nr_pages);
+ VM_WARN_ON_ONCE(!mem_cgroup_online(lruvec_memcg(lruvec)));
mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages);
__mod_zone_page_state(&pgdat->node_zones[zid],