From: Michal Hocko <mhocko@suse.com>
To: Joanne Koong <joannelkoong@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
Shakeel Butt <shakeel.butt@linux.dev>,
Yosry Ahmed <yosry@kernel.org>,
Johannes Weiner <hannes@cmpxchg.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
cgroups@vger.kernel.org, linux-mm@kvack.org
Subject: Re: [PATCH v3 2/2] memcg: don't go through the lruvec for folio and kmem lruvec stat updates
Date: Mon, 26 Oct 2026 10:38:26 +0100 [thread overview]
Message-ID: <at8fkp9hR7Mvjy4J@tiehlicka> (raw)
In-Reply-To: <20261023195417.1391216-3-joannelkoong@gmail.com>
On Fri 23-10-26 12:54:17, Joanne Koong wrote:
> lruvec_stat_mod_folio() and mod_lruvec_kmem_state() already know the memcg
> and the node, but look up the lruvec with mem_cgroup_lruvec() and update
> the stats through mod_lruvec_state(), which reads lruvec->pgdat and
> mem_cgroup_per_node->memcg to get them back. lruvec->pgdat shares a
> 128-byte cacheline pair with lines that the lru_lock holder writes
> constantly (lru_lock or lru_zone_size[], depending on the alignment of
> struct mem_cgroup_per_node), so on CPUs that do spatial prefetching
> these per-folio stat updates contend with the lru_lock holder.
>
> Pass the memcg and node they already know to mod_memcg_node_state()
> instead.
>
> On top of the mem_cgroup_per_node rearrangement, on a 2-socket Skylake
> (80 CPUs) with threads faulting in and freeing shmem in a single memcg,
> this improves throughput by about 3% at 20 threads (5.59M -> 5.77M
> pages/sec) and 2% at 80 threads (8.66M -> 8.82M).
>
> Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Thanks!
> ---
> mm/memcontrol.c | 71 ++++++++++++++++++++-----------------------------
> 1 file changed, 29 insertions(+), 42 deletions(-)
>
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index a05d9a8baa1b..9f33cc5c8293 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -587,7 +587,7 @@ unsigned long lruvec_page_state_local(const struct lruvec *lruvec,
> return x;
> }
>
> -static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn,
> +static void __mod_memcg_lruvec_state(struct mem_cgroup *memcg, int nid,
> enum node_stat_item idx, long val);
>
> void reparent_memcg_lruvec_state_local(struct mem_cgroup *memcg,
> @@ -596,16 +596,11 @@ void reparent_memcg_lruvec_state_local(struct mem_cgroup *memcg,
> int nid;
>
> for_each_node(nid) {
> - struct lruvec *child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
> - struct lruvec *parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid));
> - unsigned long value = lruvec_page_state_local(child_lruvec, idx);
> - struct mem_cgroup_per_node *child_pn, *parent_pn;
> + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
> + unsigned long value = lruvec_page_state_local(lruvec, idx);
>
> - child_pn = container_of(child_lruvec, struct mem_cgroup_per_node, lruvec);
> - parent_pn = container_of(parent_lruvec, struct mem_cgroup_per_node, lruvec);
> -
> - __mod_memcg_lruvec_state(child_pn, idx, -value);
> - __mod_memcg_lruvec_state(parent_pn, idx, value);
> + __mod_memcg_lruvec_state(memcg, nid, idx, -value);
> + __mod_memcg_lruvec_state(parent, nid, idx, value);
> }
> }
>
> @@ -990,10 +985,10 @@ void reparent_memcg_state_local(struct mem_cgroup *memcg,
> }
> #endif
>
> -static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn,
> +static void __mod_memcg_lruvec_state(struct mem_cgroup *memcg, int nid,
> enum node_stat_item idx, long val)
> {
> - struct mem_cgroup *memcg = pn->memcg;
> + struct mem_cgroup_per_node *pn = memcg->nodeinfo[nid];
> int i = memcg_stats_index(idx);
> int cpu;
>
> @@ -1015,21 +1010,23 @@ static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn,
> put_cpu();
> }
>
> +static void mod_memcg_node_state(struct mem_cgroup *memcg, int nid,
> + enum node_stat_item idx, int val)
> +{
> + bool rcu_locked = false;
> +
> + memcg = get_non_dying_memcg_start(memcg, &rcu_locked);
> + __mod_memcg_lruvec_state(memcg, nid, idx, val);
> + get_non_dying_memcg_end(rcu_locked);
> +}
> +
> void mod_memcg_lruvec_state(struct lruvec *lruvec,
> enum node_stat_item idx, int val)
> {
> - struct pglist_data *pgdat = lruvec_pgdat(lruvec);
> struct mem_cgroup_per_node *pn;
> - struct mem_cgroup *memcg;
> - bool rcu_locked = false;
>
> pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
> - memcg = get_non_dying_memcg_start(pn->memcg, &rcu_locked);
> - pn = memcg->nodeinfo[pgdat->node_id];
> -
> - __mod_memcg_lruvec_state(pn, idx, val);
> -
> - get_non_dying_memcg_end(rcu_locked);
> + mod_memcg_node_state(pn->memcg, lruvec_pgdat(lruvec)->node_id, idx, val);
> }
>
> /**
> @@ -1058,19 +1055,13 @@ void lruvec_stat_mod_folio(struct folio *folio, enum node_stat_item idx,
> {
> struct mem_cgroup *memcg;
> pg_data_t *pgdat = folio_pgdat(folio);
> - struct lruvec *lruvec;
> +
> + mod_node_page_state(pgdat, idx, val);
>
> rcu_read_lock();
> memcg = folio_memcg(folio);
> - /* Untracked pages have no memcg, no lruvec. Update only the node */
> - if (!memcg) {
> - rcu_read_unlock();
> - mod_node_page_state(pgdat, idx, val);
> - return;
> - }
> -
> - lruvec = mem_cgroup_lruvec(memcg, pgdat);
> - mod_lruvec_state(lruvec, idx, val);
> + if (memcg)
> + mod_memcg_node_state(memcg, pgdat->node_id, idx, val);
> rcu_read_unlock();
> }
> EXPORT_SYMBOL(lruvec_stat_mod_folio);
> @@ -1079,23 +1070,19 @@ void mod_lruvec_kmem_state(void *p, enum node_stat_item idx, int val)
> {
> pg_data_t *pgdat = page_pgdat(virt_to_page(p));
> struct mem_cgroup *memcg;
> - struct lruvec *lruvec;
> +
> + mod_node_page_state(pgdat, idx, val);
>
> rcu_read_lock();
> memcg = mem_cgroup_from_virt(p);
>
> /*
> - * Untracked pages have no memcg, no lruvec. Update only the
> - * node. If we reparent the slab objects to the root memcg,
> - * when we free the slab object, we need to update the per-memcg
> - * vmstats to keep it correct for the root memcg.
> + * If we reparent the slab objects to the root memcg, when we free the
> + * slab object, we need to update the per-memcg vmstats to keep it
> + * correct for the root memcg.
> */
> - if (!memcg) {
> - mod_node_page_state(pgdat, idx, val);
> - } else {
> - lruvec = mem_cgroup_lruvec(memcg, pgdat);
> - mod_lruvec_state(lruvec, idx, val);
> - }
> + if (memcg)
> + mod_memcg_node_state(memcg, pgdat->node_id, idx, val);
> rcu_read_unlock();
> }
>
> --
> 2.52.0
--
Michal Hocko
SUSE Labs
next prev parent reply other threads:[~2026-10-26 9:38 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 19:54 [PATCH v3 0/2] memcg: reduce cacheline contention from lruvec stat Joanne Koong
2026-10-23 19:54 ` [PATCH v3 1/2] memcg: reduce cacheline contention between lruvec stat updates and LRU lists Joanne Koong
2026-10-26 9:29 ` Michal Hocko
2026-10-23 19:54 ` [PATCH v3 2/2] memcg: don't go through the lruvec for folio and kmem lruvec stat updates Joanne Koong
2026-10-26 9:38 ` Michal Hocko [this message]
2026-10-23 22:54 ` [PATCH v3 0/2] memcg: reduce cacheline contention from lruvec stat Andrew Morton
2026-10-27 22:57 ` Joanne Koong
2026-10-28 2:24 ` Andrew Morton
2026-10-28 18:33 ` 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=at8fkp9hR7Mvjy4J@tiehlicka \
--to=mhocko@suse.com \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=hannes@cmpxchg.org \
--cc=joannelkoong@gmail.com \
--cc=linux-mm@kvack.org \
--cc=muchun.song@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
--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