Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1233190 > unrolled thread

[PATCH 0/5] memcg: charge page tables (x86) and pipe buffers

Started byVladimir Davydov <vdavydov@parallels.com>
First post2015-09-26 12:50 +0200
Last post2015-10-01 21:00 +0200
Articles 6 — 4 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 0/5] memcg: charge page tables (x86) and pipe buffers Vladimir Davydov <vdavydov@parallels.com> - 2015-09-26 12:50 +0200
    [PATCH 1/5] mm: uncharge kmem pages from generic free_page path Vladimir Davydov <vdavydov@parallels.com> - 2015-09-26 12:50 +0200
      Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path Andrew Morton <akpm@linux-foundation.org> - 2015-09-30 00:50 +0200
        Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path Vladimir Davydov <vdavydov@virtuozzo.com> - 2015-09-30 18:50 +0200
      Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path Greg Thelen <gthelen@google.com> - 2015-09-30 22:00 +0200
        Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path Vladimir Davydov <vdavydov@virtuozzo.com> - 2015-10-01 21:00 +0200

#1233190 — [PATCH 0/5] memcg: charge page tables (x86) and pipe buffers

FromVladimir Davydov <vdavydov@parallels.com>
Date2015-09-26 12:50 +0200
Subject[PATCH 0/5] memcg: charge page tables (x86) and pipe buffers
Message-ID<qcVdg-5WW-5@gated-at.bofh.it>
Hi,

There are at least two object types left that can be allocated by an
unprivileged process and go uncharged to memcg - pipe buffers and page
tables. This patch set tries to make them accounted.

Comments are welcome.

Thanks,

Vladimir Davydov (5):
  mm: uncharge kmem pages from generic free_page path
  fs: charge pipe buffers to memcg
  memcg: teach uncharge_list to uncharge kmem pages
  mm: add __get_free_kmem_pages helper
  x86: charge page table pages to memcg

 arch/x86/include/asm/pgalloc.h |  5 +++--
 arch/x86/mm/pgtable.c          |  8 ++++----
 fs/pipe.c                      |  2 +-
 include/linux/gfp.h            |  4 +---
 include/linux/page-flags.h     | 22 ++++++++++++++++++++++
 kernel/fork.c                  |  2 +-
 mm/memcontrol.c                | 21 ++++++++++++++-------
 mm/page_alloc.c                | 38 ++++++++++++++++++++------------------
 mm/slub.c                      |  2 +-
 9 files changed, 67 insertions(+), 37 deletions(-)

-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1233191 — [PATCH 1/5] mm: uncharge kmem pages from generic free_page path

FromVladimir Davydov <vdavydov@parallels.com>
Date2015-09-26 12:50 +0200
Subject[PATCH 1/5] mm: uncharge kmem pages from generic free_page path
Message-ID<qcVdg-5WW-21@gated-at.bofh.it>
In reply to#1233190
Currently, to charge a page to kmemcg one should use alloc_kmem_pages
helper. When the page is not needed anymore it must be freed with
free_kmem_pages helper, which will uncharge the page before freeing it.
Such a design is acceptable for thread info pages and kmalloc large
allocations, which are currently the only users of alloc_kmem_pages, but
it gets extremely inconvenient if one wants to make use of batched free
(e.g. to charge page tables - see release_pages) or page reference
counter (pipe buffers - see anon_pipe_buf_release).

To overcome this limitation, this patch moves kmemcg uncharge code to
the generic free path and zaps free_kmem_pages helper. To distinguish
kmem pages from other page types, it makes alloc_kmem_pages initialize
page->_mapcount to a special value and introduces a new PageKmem helper,
which returns true if it sees this value.

Signed-off-by: Vladimir Davydov <vdavydov@parallels.com>
---
 include/linux/gfp.h        |  3 ---
 include/linux/page-flags.h | 22 ++++++++++++++++++++++
 kernel/fork.c              |  2 +-
 mm/page_alloc.c            | 26 ++++++++------------------
 mm/slub.c                  |  2 +-
 mm/swap.c                  |  3 ++-
 6 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index f92cbd2f4450..b46147c45966 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -384,9 +384,6 @@ extern void *__alloc_page_frag(struct page_frag_cache *nc,
 			       unsigned int fragsz, gfp_t gfp_mask);
 extern void __free_page_frag(void *addr);
 
-extern void __free_kmem_pages(struct page *page, unsigned int order);
-extern void free_kmem_pages(unsigned long addr, unsigned int order);
-
 #define __free_page(page) __free_pages((page), 0)
 #define free_page(addr) free_pages((addr), 0)
 
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 416509e26d6d..a190719c2f46 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -594,6 +594,28 @@ static inline void __ClearPageBalloon(struct page *page)
 }
 
 /*
+ * PageKmem() returns true if the page was allocated with alloc_kmem_pages().
+ */
+#define PAGE_KMEM_MAPCOUNT_VALUE (-512)
+
+static inline int PageKmem(struct page *page)
+{
+	return atomic_read(&page->_mapcount) == PAGE_KMEM_MAPCOUNT_VALUE;
+}
+
+static inline void __SetPageKmem(struct page *page)
+{
+	VM_BUG_ON_PAGE(atomic_read(&page->_mapcount) != -1, page);
+	atomic_set(&page->_mapcount, PAGE_KMEM_MAPCOUNT_VALUE);
+}
+
+static inline void __ClearPageKmem(struct page *page)
+{
+	VM_BUG_ON_PAGE(!PageKmem(page), page);
+	atomic_set(&page->_mapcount, -1);
+}
+
+/*
  * If network-based swap is enabled, sl*b must keep track of whether pages
  * were allocated from pfmemalloc reserves.
  */
diff --git a/kernel/fork.c b/kernel/fork.c
index 2845623fb582..c23f8a17e99e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -169,7 +169,7 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
 
 static inline void free_thread_info(struct thread_info *ti)
 {
-	free_kmem_pages((unsigned long)ti, THREAD_SIZE_ORDER);
+	free_pages((unsigned long)ti, THREAD_SIZE_ORDER);
 }
 # else
 static struct kmem_cache *thread_info_cache;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 48aaf7b9f253..88d85367c81e 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -942,6 +942,10 @@ static bool free_pages_prepare(struct page *page, unsigned int order)
 
 	if (PageAnon(page))
 		page->mapping = NULL;
+	if (PageKmem(page)) {
+		memcg_kmem_uncharge_pages(page, order);
+		__ClearPageKmem(page);
+	}
 	bad += free_pages_check(page);
 	for (i = 1; i < (1 << order); i++) {
 		if (compound)
@@ -3434,6 +3438,8 @@ struct page *alloc_kmem_pages(gfp_t gfp_mask, unsigned int order)
 		return NULL;
 	page = alloc_pages(gfp_mask, order);
 	memcg_kmem_commit_charge(page, memcg, order);
+	if (page)
+		__SetPageKmem(page);
 	return page;
 }
 
@@ -3446,27 +3452,11 @@ struct page *alloc_kmem_pages_node(int nid, gfp_t gfp_mask, unsigned int order)
 		return NULL;
 	page = alloc_pages_node(nid, gfp_mask, order);
 	memcg_kmem_commit_charge(page, memcg, order);
+	if (page)
+		__SetPageKmem(page);
 	return page;
 }
 
-/*
- * __free_kmem_pages and free_kmem_pages will free pages allocated with
- * alloc_kmem_pages.
- */
-void __free_kmem_pages(struct page *page, unsigned int order)
-{
-	memcg_kmem_uncharge_pages(page, order);
-	__free_pages(page, order);
-}
-
-void free_kmem_pages(unsigned long addr, unsigned int order)
-{
-	if (addr != 0) {
-		VM_BUG_ON(!virt_addr_valid((void *)addr));
-		__free_kmem_pages(virt_to_page((void *)addr), order);
-	}
-}
-
 static void *make_alloc_exact(unsigned long addr, unsigned order, size_t size)
 {
 	if (addr) {
diff --git a/mm/slub.c b/mm/slub.c
index f614b5dc396b..f5248a7d9438 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3516,7 +3516,7 @@ void kfree(const void *x)
 	if (unlikely(!PageSlab(page))) {
 		BUG_ON(!PageCompound(page));
 		kfree_hook(x);
-		__free_kmem_pages(page, compound_order(page));
+		__free_pages(page, compound_order(page));
 		return;
 	}
 	slab_free(page->slab_cache, page, object, _RET_IP_);
diff --git a/mm/swap.c b/mm/swap.c
index 983f692a47fd..8d8d03118a18 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -64,7 +64,8 @@ static void __page_cache_release(struct page *page)
 		del_page_from_lru_list(page, lruvec, page_off_lru(page));
 		spin_unlock_irqrestore(&zone->lru_lock, flags);
 	}
-	mem_cgroup_uncharge(page);
+	if (!PageKmem(page))
+		mem_cgroup_uncharge(page);
 }
 
 static void __put_single_page(struct page *page)
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1235620 — Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path

FromAndrew Morton <akpm@linux-foundation.org>
Date2015-09-30 00:50 +0200
SubjectRe: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path
Message-ID<qebSH-3fs-29@gated-at.bofh.it>
In reply to#1233191
On Sat, 26 Sep 2015 13:45:53 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:

> Currently, to charge a page to kmemcg one should use alloc_kmem_pages
> helper. When the page is not needed anymore it must be freed with
> free_kmem_pages helper, which will uncharge the page before freeing it.
> Such a design is acceptable for thread info pages and kmalloc large
> allocations, which are currently the only users of alloc_kmem_pages, but
> it gets extremely inconvenient if one wants to make use of batched free
> (e.g. to charge page tables - see release_pages) or page reference
> counter (pipe buffers - see anon_pipe_buf_release).
> 
> To overcome this limitation, this patch moves kmemcg uncharge code to
> the generic free path and zaps free_kmem_pages helper. To distinguish
> kmem pages from other page types, it makes alloc_kmem_pages initialize
> page->_mapcount to a special value and introduces a new PageKmem helper,
> which returns true if it sees this value.

As far as I can tell, this new use of page._mapcount is OK, but...

- The documentation for _mapcount needs to be updated (mm_types.h)

- Don't believe the documentation!  Because someone else may have
  done what you tried to do.  Please manually audit mm/ for _mapcount
  uses.

- One such use is "For recording whether a page is in the buddy
  system, we set ->_mapcount PAGE_BUDDY_MAPCOUNT_VALUE".  Please update
  the comment for this while you're in there.  (Including description
  of the state's lifetime).

- And please update _mapcount docs for PageBalloon()

- Why is the code accessing ->_mapcount directly?  afaict page_mapcount()
  and friends will work OK?

- The patch adds overhead to all kernels, even non-kmemcg and
  non-memcg kernels.  Bad.  Fixable?

- PAGE_BUDDY_MAPCOUNT_VALUE, PAGE_BALLOON_MAPCOUNT_VALUE and
  PAGE_KMEM_MAPCOUNT_VALUE should all be put next to each other so
  readers can see all the possible values and so we don't get
  duplicates, etc.


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1236542 — Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path

FromVladimir Davydov <vdavydov@virtuozzo.com>
Date2015-09-30 18:50 +0200
SubjectRe: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path
Message-ID<qesJQ-2eW-5@gated-at.bofh.it>
In reply to#1235620
On Tue, Sep 29, 2015 at 03:43:47PM -0700, Andrew Morton wrote:
> On Sat, 26 Sep 2015 13:45:53 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> 
> > Currently, to charge a page to kmemcg one should use alloc_kmem_pages
> > helper. When the page is not needed anymore it must be freed with
> > free_kmem_pages helper, which will uncharge the page before freeing it.
> > Such a design is acceptable for thread info pages and kmalloc large
> > allocations, which are currently the only users of alloc_kmem_pages, but
> > it gets extremely inconvenient if one wants to make use of batched free
> > (e.g. to charge page tables - see release_pages) or page reference
> > counter (pipe buffers - see anon_pipe_buf_release).
> > 
> > To overcome this limitation, this patch moves kmemcg uncharge code to
> > the generic free path and zaps free_kmem_pages helper. To distinguish
> > kmem pages from other page types, it makes alloc_kmem_pages initialize
> > page->_mapcount to a special value and introduces a new PageKmem helper,
> > which returns true if it sees this value.
> 
> As far as I can tell, this new use of page._mapcount is OK, but...
> 
> - The documentation for _mapcount needs to be updated (mm_types.h)
> 
> - Don't believe the documentation!  Because someone else may have
>   done what you tried to do.  Please manually audit mm/ for _mapcount
>   uses.

OK, I rechecked mm/. Here is the list of (ab)users of the
page->_mapcount field:

 - free pages in buddy (PAGE_BUDDY_MAPCOUNT_VALUE)
 - balloon pages (PAGE_BALLOON_MAPCOUNT_VALUE)
 - compound tail pages (use _mapcount for reference counting)

None of them needs PageKmem set by design. The _mapcount is also
overloaded by slab, but the latter doesn't need alloc_kmem_pages for
kmemcg accounting.

However, there is a (ab)user of _mapcount outside mm/. It's arch/x390,
which stores its private info in page table pages' _mapcount. AFAICS
this shouldn't result in any conflicts with the PageKmem helper
introduced by this patch set, because s390 doesn't use generic
tlb_remove_page, but it looks nasty anyway and at least needs a comment.
I'll look what we can do with that.

> 
> - One such use is "For recording whether a page is in the buddy
>   system, we set ->_mapcount PAGE_BUDDY_MAPCOUNT_VALUE".  Please update
>   the comment for this while you're in there.  (Including description
>   of the state's lifetime).
> 
> - And please update _mapcount docs for PageBalloon()
> 
> - Why is the code accessing ->_mapcount directly?  afaict page_mapcount()
>   and friends will work OK?

page_mapcount() lives in mm.h, which isn't included by page-flags.h.
Anyway, I don't think it's a good idea to use page_mapcount() helper
here, because the latter returns not the value of _mapcount, but the
actual count of page mappings (i.e. _mapcount+1), which is IMO confusing
if the page is never mapped and _mapcount is (ab)used for storing a
flag.

> 
> - The patch adds overhead to all kernels, even non-kmemcg and
>   non-memcg kernels.  Bad.  Fixable?

If kmemcg is not used, memcg_kmem_uncharge_pages is a no-op (thanks to
jump labels), so the overhead added is that of load + comparison + store
(i.e. if PageKmem(page) __ClearPageKmem(page)) at worst. For most cases
(!PageKmem) it's just a load + comparison. Taking into account the fact
that page->_mapcount is accessed in free_pages_check anyway, the
overhead should therefore be negligible IMO.

I can, of course, move all PageKmem stuff under CONFIG_MEMCG_KMEM, but
is that vague performance benefit worth obscuring the code?

> 
> - PAGE_BUDDY_MAPCOUNT_VALUE, PAGE_BALLOON_MAPCOUNT_VALUE and
>   PAGE_KMEM_MAPCOUNT_VALUE should all be put next to each other so
>   readers can see all the possible values and so we don't get
>   duplicates, etc.

Right, will do.

Thanks,
Vladimir
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1236711 — Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path

FromGreg Thelen <gthelen@google.com>
Date2015-09-30 22:00 +0200
SubjectRe: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path
Message-ID<qevHI-6td-7@gated-at.bofh.it>
In reply to#1233191
Vladimir Davydov wrote:

> Currently, to charge a page to kmemcg one should use alloc_kmem_pages
> helper. When the page is not needed anymore it must be freed with
> free_kmem_pages helper, which will uncharge the page before freeing it.
> Such a design is acceptable for thread info pages and kmalloc large
> allocations, which are currently the only users of alloc_kmem_pages, but
> it gets extremely inconvenient if one wants to make use of batched free
> (e.g. to charge page tables - see release_pages) or page reference
> counter (pipe buffers - see anon_pipe_buf_release).
>
> To overcome this limitation, this patch moves kmemcg uncharge code to
> the generic free path and zaps free_kmem_pages helper. To distinguish
> kmem pages from other page types, it makes alloc_kmem_pages initialize
> page->_mapcount to a special value and introduces a new PageKmem helper,
> which returns true if it sees this value.
>
> Signed-off-by: Vladimir Davydov <vdavydov@parallels.com>
> ---
>  include/linux/gfp.h        |  3 ---
>  include/linux/page-flags.h | 22 ++++++++++++++++++++++
>  kernel/fork.c              |  2 +-
>  mm/page_alloc.c            | 26 ++++++++------------------
>  mm/slub.c                  |  2 +-
>  mm/swap.c                  |  3 ++-
>  6 files changed, 34 insertions(+), 24 deletions(-)
>
> diff --git a/include/linux/gfp.h b/include/linux/gfp.h
> index f92cbd2f4450..b46147c45966 100644
> --- a/include/linux/gfp.h
> +++ b/include/linux/gfp.h
> @@ -384,9 +384,6 @@ extern void *__alloc_page_frag(struct page_frag_cache *nc,
>  			       unsigned int fragsz, gfp_t gfp_mask);
>  extern void __free_page_frag(void *addr);
>  
> -extern void __free_kmem_pages(struct page *page, unsigned int order);
> -extern void free_kmem_pages(unsigned long addr, unsigned int order);
> -
>  #define __free_page(page) __free_pages((page), 0)
>  #define free_page(addr) free_pages((addr), 0)
>  
> diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> index 416509e26d6d..a190719c2f46 100644
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
> @@ -594,6 +594,28 @@ static inline void __ClearPageBalloon(struct page *page)
>  }
>  
>  /*
> + * PageKmem() returns true if the page was allocated with alloc_kmem_pages().
> + */
> +#define PAGE_KMEM_MAPCOUNT_VALUE (-512)
> +
> +static inline int PageKmem(struct page *page)
> +{
> +	return atomic_read(&page->_mapcount) == PAGE_KMEM_MAPCOUNT_VALUE;
> +}
> +
> +static inline void __SetPageKmem(struct page *page)
> +{
> +	VM_BUG_ON_PAGE(atomic_read(&page->_mapcount) != -1, page);
> +	atomic_set(&page->_mapcount, PAGE_KMEM_MAPCOUNT_VALUE);
> +}

What do you think about several special mapcount values for various
types of kmem?

It's helps user and administrators break down memory usage.

A nice equation is:
  memory.usage_in_bytes = memory.stat[file + anon + unevictable + kmem]

Next, it's helpful to be able to breakdown kmem into:
  kmem = stack + pgtable + slab + ...

On one hand (and the kernel I use internally) we can use separate per
memcg counters for each kmem type.  Then reconstitute memory.kmem as
needed by adding them together.  But using keeping a single kernel kmem
counter is workable if there is a way to breakdown the memory charge to
a container (e.g. by walking /proc/kpageflags-ish or per memcg
memory.kpageflags-ish file).

> +static inline void __ClearPageKmem(struct page *page)
> +{
> +	VM_BUG_ON_PAGE(!PageKmem(page), page);
> +	atomic_set(&page->_mapcount, -1);
> +}
> +
> +/*
>   * If network-based swap is enabled, sl*b must keep track of whether pages
>   * were allocated from pfmemalloc reserves.
>   */
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 2845623fb582..c23f8a17e99e 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -169,7 +169,7 @@ static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
>  
>  static inline void free_thread_info(struct thread_info *ti)
>  {
> -	free_kmem_pages((unsigned long)ti, THREAD_SIZE_ORDER);
> +	free_pages((unsigned long)ti, THREAD_SIZE_ORDER);
>  }
>  # else
>  static struct kmem_cache *thread_info_cache;
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index 48aaf7b9f253..88d85367c81e 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -942,6 +942,10 @@ static bool free_pages_prepare(struct page *page, unsigned int order)
>  
>  	if (PageAnon(page))
>  		page->mapping = NULL;
> +	if (PageKmem(page)) {
> +		memcg_kmem_uncharge_pages(page, order);
> +		__ClearPageKmem(page);
> +	}
>  	bad += free_pages_check(page);
>  	for (i = 1; i < (1 << order); i++) {
>  		if (compound)
> @@ -3434,6 +3438,8 @@ struct page *alloc_kmem_pages(gfp_t gfp_mask, unsigned int order)
>  		return NULL;
>  	page = alloc_pages(gfp_mask, order);
>  	memcg_kmem_commit_charge(page, memcg, order);
> +	if (page)
> +		__SetPageKmem(page);
>  	return page;
>  }
>  
> @@ -3446,27 +3452,11 @@ struct page *alloc_kmem_pages_node(int nid, gfp_t gfp_mask, unsigned int order)
>  		return NULL;
>  	page = alloc_pages_node(nid, gfp_mask, order);
>  	memcg_kmem_commit_charge(page, memcg, order);
> +	if (page)
> +		__SetPageKmem(page);
>  	return page;
>  }
>  
> -/*
> - * __free_kmem_pages and free_kmem_pages will free pages allocated with
> - * alloc_kmem_pages.
> - */
> -void __free_kmem_pages(struct page *page, unsigned int order)
> -{
> -	memcg_kmem_uncharge_pages(page, order);
> -	__free_pages(page, order);
> -}
> -
> -void free_kmem_pages(unsigned long addr, unsigned int order)
> -{
> -	if (addr != 0) {
> -		VM_BUG_ON(!virt_addr_valid((void *)addr));
> -		__free_kmem_pages(virt_to_page((void *)addr), order);
> -	}
> -}
> -
>  static void *make_alloc_exact(unsigned long addr, unsigned order, size_t size)
>  {
>  	if (addr) {
> diff --git a/mm/slub.c b/mm/slub.c
> index f614b5dc396b..f5248a7d9438 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -3516,7 +3516,7 @@ void kfree(const void *x)
>  	if (unlikely(!PageSlab(page))) {
>  		BUG_ON(!PageCompound(page));
>  		kfree_hook(x);
> -		__free_kmem_pages(page, compound_order(page));
> +		__free_pages(page, compound_order(page));
>  		return;
>  	}
>  	slab_free(page->slab_cache, page, object, _RET_IP_);
> diff --git a/mm/swap.c b/mm/swap.c
> index 983f692a47fd..8d8d03118a18 100644
> --- a/mm/swap.c
> +++ b/mm/swap.c
> @@ -64,7 +64,8 @@ static void __page_cache_release(struct page *page)
>  		del_page_from_lru_list(page, lruvec, page_off_lru(page));
>  		spin_unlock_irqrestore(&zone->lru_lock, flags);
>  	}
> -	mem_cgroup_uncharge(page);
> +	if (!PageKmem(page))
> +		mem_cgroup_uncharge(page);
>  }
>  
>  static void __put_single_page(struct page *page)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1237654 — Re: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path

FromVladimir Davydov <vdavydov@virtuozzo.com>
Date2015-10-01 21:00 +0200
SubjectRe: [PATCH 1/5] mm: uncharge kmem pages from generic free_page path
Message-ID<qeRfc-4FF-1@gated-at.bofh.it>
In reply to#1236711
On Wed, Sep 30, 2015 at 12:51:18PM -0700, Greg Thelen wrote:
> 
> Vladimir Davydov wrote:
...
> > diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> > index 416509e26d6d..a190719c2f46 100644
> > --- a/include/linux/page-flags.h
> > +++ b/include/linux/page-flags.h
> > @@ -594,6 +594,28 @@ static inline void __ClearPageBalloon(struct page *page)
> >  }
> >  
> >  /*
> > + * PageKmem() returns true if the page was allocated with alloc_kmem_pages().
> > + */
> > +#define PAGE_KMEM_MAPCOUNT_VALUE (-512)
> > +
> > +static inline int PageKmem(struct page *page)
> > +{
> > +	return atomic_read(&page->_mapcount) == PAGE_KMEM_MAPCOUNT_VALUE;
> > +}
> > +
> > +static inline void __SetPageKmem(struct page *page)
> > +{
> > +	VM_BUG_ON_PAGE(atomic_read(&page->_mapcount) != -1, page);
> > +	atomic_set(&page->_mapcount, PAGE_KMEM_MAPCOUNT_VALUE);
> > +}
> 
> What do you think about several special mapcount values for various
> types of kmem?
> 
> It's helps user and administrators break down memory usage.
> 
> A nice equation is:
>   memory.usage_in_bytes = memory.stat[file + anon + unevictable + kmem]
> 
> Next, it's helpful to be able to breakdown kmem into:
>   kmem = stack + pgtable + slab + ...
> 
> On one hand (and the kernel I use internally) we can use separate per
> memcg counters for each kmem type.  Then reconstitute memory.kmem as
> needed by adding them together.  But using keeping a single kernel kmem
> counter is workable if there is a way to breakdown the memory charge to
> a container (e.g. by walking /proc/kpageflags-ish or per memcg
> memory.kpageflags-ish file).

I don't think that storing information about kmem type on the page
struct just to report it via /proc/kpageflags is a good idea, because
the number of unused bits left on the page struct is limited so we'd
better (ab)use them carefully, only when it's really difficult to get
along w/o them.

OTOH I do agree that some extra info showing what "kmem" is actually
used for could be helpful. To accumulate this info we can always use
per-cpu counters, which are pretty cheap and won't degrade performance,
and then report it via memory.stat. Furthermore, it will be more
convenient for administrators to read this info in human-readable format
than parsing /proc/kpageflags, which in addition takes long on systems
with a lot of RAM.

Thanks,
Vladimir
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web