[Devel] [PATCH rh7] mm/memcg: Fix race on mem cgroup offline/kmem deactivation.

Kirill Tkhai ktkhai at virtuozzo.com
Fri May 8 19:16:21 MSK 2020


On 08.05.2020 19:10, Andrey Ryabinin wrote:
> 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>

Reviewed-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;
>  
>  	/*
> 



More information about the Devel mailing list