[Devel] [PATCH RH7] memcg leak due to missing css_put on last kmem uncharge
Vasily Averin
vvs at virtuozzo.com
Mon Feb 22 21:40:51 MSK 2021
When mem_cgroup is removes mem_cgroup_css_offline() calls
memcg_deactivate_kmem() which disables kmem accounting.
If memcg still have some charged kmem final css_put is not called,
and delayed till last kmem will be uncharged.
Usually kmem is uncharged by using memcg_uncharge_kmem() which have
according checks and if required calls final css_put().
Though patch added to vz7.162.14 kernel
"mm/memcg: Use per-cpu stock charges for ->kmem and ->cache counters"
enabled kmem charge/uncharge in refill_stock()/drain_stock()
without using of memcg_uncharge_kmem(), as result nobody called
final css_put() after last kmem uncharge.
This patch adds css_get/put for safe access to memcg in drain_stock(),
and calls an additional css_put() after last kmem uncharge.
Fixes: "mm/memcg: Use per-cpu stock charges for ->kmem and ->cache counters"
https://bugs.openvz.org/browse/OVZ-7250
Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
---
mm/memcontrol.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index e0a4309..24e3bd7 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -439,6 +439,8 @@ enum {
KMEM_ACCOUNTED_DEAD, /* dead memcg with pending kmem charges */
};
+static void memcg_kmem_release_css(struct mem_cgroup *memcg);
+
static struct mem_cgroup_per_node *
mem_cgroup_nodeinfo(struct mem_cgroup *memcg, int nid)
{
@@ -2928,11 +2930,15 @@ static void drain_stock(struct memcg_stock_pcp *stock)
{
struct mem_cgroup *old = stock->cached;
unsigned long nr_pages = stock->nr_pages + stock->cache_nr_pages + stock->kmem_nr_pages;
+ u64 kmem = 1;
+
+ if (!old)
+ return;
if (stock->cache_nr_pages)
page_counter_uncharge(&old->cache, stock->cache_nr_pages);
if (stock->kmem_nr_pages)
- page_counter_uncharge(&old->kmem, stock->kmem_nr_pages);
+ kmem = page_counter_uncharge(&old->kmem, stock->kmem_nr_pages);
if (nr_pages) {
page_counter_uncharge(&old->memory, nr_pages);
@@ -2942,6 +2948,9 @@ static void drain_stock(struct memcg_stock_pcp *stock)
stock->kmem_nr_pages = 0;
stock->cache_nr_pages = 0;
}
+ css_put(&old->css);
+ if (kmem == 0)
+ memcg_kmem_release_css(old);
stock->cached = NULL;
}
@@ -2978,6 +2987,7 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages,
if (stock->cached != memcg) { /* reset if necessary */
drain_stock(stock);
+ css_get(&memcg->css);
stock->cached = memcg;
}
@@ -3608,10 +3618,12 @@ void memcg_uncharge_kmem(struct mem_cgroup *memcg,
if (do_swap_account)
page_counter_uncharge(&memcg->memsw, nr_pages);
- /* Not down to 0 */
- if (kmem)
- return;
+ if (kmem == 0)
+ memcg_kmem_release_css(memcg);
+}
+static void memcg_kmem_release_css(struct mem_cgroup *memcg)
+{
/*
* Releases a reference taken in memcg_deactivate_kmem in case
* this last uncharge is racing with the offlining code or it is
--
1.8.3.1
More information about the Devel
mailing list