lkml.org 
[lkml]   [2020]   [Mar]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [PATCH] mm: fork: fix kernel_stack memcg stats for various stack implementations
On Sat, Mar 21, 2020 at 04:48:56PM -0700, Andrew Morton wrote:
> On Tue, 3 Mar 2020 15:35:50 -0800 Roman Gushchin <guro@fb.com> wrote:
>
> > Depending on CONFIG_VMAP_STACK and the THREAD_SIZE / PAGE_SIZE ratio
> > the space for task stacks can be allocated using __vmalloc_node_range(),
> > alloc_pages_node() and kmem_cache_alloc_node(). In the first and the
> > second cases page->mem_cgroup pointer is set, but in the third it's
> > not: memcg membership of a slab page should be determined using the
> > memcg_from_slab_page() function, which looks at
> > page->slab_cache->memcg_params.memcg . In this case, using
> > mod_memcg_page_state() (as in account_kernel_stack()) is incorrect:
> > page->mem_cgroup pointer is NULL even for pages charged to a non-root
> > memory cgroup.
> >
> > It can lead to kernel_stack per-memcg counters permanently showing 0
> > on some architectures (depending on the configuration).
> >
> > In order to fix it, let's introduce a mod_memcg_obj_state() helper,
> > which takes a pointer to a kernel object as a first argument, uses
> > mem_cgroup_from_obj() to get a RCU-protected memcg pointer and
> > calls mod_memcg_state(). It allows to handle all possible
> > configurations (CONFIG_VMAP_STACK and various THREAD_SIZE/PAGE_SIZE
> > values) without spilling any memcg/kmem specifics into fork.c .
> >
> > Note: this patch has been first posted as a part of the new slab
> > controller patchset. This is a slightly updated version: the fixes
> > tag has been added and the commit log was extended by the advice
> > of Johannes Weiner. Because it's a fix that makes sense by itself,
> > I'm re-posting it as a standalone patch.
>
> Actually, it isn't a standalone patch.
>
> > --- a/mm/memcontrol.c
> > +++ b/mm/memcontrol.c
> > @@ -776,6 +776,17 @@ void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val)
> > rcu_read_unlock();
> > }
> >
> > +void mod_memcg_obj_state(void *p, int idx, int val)
> > +{
> > + struct mem_cgroup *memcg;
> > +
> > + rcu_read_lock();
> > + memcg = mem_cgroup_from_obj(p);
> > + if (memcg)
> > + mod_memcg_state(memcg, idx, val);
> > + rcu_read_unlock();
> > +}
>
> mem_cgroup_from_obj() is later added by
> http://lkml.kernel.org/r/20200117203609.3146239-1-guro@fb.com
>
> We could merge both mm-memcg-slab-introduce-mem_cgroup_from_obj.patch
> and this patch, but that's a whole lot of stuff to backport into
> -stable.
>
> Are you able to come up with a simpler suitable-for-stable fix?

How about this one? I've merged them into one and stripped it a little bit.

Thanks!

--

From 1b8b039b05d49945aaf34a0600b04ea616fe0ba2 Mon Sep 17 00:00:00 2001
From: Roman Gushchin <guro@fb.com>
Date: Sat, 21 Mar 2020 23:05:42 +0000
Subject: [PATCH] mm: fork: fix kernel_stack memcg stats for various stack
implementations

Depending on CONFIG_VMAP_STACK and the THREAD_SIZE / PAGE_SIZE ratio the
space for task stacks can be allocated using __vmalloc_node_range(),
alloc_pages_node() and kmem_cache_alloc_node(). In the first and the
second cases page->mem_cgroup pointer is set, but in the third it's not:
memcg membership of a slab page should be determined using the
memcg_from_slab_page() function, which looks at
page->slab_cache->memcg_params.memcg . In this case, using
mod_memcg_page_state() (as in account_kernel_stack()) is incorrect:
page->mem_cgroup pointer is NULL even for pages charged to a non-root
memory cgroup.

It can lead to kernel_stack per-memcg counters permanently showing 0 on
some architectures (depending on the configuration).

In order to fix it, let's introduce a mod_memcg_obj_state() helper, which
takes a pointer to a kernel object as a first argument, uses
mem_cgroup_from_obj() to get a RCU-protected memcg pointer and calls
mod_memcg_state(). It allows to handle all possible configurations
(CONFIG_VMAP_STACK and various THREAD_SIZE/PAGE_SIZE values) without
spilling any memcg/kmem specifics into fork.c .

Note: This is a special version of the patch created for stable
backports. It contains code from the following two patches:
- mm: memcg/slab: introduce mem_cgroup_from_obj()
- mm: fork: fix kernel_stack memcg stats for various stack implementations

Fixes: 4d96ba353075 ("mm: memcg/slab: stop setting page->mem_cgroup pointer for slab pages")
Signed-off-by: Roman Gushchin <guro@fb.com>
Cc: stable@vger.kernel.org
---
include/linux/memcontrol.h | 12 ++++++++++++
kernel/fork.c | 4 ++--
mm/memcontrol.c | 38 ++++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index a7a0a1a5c8d5..e9ba01336d4e 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -695,6 +695,7 @@ static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx,
int val);
void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val);
+void mod_memcg_obj_state(void *p, int idx, int val);

static inline void mod_lruvec_state(struct lruvec *lruvec,
enum node_stat_item idx, int val)
@@ -1123,6 +1124,10 @@ static inline void __mod_lruvec_slab_state(void *p, enum node_stat_item idx,
__mod_node_page_state(page_pgdat(page), idx, val);
}

+static inline void mod_memcg_obj_state(void *p, int idx, int val)
+{
+}
+
static inline
unsigned long mem_cgroup_soft_limit_reclaim(pg_data_t *pgdat, int order,
gfp_t gfp_mask,
@@ -1427,6 +1432,8 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)
return memcg ? memcg->kmemcg_id : -1;
}

+struct mem_cgroup *mem_cgroup_from_obj(void *p);
+
#else

static inline int memcg_kmem_charge(struct page *page, gfp_t gfp, int order)
@@ -1468,6 +1475,11 @@ static inline void memcg_put_cache_ids(void)
{
}

+static inline struct mem_cgroup *mem_cgroup_from_obj(void *p)
+{
+ return NULL;
+}
+
#endif /* CONFIG_MEMCG_KMEM */

#endif /* _LINUX_MEMCONTROL_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 080809560072..183a6722dfe2 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -397,8 +397,8 @@ static void account_kernel_stack(struct task_struct *tsk, int account)
mod_zone_page_state(page_zone(first_page), NR_KERNEL_STACK_KB,
THREAD_SIZE / 1024 * account);

- mod_memcg_page_state(first_page, MEMCG_KERNEL_STACK_KB,
- account * (THREAD_SIZE / 1024));
+ mod_memcg_obj_state(stack, MEMCG_KERNEL_STACK_KB,
+ account * (THREAD_SIZE / 1024));
}
}

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6c83cf4ed970..a0aa1c213231 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -775,6 +775,17 @@ void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val)
rcu_read_unlock();
}

+void mod_memcg_obj_state(void *p, int idx, int val)
+{
+ struct mem_cgroup *memcg;
+
+ rcu_read_lock();
+ memcg = mem_cgroup_from_obj(p);
+ if (memcg)
+ mod_memcg_state(memcg, idx, val);
+ rcu_read_unlock();
+}
+
/**
* __count_memcg_events - account VM events in a cgroup
* @memcg: the memory cgroup
@@ -2636,6 +2647,33 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg,
}

#ifdef CONFIG_MEMCG_KMEM
+/*
+ * Returns a pointer to the memory cgroup to which the kernel object is charged.
+ *
+ * The caller must ensure the memcg lifetime, e.g. by taking rcu_read_lock(),
+ * cgroup_mutex, etc.
+ */
+struct mem_cgroup *mem_cgroup_from_obj(void *p)
+{
+ struct page *page;
+
+ if (mem_cgroup_disabled())
+ return NULL;
+
+ page = virt_to_head_page(p);
+
+ /*
+ * Slab pages don't have page->mem_cgroup set because corresponding
+ * kmem caches can be reparented during the lifetime. That's why
+ * memcg_from_slab_page() should be used instead.
+ */
+ if (PageSlab(page))
+ return memcg_from_slab_page(page);
+
+ /* All other pages use page->mem_cgroup */
+ return page->mem_cgroup;
+}
+
static int memcg_alloc_cache_id(void)
{
int id, size;
--
2.25.1
\
 
 \ /
  Last update: 2020-03-24 01:43    [W:0.068 / U:0.952 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site