[PATCH RFC 5/5] memcg: filter out reparented memcgs got using memcgid
Bingfang Guo via B4 Relay <[email protected]>
| Newsgroups | org.kernel.vger.cgroups,org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
From: Bingfang Guo <[email protected]> mem_cgroup_from_private_id() looks up the objcg that owns the id and returns the objcg's current memcg. After reparenting, that memcg can differ from the one the id originally belonged to. Callers such as the list lru and workingset refault code expect to get back exactly the memcg referred to by the memcgid, so check that the returned memcg still owns the id and return NULL otherwise, letting the callers skip the entry. Signed-off-by: Bingfang Guo <[email protected]> --- mm/list_lru.c | 2 +- mm/memcontrol.c | 9 ++++++++- mm/workingset.c | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index 36662d02ff963..bc956267f6835 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -428,7 +428,7 @@ unsigned long list_lru_walk_node(struct list_lru *lru, int nid, xa_for_each(&lru->xa, index, mlru) { rcu_read_lock(); memcg = mem_cgroup_from_private_id(index); - if (!mem_cgroup_tryget(memcg)) { + if (!memcg || !mem_cgroup_tryget(memcg)) { rcu_read_unlock(); continue; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index fdf2e0d1f17e5..e7555eca77019 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3999,17 +3999,24 @@ void mem_cgroup_private_id_get(struct mem_cgroup *memcg, unsigned int n) * @id: the memcg id to look up * * Caller must hold rcu_read_lock(). + * + * @return: the memcg, or NULL if the memcg is already reparented. */ struct mem_cgroup *mem_cgroup_from_private_id(unsigned short id) { struct obj_cgroup *objcg; + struct mem_cgroup *memcg; WARN_ON_ONCE(!rcu_read_lock_held()); objcg = xa_load(&mem_cgroup_private_ids, id); if (!objcg) return NULL; - return obj_cgroup_memcg(objcg); + memcg = obj_cgroup_memcg(objcg); + if (mem_cgroup_private_id(memcg) != id) + return NULL; + + return memcg; } /** diff --git a/mm/workingset.c b/mm/workingset.c index f351798e723ac..b6e22536a5240 100644 --- a/mm/workingset.c +++ b/mm/workingset.c @@ -283,6 +283,10 @@ static bool lru_gen_test_recent(void *shadow, struct lruvec **lruvec, memcg = mem_cgroup_from_private_id(memcg_id); *lruvec = mem_cgroup_lruvec(memcg, pgdat); + /* reparented memcg loses its max_seq */ + if (!memcg) + return false; + max_seq = READ_ONCE((*lruvec)->lrugen.max_seq); max_seq &= (file ? EVICTION_MASK : EVICTION_MASK_ANON) >> LRU_REFS_WIDTH; @@ -470,7 +474,7 @@ bool workingset_test_recent(void *shadow, bool file, bool *workingset, * configurations instead. */ eviction_memcg = mem_cgroup_from_private_id(memcgid); - if (!mem_cgroup_tryget(eviction_memcg)) + if (!eviction_memcg || !mem_cgroup_tryget(eviction_memcg)) eviction_memcg = NULL; rcu_read_unlock(); -- 2.43.7