diff --git a/mm/list_lru.c b/mm/list_lru.c index dd29bcf8eb5f..9bf7f524796b 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -473,26 +473,29 @@ void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *paren mutex_lock(&list_lrus_mutex); list_for_each_entry(lru, &memcg_list_lrus, list) { struct list_lru_memcg *mlru; - XA_STATE(xas, &lru->xa, memcg->kmemcg_id); /* - * Lock the Xarray to ensure no on going list_lru_memcg - * allocation and further allocation will see css_is_dying(). + * css_is_dying() check in memcg_list_lru_alloc() avoids + * allocating a new mlru since CSS_DYING is already set for this + * memcg a rcu grace period ago. */ - xas_lock_irq(&xas); - mlru = xas_store(&xas, NULL); - xas_unlock_irq(&xas); + mlru = xa_load(&lru->xa, memcg->kmemcg_id); if (!mlru) continue; /* - * With Xarray value set to NULL, holding the lru lock below - * prevents list_lru_{add,del,isolate} from touching the lru, - * safe to reparent. + * Reparent each per-node list and mark the child dead + * (LONG_MIN) before clearing xarray entry otherwise a + * concurrent list_lru_del() may corrupt the list if it arrives + * after xarray clear but before reparenting as + * lock_list_lru_of_memcg will acquire parent's lock while the + * item is still on child's list. */ for_each_node(i) memcg_reparent_list_lru_one(lru, i, &mlru->node[i], parent); + xa_erase_irq(&lru->xa, memcg->kmemcg_id); + /* * Here all list_lrus corresponding to the cgroup are guaranteed * to remain empty, we can safely free this lru, any further