[Devel] [PATCH rh7] mm/memcg: Fix race on mem cgroup offline/kmem deactivation.
Andrey Ryabinin
aryabinin at virtuozzo.com
Fri May 8 19:10:00 MSK 2020
The following race could happen
Initial state: kmem = 0, cgroup is online
CPU0:
charge_kmem(1); //kmem = 1
memcg_uncharge_kmem()
{
...
kmem = page_counter_uncharge(&memcg->kmem, nr_pages); //kmem = 0
----------------------------------------------------------------------
CPU1:
charge_kmem(1); //kmem=1 again
....
mem_cgroup_css_offline();
memcg_deactivate_kmem() {
...
css_get();
memcg_kmem_mark_dead(memcg);
if (page_counter_read(&memcg->kmem)) //kmem=1, thus return
return;
}
----------------------------------------------------------------------
CPU0:
/* Not down to 0 */
if (kmem)
return;
if (memcg_kmem_test_and_clear_dead(memcg))
css_put(&memcg->css); //kill memcg with kmem != 0
}
Fix this by making a fake kmem charge on cgroup creation, and uncharging it
back during the offline stage. This way we guarantee that kmem is never 0
while cgroup is online, so the race above isn't possible anymore.
https://jira.sw.ru/browse/PSBM-98148
Signed-off-by: Andrey Ryabinin <aryabinin at virtuozzo.com>
Suggested-by: Kirill Tkhai <ktkhai at virtuozzo.com>
---
mm/memcontrol.c | 40 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 4846e7a9bf63..08aa00e0661a 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4794,6 +4794,43 @@ static int __memcg_activate_kmem(struct mem_cgroup *memcg,
memcg->kmemcg_id = memcg_id;
set_bit(KMEM_ACCOUNTED_ACTIVE, &memcg->kmem_account_flags);
set_bit(KMEM_ACCOUNTED_ACTIVATED, &memcg->kmem_account_flags);
+
+ /*
+ * Fake charge to keep kmem > 0 while cgroup is online. This prevents
+ * premature cgroup destruction due to the following race:
+ * CPU0:
+ * charge_kmem(1); //kmem = 1
+ * memcg_uncharge_kmem()
+ * {
+ * ...
+ * kmem = page_counter_uncharge(&memcg->kmem, nr_pages); //kmem = 0
+ *
+ * ----------------------------------------------------------------------
+ *
+ * CPU1:
+ *
+ * charge_kmem(1); //kmem=1 again
+ * ....
+ * mem_cgroup_css_offline();
+ * memcg_deactivate_kmem() {
+ * ...
+ * css_get();
+ * memcg_kmem_mark_dead(memcg);
+ * if (page_counter_read(&memcg->kmem)) //kmem=1, thus return
+ * return;
+ * }
+ * ----------------------------------------------------------------------
+ *
+ * CPU0:
+ *
+ * if (kmem)
+ * return;
+ * if (memcg_kmem_test_and_clear_dead(memcg))
+ * css_put(&memcg->css); //kill memcg with kmem != 0
+ *
+ * }
+ */
+ page_counter_charge(&memcg->kmem, 1);
out:
return err;
}
@@ -6112,7 +6149,8 @@ static void memcg_deactivate_kmem(struct mem_cgroup *memcg)
memcg_kmem_mark_dead(memcg);
- if (page_counter_read(&memcg->kmem))
+ /* Uncharge fake charge from __memcg_activate_kmem() */
+ if (page_counter_uncharge(&memcg->kmem, 1))
return;
/*
--
2.26.2
More information about the Devel
mailing list