On Wed, Jan 12, 2022 at 09:22:36PM +0800, Muchun Song <songmuchun@xxxxxxxxxxxxx> wrote: > root(-1) -> A(0) -> B(1) -> C(2) > > CPU0: CPU1: > memcg_list_lru_alloc(C) > memcg_drain_all_list_lrus(C) > memcg_drain_all_list_lrus(B) > // Now C and B are offline. The > // kmemcg_id becomes the following if > // we do not the kmemcg_id of its > // descendants in > // memcg_drain_all_list_lrus(). > // > // root(-1) -> A(0) -> B(0) -> C(1) > > for (i = 0; memcg; memcg = parent_mem_cgroup(memcg), i++) { > // allocate struct list_lru_per_memcg for memcg C > table[i].mlru = memcg_init_list_lru_one(gfp); > } > > spin_lock_irqsave(&lru->lock, flags); > while (i--) { > // here index = 1 > int index = table[i].memcg->kmemcg_id; > > struct list_lru_per_memcg *mlru = table[i].mlru; > if (index < 0 || rcu_dereference_protected(mlrus->mlru[index], true)) > kfree(mlru); > else > // mlrus->mlru[index] will be assigned a new value regardless > // memcg C is already offline. > rcu_assign_pointer(mlrus->mlru[index], mlru); > } > spin_unlock_irqrestore(&lru->lock, flags); > > So changing ->kmemcg_id of all its descendants can prevent > memcg_list_lru_alloc() from allocating list lrus for the offlined > cgroup after memcg_list_lru_free() calling. Thanks for the illustrative example. I can see how this can be a problem in a general call of memcg_list_lru_alloc(C). However, the code, as I understand it, resolves the memcg to which lru allocation should be associated via get_mem_cgroup_from_objcg() and memcg_reparent_list_lrus(C) comes after memcg_reparent_objcgs(C, B), i.e. the allocation would target B (or even A if after memcg_reparent_objcgs(B, A))? It seems to me like "wasting" the existing objcg reparenting mechanism. Or what do you think could be a problem relying on it? Thanks, Michal