Re: [PATCH 2/3] memcg: unify slab and other kmem pages charging

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon 05-10-15 01:21:42, Vladimir Davydov wrote:
> We have memcg_kmem_charge and memcg_kmem_uncharge methods for charging
> and uncharging kmem pages to memcg, but currently they are not used for
> charging slab pages (i.e. they are only used for charging pages
> allocated with alloc_kmem_pages). The only reason why the slab subsystem
> uses special helpers, memcg_charge_slab and memcg_uncharge_slab, is that
> it needs to charge to the memcg of kmem cache while memcg_charge_kmem
> charges to the memcg that the current task belongs to.
> 
> To remove this diversity, this patch adds an extra argument to
> __memcg_kmem_charge that can be a pointer to a memcg or NULL. If it is
> not NULL, the function tries to charge to the memcg it points to,
> otherwise it charge to the current context. Next, it makes the slab
> subsystem use this function to charge slab pages.
> 
> Since memcg_charge_kmem and memcg_uncharge_kmem helpers are now used
> only in __memcg_kmem_charge and __memcg_kmem_uncharge, they are inlined.
> Since __memcg_kmem_charge stores a pointer to the memcg in the page
> struct, we don't need memcg_uncharge_slab anymore and can use
> free_kmem_pages. Besides, one can now detect which memcg a slab page
> belongs to by reading /proc/kpagecgroup.

I like it! All the charged pages are marked the same way finally.

> Note, this patch switches slab to charge-after-alloc design. Since this
> design is already used for all other memcg charges, it should not make
> any difference.
> 
> Signed-off-by: Vladimir Davydov <vdavydov@xxxxxxxxxxxxx>

Acked-by: Michal Hocko <mhocko@xxxxxxxx>

> ---
>  include/linux/memcontrol.h |  9 ++----
>  mm/memcontrol.c            | 73 +++++++++++++++++++++-------------------------
>  mm/slab.c                  | 12 ++++----
>  mm/slab.h                  | 24 +++++----------
>  mm/slub.c                  | 12 ++++----
>  5 files changed, 55 insertions(+), 75 deletions(-)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 9e1f4d5efc56..8a9b7a798f14 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -752,7 +752,8 @@ static inline bool memcg_kmem_is_active(struct mem_cgroup *memcg)
>   * conditions, but because they are pretty simple, they are expected to be
>   * fast.
>   */
> -int __memcg_kmem_charge(struct page *page, gfp_t gfp, int order);
> +int __memcg_kmem_charge(struct page *page, gfp_t gfp, int order,
> +			struct mem_cgroup *memcg);
>  void __memcg_kmem_uncharge(struct page *page, int order);
>  
>  /*
> @@ -770,10 +771,6 @@ void __memcg_kmem_put_cache(struct kmem_cache *cachep);
>  
>  struct mem_cgroup *__mem_cgroup_from_kmem(void *ptr);
>  
> -int memcg_charge_kmem(struct mem_cgroup *memcg, gfp_t gfp,
> -		      unsigned long nr_pages);
> -void memcg_uncharge_kmem(struct mem_cgroup *memcg, unsigned long nr_pages);
> -
>  static inline bool __memcg_kmem_bypass(gfp_t gfp)
>  {
>  	if (!memcg_kmem_enabled())
> @@ -798,7 +795,7 @@ static __always_inline int memcg_kmem_charge(struct page *page,
>  {
>  	if (__memcg_kmem_bypass(gfp))
>  		return 0;
> -	return __memcg_kmem_charge(page, gfp, order);
> +	return __memcg_kmem_charge(page, gfp, order, NULL);
>  }
>  
>  /**
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 7c0af36fc8d0..1d6413e0dd29 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -2215,34 +2215,6 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg,
>  }
>  
>  #ifdef CONFIG_MEMCG_KMEM
> -int memcg_charge_kmem(struct mem_cgroup *memcg, gfp_t gfp,
> -		      unsigned long nr_pages)
> -{
> -	struct page_counter *counter;
> -	int ret = 0;
> -
> -	ret = page_counter_try_charge(&memcg->kmem, nr_pages, &counter);
> -	if (ret < 0)
> -		return ret;
> -
> -	ret = try_charge(memcg, gfp, nr_pages);
> -	if (ret)
> -		page_counter_uncharge(&memcg->kmem, nr_pages);
> -
> -	return ret;
> -}
> -
> -void memcg_uncharge_kmem(struct mem_cgroup *memcg, unsigned long nr_pages)
> -{
> -	page_counter_uncharge(&memcg->memory, nr_pages);
> -	if (do_swap_account)
> -		page_counter_uncharge(&memcg->memsw, nr_pages);
> -
> -	page_counter_uncharge(&memcg->kmem, nr_pages);
> -
> -	css_put_many(&memcg->css, nr_pages);
> -}
> -
>  static int memcg_alloc_cache_id(void)
>  {
>  	int id, size;
> @@ -2404,36 +2376,59 @@ void __memcg_kmem_put_cache(struct kmem_cache *cachep)
>  		css_put(&cachep->memcg_params.memcg->css);
>  }
>  
> -int __memcg_kmem_charge(struct page *page, gfp_t gfp, int order)
> +/*
> + * If @memcg != NULL, charge to @memcg, otherwise charge to the memcg the
> + * current task belongs to.
> + */
> +int __memcg_kmem_charge(struct page *page, gfp_t gfp, int order,
> +			struct mem_cgroup *memcg)
>  {
> -	struct mem_cgroup *memcg;
> -	int ret;
> -
> -	memcg = get_mem_cgroup_from_mm(current->mm);
> +	struct page_counter *counter;
> +	unsigned int nr_pages = 1 << order;
> +	bool put = false;
> +	int ret = 0;
>  
> -	if (!memcg_kmem_is_active(memcg)) {
> -		css_put(&memcg->css);
> -		return 0;
> +	if (!memcg) {
> +		memcg = get_mem_cgroup_from_mm(current->mm);
> +		put = true;
>  	}
> +	if (!memcg_kmem_is_active(memcg))
> +		goto out;
>  
> -	ret = memcg_charge_kmem(memcg, gfp, 1 << order);
> +	ret = page_counter_try_charge(&memcg->kmem, nr_pages, &counter);
> +	if (ret)
> +		goto out;
> +
> +	ret = try_charge(memcg, gfp, nr_pages);
> +	if (ret) {
> +		page_counter_uncharge(&memcg->kmem, nr_pages);
> +		goto out;
> +	}
>  
> -	css_put(&memcg->css);
>  	page->mem_cgroup = memcg;
> +out:
> +	if (put)
> +		css_put(&memcg->css);
>  	return ret;
>  }
>  
>  void __memcg_kmem_uncharge(struct page *page, int order)
>  {
>  	struct mem_cgroup *memcg = page->mem_cgroup;
> +	unsigned int nr_pages = 1 << order;
>  
>  	if (!memcg)
>  		return;
>  
>  	VM_BUG_ON_PAGE(mem_cgroup_is_root(memcg), page);
>  
> -	memcg_uncharge_kmem(memcg, 1 << order);
> +	page_counter_uncharge(&memcg->kmem, nr_pages);
> +	page_counter_uncharge(&memcg->memory, nr_pages);
> +	if (do_swap_account)
> +		page_counter_uncharge(&memcg->memsw, nr_pages);
> +
>  	page->mem_cgroup = NULL;
> +	css_put_many(&memcg->css, nr_pages);
>  }
>  
>  struct mem_cgroup *__mem_cgroup_from_kmem(void *ptr)
> diff --git a/mm/slab.c b/mm/slab.c
> index ad6c6f8385d9..037d9d71633a 100644
> --- a/mm/slab.c
> +++ b/mm/slab.c
> @@ -1592,16 +1592,17 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags,
>  	if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
>  		flags |= __GFP_RECLAIMABLE;
>  
> -	if (memcg_charge_slab(cachep, flags, cachep->gfporder))
> -		return NULL;
> -
>  	page = __alloc_pages_node(nodeid, flags | __GFP_NOTRACK, cachep->gfporder);
>  	if (!page) {
> -		memcg_uncharge_slab(cachep, cachep->gfporder);
>  		slab_out_of_memory(cachep, flags, nodeid);
>  		return NULL;
>  	}
>  
> +	if (memcg_charge_slab(page, flags, cachep->gfporder, cachep)) {
> +		__free_pages(page, cachep->gfporder);
> +		return NULL;
> +	}
> +
>  	/* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */
>  	if (page_is_pfmemalloc(page))
>  		pfmemalloc_active = true;
> @@ -1653,8 +1654,7 @@ static void kmem_freepages(struct kmem_cache *cachep, struct page *page)
>  
>  	if (current->reclaim_state)
>  		current->reclaim_state->reclaimed_slab += nr_freed;
> -	__free_pages(page, cachep->gfporder);
> -	memcg_uncharge_slab(cachep, cachep->gfporder);
> +	__free_kmem_pages(page, cachep->gfporder);
>  }
>  
>  static void kmem_rcu_free(struct rcu_head *head)
> diff --git a/mm/slab.h b/mm/slab.h
> index a3a967d7d7c2..16cc5b0de1d8 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -240,23 +240,16 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s)
>  	return s->memcg_params.root_cache;
>  }
>  
> -static __always_inline int memcg_charge_slab(struct kmem_cache *s,
> -					     gfp_t gfp, int order)
> +static __always_inline int memcg_charge_slab(struct page *page,
> +					     gfp_t gfp, int order,
> +					     struct kmem_cache *s)
>  {
>  	if (!memcg_kmem_enabled())
>  		return 0;
>  	if (is_root_cache(s))
>  		return 0;
> -	return memcg_charge_kmem(s->memcg_params.memcg, gfp, 1 << order);
> -}
> -
> -static __always_inline void memcg_uncharge_slab(struct kmem_cache *s, int order)
> -{
> -	if (!memcg_kmem_enabled())
> -		return;
> -	if (is_root_cache(s))
> -		return;
> -	memcg_uncharge_kmem(s->memcg_params.memcg, 1 << order);
> +	return __memcg_kmem_charge(page, gfp, order,
> +				   s->memcg_params.memcg);
>  }
>  
>  extern void slab_init_memcg_params(struct kmem_cache *);
> @@ -295,15 +288,12 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s)
>  	return s;
>  }
>  
> -static inline int memcg_charge_slab(struct kmem_cache *s, gfp_t gfp, int order)
> +static inline int memcg_charge_slab(struct page *page, gfp_t gfp, int order,
> +				    struct kmem_cache *s)
>  {
>  	return 0;
>  }
>  
> -static inline void memcg_uncharge_slab(struct kmem_cache *s, int order)
> -{
> -}
> -
>  static inline void slab_init_memcg_params(struct kmem_cache *s)
>  {
>  }
> diff --git a/mm/slub.c b/mm/slub.c
> index 2b40c186e941..a05388e8a80f 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -1330,16 +1330,15 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s,
>  
>  	flags |= __GFP_NOTRACK;
>  
> -	if (memcg_charge_slab(s, flags, order))
> -		return NULL;
> -
>  	if (node == NUMA_NO_NODE)
>  		page = alloc_pages(flags, order);
>  	else
>  		page = __alloc_pages_node(node, flags, order);
>  
> -	if (!page)
> -		memcg_uncharge_slab(s, order);
> +	if (page && memcg_charge_slab(page, flags, order, s)) {
> +		__free_pages(page, order);
> +		page = NULL;
> +	}
>  
>  	return page;
>  }
> @@ -1478,8 +1477,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
>  	page_mapcount_reset(page);
>  	if (current->reclaim_state)
>  		current->reclaim_state->reclaimed_slab += pages;
> -	__free_pages(page, order);
> -	memcg_uncharge_slab(s, order);
> +	__free_kmem_pages(page, order);
>  }
>  
>  #define need_reserve_slab_rcu						\
> -- 
> 2.1.4

-- 
Michal Hocko
SUSE Labs

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]