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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.