[Devel] [PATCH RHEL7 COMMIT] memcg leak due to missing css_put on last kmem uncharge
Vasily Averin
vvs at virtuozzo.com
Mon Feb 22 21:50:18 MSK 2021
The commit is pushed to "branch-rh7-3.10.0-1160.11.1.vz7.172.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1160.11.1.vz7.172.14
------>
commit d70d9e2ba4f7b8515a7fcd7cf2ad39e09a7c334c
Author: Vasily Averin <vvs at virtuozzo.com>
Date: Mon Feb 22 21:50:17 2021 +0300
memcg leak due to missing css_put on last kmem uncharge
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
More information about the Devel
mailing list