mm: memcg: skip the RCU lock when the memcg is not dying

get_non_dying_memcg_start() takes rcu_read_lock() on every stat update,
but the lock only protects the upward walk to a non-dying ancestor, which
happens solely while a memcg is being offlined.  The dying check itself
reads the CSS_DYING flag of a memcg the caller already holds a reference
to, so it is safe without the lock.

Check memcg_is_dying() first and return immediately when the memcg is
alive, taking the RCU lock only on the rare dying path.  On an anon
fault/charge churn workload in a memcg this recovers the ~0.6% overhead
added by the previous patch (4368077 vs 4343159 pages/s before, back to
~4377000 pages/s after).

Link: https://lore.kernel.org/9ffdbdfc96312e3e13cb8f056bfe26649492d949.1789096175.git.zhuhui@kylinos.cn
Fixes: 7404bd37cf ("mm: workingset: use lruvec_lru_size() to get the number of lru pages")
Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: <stable@vger.kernel.org>
This commit is contained in:
Hui Zhu 2026-09-11 16:00:50 +08:00 committed by Andrew Morton
parent c7a37bac3c
commit 7c0a645068

View File

@ -881,6 +881,17 @@ static long memcg_state_val_in_pages(int idx, long val)
static inline struct mem_cgroup *
get_non_dying_memcg_start(struct mem_cgroup *memcg, bool *rcu_locked)
{
/*
* Fast path: the caller holds a reference to @memcg, so reading
* its CSS_DYING flag without the RCU lock is safe. The RCU lock
* is only needed to walk up to a non-dying ancestor, which
* happens only while a memcg is actually being offlined.
*/
if (!memcg_is_dying(memcg)) {
*rcu_locked = false;
return memcg;
}
rcu_read_lock();
*rcu_locked = true;
@ -892,6 +903,9 @@ get_non_dying_memcg_start(struct mem_cgroup *memcg, bool *rcu_locked)
static inline void get_non_dying_memcg_end(bool rcu_locked)
{
if (!rcu_locked)
return;
rcu_read_unlock();
}