[Devel] [PATCH RH7] memcg leak due to missing css_put on last kmem uncharge
Kirill Tkhai
ktkhai at virtuozzo.com
Wed Feb 24 11:47:11 MSK 2021
On 22.02.2021 21:40, Vasily Averin wrote:
> 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);
css_put()/css_get() pair looks excess. What is your arguments, why do we need them?
We have a problem with kmem only, while the rest of accounting looks safe:
they are safely uncharged from css_offline->mem_cgroup_reparent_charges(), and they
never own css counter.
> + if (kmem == 0)
> + memcg_kmem_release_css(old);
This looks OK.
> 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)
> +{
We should not mix refactoring and functional changes. This should go in a separate patch.
> /*
> * 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