From: Joanne Koong <joannelkoong@gmail.com>
To: akpm@linux-foundation.org, david@kernel.org
Cc: yosry@kernel.org, mkoutny@suse.com, shakeel.butt@linux.dev,
hannes@cmpxchg.org, mhocko@kernel.org, roman.gushchin@linux.dev,
muchun.song@linux.dev, nphamcs@gmail.com,
chengming.zhou@linux.dev, sj@kernel.org, ljs@kernel.org,
linux-mm@kvack.org, cgroups@vger.kernel.org,
damon@lists.linux.dev
Subject: [PATCH v3 2/4] mm: memcg: read root lruvec stats from the node counters
Date: Fri, 13 Nov 2026 16:28:20 -0800 [thread overview]
Message-ID: <20261114002822.1043378-3-joannelkoong@gmail.com> (raw)
In-Reply-To: <20261114002822.1043378-1-joannelkoong@gmail.com>
For the root memcg, lruvec_page_state() returns the rstat value, which
needs a flush to be up to date. Flushing the root memcg is expensive, as
it flushes every memcg in the system.
Most memcg node stats only track memory charged to a memcg, so the root
memcg's hierarchical value equals the node counter, which is always up
to date and doesn't need a flush. For those stats, have
lruvec_page_state() and lruvec_page_state_monotonic() read the node
counter for the root memcg, as they already do when memcg is disabled.
Callers that only read the root memcg's own lruvec then no longer need
to flush it, which the following patches take advantage of. It also
makes the root memcg's stats more accurate, as callers that ratelimit
the flush can otherwise read values that are seconds old.
The exceptions are the kernel memory stats (slab, kernel stack, page
tables, secondary page tables and vmalloc), hugetlb, and
WORKINGSET_NODERECLAIM, which counts reclaimed xarray nodes (slab
objects). Their node counters also count memory that isn't charged to a
memcg, which falls into two categories:
a) the allocation isn't accounted to any memcg (eg kernel page tables,
or hugetlb when hugetlb accounting is off)
b) the allocation is accounted, but it comes from a task running in the
root cgroup, and the root memcg isn't charged for it
As such, these stats still need to read the rstat value, since the node
counter value can be higher than the rstat value.
One user-visible change is that the root memcg's memory.numa_stat (and
the hierarchical_* entries of the cgroup v1 memory.numa_stat) now
reports the node counters for the non-excluded stats, as it reads
lruvec_page_state(). This is more accurate, but as a result, it may not
exactly match the root memcg's memory.stat, which still reads the rstat
values.
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
mm/memcontrol.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 56 insertions(+), 2 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 091401cecf39..196c0382181b 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -504,13 +504,67 @@ struct lruvec_stats {
long state_pending[NR_MEMCG_NODE_STAT_ITEMS];
};
+/*
+ * Whether to read the node counter for @idx instead of @lruvec's rstat value.
+ * This is only done for the root memcg for stats where every update also
+ * updates a memcg's stats, so the root memcg's hierarchical value equals the
+ * node counter. The node counter is preferred since it is always up to date,
+ * and avoids an rstat flush which for the root memcg flushes every cgroup in
+ * the system.
+ */
+static bool lruvec_use_node_stat(const struct lruvec *lruvec,
+ enum node_stat_item idx)
+{
+ if (!mem_cgroup_is_root(lruvec_memcg(lruvec)))
+ return false;
+
+ switch (idx) {
+ case NR_INACTIVE_ANON:
+ case NR_ACTIVE_ANON:
+ case NR_INACTIVE_FILE:
+ case NR_ACTIVE_FILE:
+ case NR_UNEVICTABLE:
+ case WORKINGSET_REFAULT_ANON:
+ case WORKINGSET_REFAULT_FILE:
+ case WORKINGSET_ACTIVATE_ANON:
+ case WORKINGSET_ACTIVATE_FILE:
+ case WORKINGSET_RESTORE_ANON:
+ case WORKINGSET_RESTORE_FILE:
+#ifdef CONFIG_NUMA_BALANCING
+ case PGPROMOTE_SUCCESS:
+#endif
+ case PGDEMOTE_KSWAPD:
+ case PGDEMOTE_DIRECT:
+ case PGDEMOTE_KHUGEPAGED:
+ case PGDEMOTE_PROACTIVE:
+ case PGSTEAL_KSWAPD:
+ case PGSTEAL_DIRECT:
+ case PGSTEAL_KHUGEPAGED:
+ case PGSTEAL_PROACTIVE:
+ case PGSTEAL_ANON:
+ case PGSTEAL_FILE:
+ case PGSCAN_KSWAPD:
+ case PGSCAN_DIRECT:
+ case PGSCAN_KHUGEPAGED:
+ case PGSCAN_PROACTIVE:
+ case PGSCAN_ANON:
+ case PGSCAN_FILE:
+ case PGROTATE_ANON:
+ case PGROTATE_FILE:
+ case PGREFILL:
+ return true;
+ default:
+ return false;
+ }
+}
+
unsigned long lruvec_page_state(const struct lruvec *lruvec, enum node_stat_item idx)
{
const struct mem_cgroup_per_node *pn;
long x;
int i;
- if (mem_cgroup_disabled())
+ if (mem_cgroup_disabled() || lruvec_use_node_stat(lruvec, idx))
return node_page_state(lruvec_pgdat(lruvec), idx);
i = memcg_stats_index(idx);
@@ -551,7 +605,7 @@ unsigned long lruvec_page_state_monotonic(const struct lruvec *lruvec,
const struct mem_cgroup_per_node *pn;
int i;
- if (mem_cgroup_disabled())
+ if (mem_cgroup_disabled() || lruvec_use_node_stat(lruvec, idx))
return node_page_state_monotonic(lruvec_pgdat(lruvec), idx);
i = memcg_stats_index(idx);
--
2.52.1
next prev parent reply other threads:[~2026-11-14 0:30 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 0:28 [PATCH v3 0/4] mm: read root memcg stats from global counters instead of flushing Joanne Koong
2026-11-14 0:28 ` [PATCH v3 1/4] mm: zswap: fix shrinker count for the root memcg Joanne Koong
2026-11-14 0:28 ` Joanne Koong [this message]
2026-11-14 0:49 ` [PATCH v3 2/4] mm: memcg: read root lruvec stats from the node counters sashiko-bot
2026-11-14 0:28 ` [PATCH v3 3/4] mm: workingset: skip the root memcg flush in workingset_test_recent() Joanne Koong
2026-11-14 0:28 ` [PATCH v3 4/4] mm/damon/core: use node counters instead of flushing the root memcg Joanne Koong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261114002822.1043378-3-joannelkoong@gmail.com \
--to=joannelkoong@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=damon@lists.linux.dev \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=mkoutny@suse.com \
--cc=muchun.song@linux.dev \
--cc=nphamcs@gmail.com \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=sj@kernel.org \
--cc=yosry@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).