From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:22
[resending with all relevant lists in Cc]
Hi,
This patch implements per kmemcg accounting of page tables (x86-only),
pipe buffers, and unix socket buffers.
Basically, this is v2 of my earlier attempt [1], addressing comments by
Andrew, namely: lack of comments to non-standard _mapcount usage, extra
overhead even when kmemcg is unused, wrong handling of stolen pipe
buffer pages.
Patches 1-3 are just cleanups that are not supposed to introduce any
functional changes. Patches 4 and 5 move charge/uncharge to generic page
allocator paths for the sake of accounting pipe and unix socket buffers.
Patches 5-7 make x86 page tables, pipe buffers, and unix socket buffers
accountable.
[1] http://lkml.kernel.org/r/%3Ccover.1443262808.git.vdavydov@parallels.com%3E
Thanks,
Vladimir Davydov (8):
mm: remove pointless struct in struct page definition
mm: clean up non-standard page->_mapcount users
mm: memcontrol: cleanup kmem charge functions
mm: charge/uncharge kmemcg from generic page allocator paths
mm: memcontrol: teach uncharge_list to deal with kmem pages
arch: x86: charge page tables to kmemcg
pipe: account to kmemcg
af_unix: charge buffers to kmemcg
arch/x86/include/asm/pgalloc.h | 12 ++++-
arch/x86/mm/pgtable.c | 11 ++--
fs/pipe.c | 32 ++++++++---
include/linux/gfp.h | 10 +---
include/linux/memcontrol.h | 103 +++---------------------------------
include/linux/mm_types.h | 73 ++++++++++++-------------
include/linux/page-flags.h | 78 +++++++++++++--------------
kernel/fork.c | 6 +--
mm/memcontrol.c | 117 ++++++++++++++++++++++++++++-------------
mm/page_alloc.c | 63 +++++-----------------
mm/slab.h | 16 ++++--
mm/slab_common.c | 2 +-
mm/slub.c | 6 +--
mm/vmalloc.c | 6 +--
net/unix/af_unix.c | 1 +
scripts/tags.sh | 3 ++
16 files changed, 245 insertions(+), 294 deletions(-)
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -60,51 +60,47 @@ struct page {};/* Second double word */-struct{-union{-pgoff_tindex;/* Our offset within mapping. */-void*freelist;/* sl[aou]b first free object */-/* page_deferred_list().prev -- second tail page */-};+union{+pgoff_tindex;/* Our offset within mapping. */+void*freelist;/* sl[aou]b first free object */+/* page_deferred_list().prev -- second tail page */+};-union{+union{#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)-/* Used for cmpxchg_double in slub */-unsignedlongcounters;+/* Used for cmpxchg_double in slub */+unsignedlongcounters;#else-/*-*Keep_refcountseparatefromslubcmpxchg_double-*data.Astherestofthedoublewordisprotectedby-*slab_lockbut_refcountisnot.-*/-unsignedcounters;+/*+*Keep_refcountseparatefromslubcmpxchg_doubledata.+*Astherestofthedoublewordisprotectedbyslab_lock+*but_refcountisnot.+*/+unsignedcounters;#endif+struct{-struct{--union{-/*-*Countofptesmappedinmms,toshow-*whenpageismapped&limitreverse-*mapsearches.-*/-atomic_t_mapcount;--struct{/* SLUB */-unsignedinuse:16;-unsignedobjects:15;-unsignedfrozen:1;-};-intunits;/* SLOB */-};+union{/*-*Usagecount,*USEWRAPPERFUNCTION*-*whenmanualaccounting.Seepage_ref.h+*Countofptesmappedinmms,toshowwhen+*pageismapped&limitreversemapsearches.*/-atomic_t_refcount;+atomic_t_mapcount;++unsignedintactive;/* SLAB */+struct{/* SLUB */+unsignedinuse:16;+unsignedobjects:15;+unsignedfrozen:1;+};+intunits;/* SLOB */};-unsignedintactive;/* SLAB */+/*+*Usagecount,*USEWRAPPERFUNCTION*whenmanual+*accounting.Seepage_ref.h+*/+atomic_t_refcount;};};
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:24
- Add a proper comment to page->_mapcount.
- Introduce a macro for generating helper functions.
- Place all special page->_mapcount values next to each other so that
readers can see all possible values and so we don't get duplicates.
Signed-off-by: Vladimir Davydov <redacted>
---
include/linux/mm_types.h | 5 ++++
include/linux/page-flags.h | 73 ++++++++++++++++++++--------------------------
scripts/tags.sh | 3 ++
3 files changed, 40 insertions(+), 41 deletions(-)
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:25
- Handle memcg_kmem_enabled check out to the caller. This reduces the
number of function definitions making the code easier to follow. At
the same time it doesn't result in code bloat, because all of these
functions are used only in one or two places.
- Move __GFP_ACCOUNT check to the caller as well so that one wouldn't
have to dive deep into memcg implementation to see which allocations
are charged and which are not.
- Refresh comments.
Signed-off-by: Vladimir Davydov <redacted>
---
include/linux/memcontrol.h | 103 +++------------------------------------------
mm/memcontrol.c | 75 ++++++++++++++++++++++++---------
mm/page_alloc.c | 9 ++--
mm/slab.h | 16 +++++--
4 files changed, 80 insertions(+), 123 deletions(-)
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:26
Currently, to charge a non-slab allocation to kmemcg one has to use
alloc_kmem_pages helper with __GFP_ACCOUNT flag. A page allocated with
this helper should finally be freed using free_kmem_pages, otherwise it
won't be uncharged.
This API suits its current users fine, but it turns out to be impossible
to use along with page reference counting, i.e. when an allocation is
supposed to be freed with put_page, as it is the case with pipe or unix
socket buffers.
To overcome this limitation, this patch moves charging/uncharging to
generic page allocator paths, i.e. to __alloc_pages_nodemask and
free_pages_prepare, and zaps alloc/free_kmem_pages helpers. This way,
one can use any of the available page allocation functions to get the
allocated page charged to kmemcg - it's enough to pass __GFP_ACCOUNT,
just like in case of kmalloc and friends. A charged page will be
automatically uncharged on free.
To make it possible, we need to mark pages charged to kmemcg somehow. To
avoid introducing a new page flag, we make use of page->_mapcount for
marking such pages. Since pages charged to kmemcg are not supposed to be
mapped to userspace, it should work just fine. There are other (ab)users
of page->_mapcount - buddy and balloon pages - but we don't conflict
with them.
In case kmemcg is compiled out or not used at runtime, this patch
introduces no overhead to generic page allocator paths. If kmemcg is
used, it will be plus one gfp flags check on alloc and plus one
page->_mapcount check on free, which shouldn't hurt performance, because
the data accessed are hot.
Signed-off-by: Vladimir Davydov <redacted>
---
include/linux/gfp.h | 10 +------
include/linux/page-flags.h | 7 +++++
kernel/fork.c | 6 ++---
mm/page_alloc.c | 66 +++++++++-------------------------------------
mm/slab_common.c | 2 +-
mm/slub.c | 6 ++---
mm/vmalloc.c | 6 ++---
7 files changed, 31 insertions(+), 72 deletions(-)
@@ -1628,9 +1628,9 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,structpage*page;if(node==NUMA_NO_NODE)-page=alloc_kmem_pages(alloc_mask,order);+page=alloc_pages(alloc_mask,order);else-page=alloc_kmem_pages_node(node,alloc_mask,order);+page=alloc_pages_node(node,alloc_mask,order);if(unlikely(!page)){/* Successfully allocated i pages, free them in __vunmap() */
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:27
Page table pages are batched-freed in release_pages on most
architectures. If we want to charge them to kmemcg (this is what is done
later in this series), we need to teach mem_cgroup_uncharge_list to
handle kmem pages.
Signed-off-by: Vladimir Davydov <redacted>
---
mm/memcontrol.c | 42 ++++++++++++++++++++++++------------------
1 file changed, 24 insertions(+), 18 deletions(-)
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:28
Page tables can bite a relatively big chunk off system memory and their
allocations are easy to trigger from userspace, so they should be
accounted to kmemcg.
This patch marks page table allocations as __GFP_ACCOUNT for x86. Note
we must not charge allocations of kernel page tables, because they can
be shared among processes from different cgroups so accounting them to a
particular one can pin other cgroups for indefinitely long. So we clear
__GFP_ACCOUNT flag if a page table is allocated for the kernel.
Signed-off-by: Vladimir Davydov <redacted>
Cc: Thomas Gleixner <redacted>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
---
arch/x86/include/asm/pgalloc.h | 12 ++++++++++--
arch/x86/mm/pgtable.c | 11 ++++++++---
2 files changed, 18 insertions(+), 5 deletions(-)
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:29
Pipes can consume a significant amount of system memory, hence they
should be accounted to kmemcg.
This patch marks pipe_inode_info and anonymous pipe buffer page
allocations as __GFP_ACCOUNT so that they would be charged to kmemcg.
Note, since a pipe buffer page can be "stolen" and get reused for other
purposes, including mapping to userspace, we clear PageKmemcg thus
resetting page->_mapcount and uncharge it in anon_pipe_buf_steal, which
is introduced by this patch.
Signed-off-by: Vladimir Davydov <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
---
fs/pipe.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
@@ -1010,7 +1029,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)if(nr_pages<pipe->nrbufs)return-EBUSY;-bufs=kcalloc(nr_pages,sizeof(*bufs),GFP_KERNEL|__GFP_NOWARN);+bufs=kcalloc(nr_pages,sizeof(*bufs),+GFP_KERNEL_ACCOUNT|__GFP_NOWARN);if(unlikely(!bufs))return-ENOMEM;
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 08:49:30
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
Signed-off-by: Vladimir Davydov <redacted>
Cc: "David S. Miller" <davem@davemloft.net>
---
net/unix/af_unix.c | 1 +
1 file changed, 1 insertion(+)
@@ -769,6 +769,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern)lockdep_set_class(&sk->sk_receive_queue.lock,&af_unix_sk_receive_queue_lock_key);+sk->sk_allocation=GFP_KERNEL_ACCOUNT;sk->sk_write_space=unix_write_space;sk->sk_max_ack_backlog=net->unx.sysctl_max_dgram_qlen;sk->sk_destruct=unix_sock_destructor;
--
2.1.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Eric Dumazet <hidden> Date: 2016-05-24 12:59:02
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
quoted hunk
Pipes can consume a significant amount of system memory, hence they
should be accounted to kmemcg.
This patch marks pipe_inode_info and anonymous pipe buffer page
allocations as __GFP_ACCOUNT so that they would be charged to kmemcg.
Note, since a pipe buffer page can be "stolen" and get reused for other
purposes, including mapping to userspace, we clear PageKmemcg thus
resetting page->_mapcount and uncharge it in anon_pipe_buf_steal, which
is introduced by this patch.
Signed-off-by: Vladimir Davydov <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
---
fs/pipe.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Eric Dumazet <hidden> Date: 2016-05-24 13:02:06
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
I have two questions :
1) What happens when a buffer, allocated from socket <A> lands in a
different socket <B>, maybe owned by another user/process.
Who owns it now, in term of kmemcg accounting ?
2) Has performance impact been evaluated ?
Thanks.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
Thanks,
Vladimir
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-05-24 16:36:06
On Tue, May 24, 2016 at 06:02:06AM -0700, Eric Dumazet wrote:
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
quoted
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
I have two questions :
1) What happens when a buffer, allocated from socket <A> lands in a
different socket <B>, maybe owned by another user/process.
Who owns it now, in term of kmemcg accounting ?
We never move memcg charges. E.g. if two processes from different
cgroups are sharing a memory region, each page will be charged to the
process which touched it first. Or if two processes are working with the
same directory tree, inodes and dentries will be charged to the first
user. The same is fair for unix socket buffers - they will be charged to
the sender.
2) Has performance impact been evaluated ?
I ran netperf STREAM_STREAM with default options in a kmemcg on
a 4 core x 2 HT box. The results are below:
# clients bandwidth (10^6bits/sec)
base patched
1 67643 +- 725 64874 +- 353 - 4.0 %
4 193585 +- 2516 186715 +- 1460 - 3.5 %
8 194820 +- 377 187443 +- 1229 - 3.7 %
So the accounting doesn't come for free - it takes ~4% of performance.
I believe we could optimize it by using per cpu batching not only on
charge, but also on uncharge in memcg core, but that's beyond the scope
of this patch set - I'll take a look at this later.
Anyway, if performance impact is found to be unacceptable, it is always
possible to disable kmem accounting at boot time (cgroup.memory=nokmem)
or not use memory cgroups at runtime at all (thanks to jump labels
there'll be no overhead even if they are compiled in).
Thanks,
Vladimir
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
Each pipe_buffer holds a reference to its page. If we find page's
refcount to be 1 here, then it can be referenced only by our
pipe_buffer. And the refcount cannot be increased by a parallel thread,
because we hold pipe_lock, which rules out splice, and otherwise it's
impossible to reach the page as it is not on lru. That said, I think I
guarantee that this should be safe.
Thanks,
Vladimir
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
Each pipe_buffer holds a reference to its page. If we find page's
refcount to be 1 here, then it can be referenced only by our
pipe_buffer. And the refcount cannot be increased by a parallel thread,
because we hold pipe_lock, which rules out splice, and otherwise it's
impossible to reach the page as it is not on lru. That said, I think I
guarantee that this should be safe.
I don't know kmemcg internal and pipe stuff so my comment might be
totally crap.
No one cannot guarantee any CPU cannot held a reference of a page.
Look at get_page_unless_zero usecases.
1. balloon_page_isolate
It can hold a reference in random page and then verify the page
is balloon page. Otherwise, just put.
2. page_idle_get_page
It has PageLRU check but it's racy so it can hold a reference
of randome page and then verify within zone->lru_lock. If it's
not LRU page, just put.
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
Each pipe_buffer holds a reference to its page. If we find page's
refcount to be 1 here, then it can be referenced only by our
pipe_buffer. And the refcount cannot be increased by a parallel thread,
because we hold pipe_lock, which rules out splice, and otherwise it's
impossible to reach the page as it is not on lru. That said, I think I
guarantee that this should be safe.
I don't know kmemcg internal and pipe stuff so my comment might be
totally crap.
No one cannot guarantee any CPU cannot held a reference of a page.
Look at get_page_unless_zero usecases.
1. balloon_page_isolate
It can hold a reference in random page and then verify the page
is balloon page. Otherwise, just put.
2. page_idle_get_page
It has PageLRU check but it's racy so it can hold a reference
of randome page and then verify within zone->lru_lock. If it's
not LRU page, just put.
Well, I see your concern now - even if a page is not on lru and we
locked all structs pointing to it, it can always get accessed by pfn in
a completely unrelated thread, like in examples you gave above. That's a
fair point.
However, I still think that it's OK in case of pipe buffers. What can
happen if somebody takes a transient reference to a pipe buffer page? At
worst, we'll see page_count > 1 due to temporary ref and abort stealing,
falling back on copying instead. That's OK, because stealing is not
guaranteed. Can a function that takes a transient ref to page by pfn
mistakenly assume that this is a page it's interested in? I don't think
so, because this page has no marks on it except special _mapcount value,
which should only be set on kmemcg pages.
Thanks,
Vladimir
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
Each pipe_buffer holds a reference to its page. If we find page's
refcount to be 1 here, then it can be referenced only by our
pipe_buffer. And the refcount cannot be increased by a parallel thread,
because we hold pipe_lock, which rules out splice, and otherwise it's
impossible to reach the page as it is not on lru. That said, I think I
guarantee that this should be safe.
I don't know kmemcg internal and pipe stuff so my comment might be
totally crap.
No one cannot guarantee any CPU cannot held a reference of a page.
Look at get_page_unless_zero usecases.
1. balloon_page_isolate
It can hold a reference in random page and then verify the page
is balloon page. Otherwise, just put.
2. page_idle_get_page
It has PageLRU check but it's racy so it can hold a reference
of randome page and then verify within zone->lru_lock. If it's
not LRU page, just put.
Well, I see your concern now - even if a page is not on lru and we
locked all structs pointing to it, it can always get accessed by pfn in
a completely unrelated thread, like in examples you gave above. That's a
fair point.
However, I still think that it's OK in case of pipe buffers. What can
happen if somebody takes a transient reference to a pipe buffer page? At
worst, we'll see page_count > 1 due to temporary ref and abort stealing,
falling back on copying instead. That's OK, because stealing is not
guaranteed. Can a function that takes a transient ref to page by pfn
mistakenly assume that this is a page it's interested in? I don't think
so, because this page has no marks on it except special _mapcount value,
which should only be set on kmemcg pages.
Well, all this information deserve to be in the changelog.
Maybe in 6 months, this will be incredibly useful for bug hunting.
pipes can be used to exchange data (or pages) between processes in
different domains.
If kmemcg is not precise, this could be used by some attackers to force
some processes to consume all their budget and eventually not be able to
allocate new pages.
This looks racy : some cpu could have temporarily elevated page count.
All pipe operations (pipe_buf_operations->get, ->release, ->steal) are
supposed to be called under pipe_lock. So, if we see a pipe_buffer->page
with refcount of 1 in ->steal, that means that we are the only its user
and it can't be spliced to another pipe.
In fact, I just copied the code from generic_pipe_buf_steal, adding
kmemcg related checks along the way, so it should be fine.
So you guarantee that no other cpu might have done
get_page_unless_zero() right before this test ?
Each pipe_buffer holds a reference to its page. If we find page's
refcount to be 1 here, then it can be referenced only by our
pipe_buffer. And the refcount cannot be increased by a parallel thread,
because we hold pipe_lock, which rules out splice, and otherwise it's
impossible to reach the page as it is not on lru. That said, I think I
guarantee that this should be safe.
I don't know kmemcg internal and pipe stuff so my comment might be
totally crap.
No one cannot guarantee any CPU cannot held a reference of a page.
Look at get_page_unless_zero usecases.
1. balloon_page_isolate
It can hold a reference in random page and then verify the page
is balloon page. Otherwise, just put.
2. page_idle_get_page
It has PageLRU check but it's racy so it can hold a reference
of randome page and then verify within zone->lru_lock. If it's
not LRU page, just put.
Well, I see your concern now - even if a page is not on lru and we
locked all structs pointing to it, it can always get accessed by pfn in
a completely unrelated thread, like in examples you gave above. That's a
fair point.
However, I still think that it's OK in case of pipe buffers. What can
happen if somebody takes a transient reference to a pipe buffer page? At
worst, we'll see page_count > 1 due to temporary ref and abort stealing,
falling back on copying instead. That's OK, because stealing is not
guaranteed. Can a function that takes a transient ref to page by pfn
mistakenly assume that this is a page it's interested in? I don't think
so, because this page has no marks on it except special _mapcount value,
which should only be set on kmemcg pages.
Well, all this information deserve to be in the changelog.
Maybe in 6 months, this will be incredibly useful for bug hunting.
pipes can be used to exchange data (or pages) between processes in
different domains.
If kmemcg is not precise, this could be used by some attackers to force
some processes to consume all their budget and eventually not be able to
allocate new pages.
Here goes the patch with updated change log.
---
From: Vladimir Davydov <redacted>
Subject: [PATCH] pipe: account to kmemcg
Pipes can consume a significant amount of system memory, hence they
should be accounted to kmemcg.
This patch marks pipe_inode_info and anonymous pipe buffer page
allocations as __GFP_ACCOUNT so that they would be charged to kmemcg.
Note, since a pipe buffer page can be "stolen" and get reused for other
purposes, including mapping to userspace, we clear PageKmemcg thus
resetting page->_mapcount and uncharge it in anon_pipe_buf_steal, which
is introduced by this patch.
A note regarding anon_pipe_buf_steal implementation. We allow to steal
the page if its ref count equals 1. It looks racy, but it is correct for
anonymous pipe buffer pages, because:
- We lock out all other pipe users, because ->steal is called with
pipe_lock held, so the page can't be spliced to another pipe from
under us.
- The page is not on LRU and it never was.
- Thus a parallel thread can access it only by PFN. Although this is
quite possible (e.g. see page_idle_get_page and balloon_page_isolate)
this is not dangerous, because all such functions do is increase page
ref count, check if the page is the one they are looking for, and
decrease ref count if it isn't. Since our page is clean except for
PageKmemcg mark, which doesn't conflict with other _mapcount users,
the worst that can happen is we see page_count > 2 due to a transient
ref, in which case we false-positively abort ->steal, which is still
fine, because ->steal is not guaranteed to succeed.
Signed-off-by: Vladimir Davydov <redacted>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
@@ -1010,7 +1029,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)if(nr_pages<pipe->nrbufs)return-EBUSY;-bufs=kcalloc(nr_pages,sizeof(*bufs),GFP_KERNEL|__GFP_NOWARN);+bufs=kcalloc(nr_pages,sizeof(*bufs),+GFP_KERNEL_ACCOUNT|__GFP_NOWARN);if(unlikely(!bufs))return-ENOMEM;--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Sudeep K N <hidden> Date: 2016-08-23 13:48:11
On Tue, May 24, 2016 at 5:36 PM, Vladimir Davydov
[off-list ref] wrote:
On Tue, May 24, 2016 at 06:02:06AM -0700, Eric Dumazet wrote:
quoted
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
quoted
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
I have two questions :
1) What happens when a buffer, allocated from socket <A> lands in a
different socket <B>, maybe owned by another user/process.
Who owns it now, in term of kmemcg accounting ?
We never move memcg charges. E.g. if two processes from different
cgroups are sharing a memory region, each page will be charged to the
process which touched it first. Or if two processes are working with the
same directory tree, inodes and dentries will be charged to the first
user. The same is fair for unix socket buffers - they will be charged to
the sender.
quoted
2) Has performance impact been evaluated ?
I ran netperf STREAM_STREAM with default options in a kmemcg on
a 4 core x 2 HT box. The results are below:
# clients bandwidth (10^6bits/sec)
base patched
1 67643 +- 725 64874 +- 353 - 4.0 %
4 193585 +- 2516 186715 +- 1460 - 3.5 %
8 194820 +- 377 187443 +- 1229 - 3.7 %
So the accounting doesn't come for free - it takes ~4% of performance.
I believe we could optimize it by using per cpu batching not only on
charge, but also on uncharge in memcg core, but that's beyond the scope
of this patch set - I'll take a look at this later.
Anyway, if performance impact is found to be unacceptable, it is always
possible to disable kmem accounting at boot time (cgroup.memory=nokmem)
or not use memory cgroups at runtime at all (thanks to jump labels
there'll be no overhead even if they are compiled in).
I started seeing almost 10% degradation in the hackbench score with v4.8-rc1
Bisecting it resulted in this patch, i.e. Commit 3aa9799e1364 ("af_unix: charge
buffers to kmemcg") in the mainline.
As per the commit log, it seems like that's expected but I was not sure about
the margin. I also see the hackbench score is more inconsistent after this
patch, but I may be wrong as that's based on limited observation.
Is this something we can ignore as hackbench is more synthetic compared
to the gain this patch provides in some real workloads ?
--
Regards,
Sudeep
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Vladimir Davydov <hidden> Date: 2016-08-23 16:44:59
Hello,
On Tue, Aug 23, 2016 at 02:48:11PM +0100, Sudeep K N wrote:
On Tue, May 24, 2016 at 5:36 PM, Vladimir Davydov
[off-list ref] wrote:
quoted
On Tue, May 24, 2016 at 06:02:06AM -0700, Eric Dumazet wrote:
quoted
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
quoted
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
I have two questions :
1) What happens when a buffer, allocated from socket <A> lands in a
different socket <B>, maybe owned by another user/process.
Who owns it now, in term of kmemcg accounting ?
We never move memcg charges. E.g. if two processes from different
cgroups are sharing a memory region, each page will be charged to the
process which touched it first. Or if two processes are working with the
same directory tree, inodes and dentries will be charged to the first
user. The same is fair for unix socket buffers - they will be charged to
the sender.
quoted
2) Has performance impact been evaluated ?
I ran netperf STREAM_STREAM with default options in a kmemcg on
a 4 core x 2 HT box. The results are below:
# clients bandwidth (10^6bits/sec)
base patched
1 67643 +- 725 64874 +- 353 - 4.0 %
4 193585 +- 2516 186715 +- 1460 - 3.5 %
8 194820 +- 377 187443 +- 1229 - 3.7 %
So the accounting doesn't come for free - it takes ~4% of performance.
I believe we could optimize it by using per cpu batching not only on
charge, but also on uncharge in memcg core, but that's beyond the scope
of this patch set - I'll take a look at this later.
Anyway, if performance impact is found to be unacceptable, it is always
possible to disable kmem accounting at boot time (cgroup.memory=nokmem)
or not use memory cgroups at runtime at all (thanks to jump labels
there'll be no overhead even if they are compiled in).
I started seeing almost 10% degradation in the hackbench score with v4.8-rc1
Bisecting it resulted in this patch, i.e. Commit 3aa9799e1364 ("af_unix: charge
buffers to kmemcg") in the mainline.
As per the commit log, it seems like that's expected but I was not sure about
the margin. I also see the hackbench score is more inconsistent after this
patch, but I may be wrong as that's based on limited observation.
Is this something we can ignore as hackbench is more synthetic compared
to the gain this patch provides in some real workloads ?
AFAIU hackbench essentially measures the rate of sending data over a
unix socket back and forth between processes running on different cpus,
so it isn't a surprise that the patch resulted in a degradation, as it
makes every skb page allocation/deallocation inc/dec an atomic counter
inside memcg. The more processes/cpus running in the same cgroup are
involved in this test, the more significant the overhead of this atomic
counter is going to be.
The degradation is not unavoidable - it can be fixed by making kmem
charge/uncharge code use per-cpu batches. The infrastructure for this
already exists in memcontrol.c. If it were not for the legacy
mem_cgroup->kmem counter (which is actually useless and will be dropped
in cgroup v2), the issue would be pretty easy to fix. However, this
legacy counter makes a possible implementation quite messy, so I'd like
to postpone it until cgroup v2 has finally settled down.
Regarding your problem. As a workaround you can either start your
workload in the root memory cgroup or disable kmem accounting for memory
cgroups altogether (via cgroup.memory=nokmem boot option). If you find
the issue critical, I don't mind reverting the patch - we can always
re-apply it once per-cpu batches are implemented for kmem charges.
Thanks,
Vladimir
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hello,
On Tue, Aug 23, 2016 at 02:48:11PM +0100, Sudeep K N wrote:
quoted
On Tue, May 24, 2016 at 5:36 PM, Vladimir Davydov
[off-list ref] wrote:
quoted
On Tue, May 24, 2016 at 06:02:06AM -0700, Eric Dumazet wrote:
quoted
On Tue, 2016-05-24 at 11:49 +0300, Vladimir Davydov wrote:
quoted
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context,
all we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
I have two questions :
1) What happens when a buffer, allocated from socket <A> lands in a
different socket <B>, maybe owned by another user/process.
Who owns it now, in term of kmemcg accounting ?
We never move memcg charges. E.g. if two processes from different
cgroups are sharing a memory region, each page will be charged to the
process which touched it first. Or if two processes are working with the
same directory tree, inodes and dentries will be charged to the first
user. The same is fair for unix socket buffers - they will be charged to
the sender.
quoted
2) Has performance impact been evaluated ?
I ran netperf STREAM_STREAM with default options in a kmemcg on
a 4 core x 2 HT box. The results are below:
# clients bandwidth (10^6bits/sec)
base patched
1 67643 +- 725 64874 +- 353 - 4.0 %
4 193585 +- 2516 186715 +- 1460 - 3.5 %
8 194820 +- 377 187443 +- 1229 - 3.7 %
So the accounting doesn't come for free - it takes ~4% of performance.
I believe we could optimize it by using per cpu batching not only on
charge, but also on uncharge in memcg core, but that's beyond the scope
of this patch set - I'll take a look at this later.
Anyway, if performance impact is found to be unacceptable, it is always
possible to disable kmem accounting at boot time (cgroup.memory=nokmem)
or not use memory cgroups at runtime at all (thanks to jump labels
there'll be no overhead even if they are compiled in).
I started seeing almost 10% degradation in the hackbench score with v4.8-rc1
Bisecting it resulted in this patch, i.e. Commit 3aa9799e1364 ("af_unix: charge
buffers to kmemcg") in the mainline.
As per the commit log, it seems like that's expected but I was not sure about
the margin. I also see the hackbench score is more inconsistent after this
patch, but I may be wrong as that's based on limited observation.
Is this something we can ignore as hackbench is more synthetic compared
to the gain this patch provides in some real workloads ?
AFAIU hackbench essentially measures the rate of sending data over a
unix socket back and forth between processes running on different cpus,
so it isn't a surprise that the patch resulted in a degradation, as it
makes every skb page allocation/deallocation inc/dec an atomic counter
inside memcg. The more processes/cpus running in the same cgroup are
involved in this test, the more significant the overhead of this atomic
counter is going to be.
Understood.
The degradation is not unavoidable - it can be fixed by making kmem
charge/uncharge code use per-cpu batches. The infrastructure for this
already exists in memcontrol.c. If it were not for the legacy
mem_cgroup->kmem counter (which is actually useless and will be dropped
in cgroup v2), the issue would be pretty easy to fix. However, this
legacy counter makes a possible implementation quite messy, so I'd like
to postpone it until cgroup v2 has finally settled down.
Sure
Regarding your problem. As a workaround you can either start your
workload in the root memory cgroup or disable kmem accounting for memory
cgroups altogether (via cgroup.memory=nokmem boot option). If you find
the issue critical, I don't mind reverting the patch - we can always
re-apply it once per-cpu batches are implemented for kmem charges.
I did try "cgroup.memory=nokmem" as specified in the commit message, I
saw the result to be not so consistent. I need to check again to be sure.
I am not asking to revert, just wanted to know if that's expected so
that we can adjust the scores when comparing especially if we are using
it as some kind of benchmark in development.
--
Regards,
Sudeep
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>