Re: [PATCH 1/7] mm/memcontrol: make lru_zone_size atomic and simplify sanity check
Ridong Chen <[email protected]>
| Newsgroups | org.kernel.vger.cgroups,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
On 8/18/2026 1:38 PM, Kairui Song via B4 Relay wrote: > From: Kairui Song <[email protected]> > > commit ca707239e8a7 ("mm: update_lru_size warn and reset bad lru_size") > introduced a sanity check to catch memcg counter underflow, which was > more of a workaround for another bug: lru_zone_size is unsigned, so > underflow wraps it around and returns an enormously large number, then > the memcg shrinker loops almost forever as the calculated number of > folios to shrink is huge. That commit also checked if a zero value > matches the empty LRU list, so we have to hold the LRU lock, and > handle the positive and negative deltas separately. > > But later commit b4536f0c829c ("mm, memcg: fix the active list aging > for lowmem requests when memcg is enabled") already removed the LRU > emptiness check, so handling the deltas separately is no longer > needed. And if we just turn it into an atomic long, underflow isn't a > big issue either, and can be checked at the reader side, which is > called much less frequently than the updater. > > So let's turn the counter into an atomic long and check at the reader > side instead, which has a smaller overhead. The underflow correction > is removed: a massive leak of the LRU size counter would indicate > that something else has gone very wrong, and one should fix that > leaking site instead. Besides, the updater-side sanity check is > unlikely to catch the leaking site anyway: if a folio was removed > without updating the counter while other folios remain on the LRU, > the WARN only triggers much later, from a likely innocent callsite. > > Signed-off-by: Kairui Song <[email protected]> > --- > include/linux/memcontrol.h | 9 +++++++-- > mm/memcontrol.c | 18 +----------------- > 2 files changed, 8 insertions(+), 19 deletions(-) > > diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h > index e78bc98ab229..b13e3f056319 100644 > --- a/include/linux/memcontrol.h > +++ b/include/linux/memcontrol.h > @@ -113,7 +113,7 @@ struct mem_cgroup_per_node { > /* Fields which get updated often at the end. */ > struct lruvec lruvec; > CACHELINE_PADDING(_pad2_); > - unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS]; > + atomic_long_t lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS]; > struct mem_cgroup_reclaim_iter iter; > > /* > @@ -897,10 +897,15 @@ static inline > unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec, > enum lru_list lru, int zone_idx) > { > + long val; > struct mem_cgroup_per_node *mz; > > mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec); > - return READ_ONCE(mz->lru_zone_size[zone_idx][lru]); > + val = atomic_long_read(&mz->lru_zone_size[zone_idx][lru]); > + if (WARN_ON_ONCE(val < 0)) > + return 0; > + > + return val; > } > > void __mem_cgroup_handle_over_high(gfp_t gfp_mask); > diff --git a/mm/memcontrol.c b/mm/memcontrol.c > index 1d3339520809..9d0ee3d3bda7 100644 > --- a/mm/memcontrol.c > +++ b/mm/memcontrol.c > @@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru, > int zid, long nr_pages) > { > struct mem_cgroup_per_node *mz; > - unsigned long *lru_size; > - long size; > > if (mem_cgroup_disabled()) > return; > > mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec); > - lru_size = &mz->lru_zone_size[zid][lru]; > - > - if (nr_pages < 0) > - *lru_size += nr_pages; > - > - size = *lru_size; > - if (WARN_ONCE(size < 0, > - "%s(%p, %d, %ld): lru_size %ld\n", > - __func__, lruvec, lru, nr_pages, size)) { > - VM_BUG_ON(1); > - *lru_size = 0; > - } > - This code is confusing, I used to try to remove it. Well done. > - if (nr_pages > 0) > - *lru_size += nr_pages; > + atomic_long_add(nr_pages, &mz->lru_zone_size[zid][lru]); > } > > /** > Looks good to me. Reviewed-by: Ridong Chen <[email protected]> -- Best regards Ridong