[rh7,v3] mm/memcg: save css_get/put stacks

Submitted by Konstantin Khorenko on Feb. 11, 2020, 3:17 p.m.

Details

Message ID 20200211151706.26085-1-khorenko@virtuozzo.com
State New
Series "mm/memcg: save css_get/put stacks"
Headers show

Commit Message

Konstantin Khorenko Feb. 11, 2020, 3:17 p.m.
From: Andrey Ryabinin <aryabinin@virtuozzo.com>

Save stack traces on css_get()/css_put() and spit them into
dmesg when memcg_css_release_check_kmem() discovers a problem.
This supposed to help us to find css_get/css_put imbalance.

This patch for debug purposes only.

khorenko@: notice that css_tryget() saves the stack unconditionally,
so there can be stacks for failed tryget-s.

https://jira.sw.ru/browse/PSBM-98148
Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>

v3: add a new "stacks" field in css instead of reusing "flags"
---
 Makefile               |  2 +-
 include/linux/cgroup.h | 19 +++++++++++++++++--
 kernel/cgroup.c        | 38 ++++++++++++++++++++++++++++++++++++++
 mm/memcontrol.c        | 21 +++++++++++++++++++--
 4 files changed, 75 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/Makefile b/Makefile
index 848a20a8da8a3..065f6098b4a08 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@  RHEL_DRM_VERSION = 5
 RHEL_DRM_PATCHLEVEL = 0
 RHEL_DRM_SUBLEVEL = 10
 # VZVERSION = ovz.131.2
-VZVERSION = ovz.custom
+VZVERSION = ovz.finist
 
 ifeq ($(VZVERSION), ovz.custom)
   GIT_DIR := .git
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index aa91e476b912e..a2c5f07170725 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -117,6 +117,11 @@  extern void cgroup_unload_subsys(struct cgroup_subsys *ss);
 
 extern int proc_cgroup_show(struct seq_file *, void *);
 
+struct css_stacks {
+	atomic_t offset;
+	unsigned long stacks[(PAGE_SIZE*2)/8 - 1];
+};
+
 /* Per-subsystem/per-cgroup state maintained by the system. */
 struct cgroup_subsys_state {
 	/*
@@ -130,6 +135,8 @@  struct cgroup_subsys_state {
 	struct percpu_ref refcnt;
 
 	unsigned long flags;
+	/* saving stacks for css get/put - debug */
+	struct css_stacks *stacks;
 	/* ID for this css, if possible */
 	struct css_id __rcu *id;
 
@@ -149,12 +156,16 @@  enum {
  * - an existing ref-counted reference to the css
  * - task->cgroups for a locked task
  */
+void save_css_stack(struct cgroup_subsys_state *css);
 
 static inline void css_get(struct cgroup_subsys_state *css)
 {
 	/* We don't need to reference count the root state */
-	if (!(css->flags & CSS_ROOT))
+	if (!(css->flags & CSS_ROOT)) {
+		save_css_stack(css);
 		percpu_ref_get(&css->refcnt);
+	}
+
 }
 
 /*
@@ -167,6 +178,7 @@  static inline bool css_tryget(struct cgroup_subsys_state *css)
 {
 	if (css->flags & CSS_ROOT)
 		return true;
+	save_css_stack(css);
 	return percpu_ref_tryget_live(&css->refcnt);
 }
 
@@ -177,8 +189,11 @@  static inline bool css_tryget(struct cgroup_subsys_state *css)
 
 static inline void css_put(struct cgroup_subsys_state *css)
 {
-	if (!(css->flags & CSS_ROOT))
+	if (!(css->flags & CSS_ROOT)) {
+		save_css_stack(css);
 		percpu_ref_put(&css->refcnt);
+
+	}
 }
 
 /* bits in struct cgroup flags field */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 9ca8af9f73d91..f46ef8de43ee6 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -60,6 +60,7 @@ 
 #include <linux/flex_array.h> /* used in cgroup_attach_task */
 #include <linux/kthread.h>
 #include <linux/ve.h>
+#include <linux/stacktrace.h>
 
 #include <linux/atomic.h>
 
@@ -4143,6 +4144,11 @@  static void css_dput_fn(struct work_struct *work)
 {
 	struct cgroup_subsys_state *css =
 		container_of(work, struct cgroup_subsys_state, dput_work);
+	struct css_stacks *css_stacks;
+
+	css_stacks = css->stacks;
+	if (css_stacks)
+		free_pages((unsigned long)css_stacks, 1);
 
 	percpu_ref_exit(&css->refcnt);
 	cgroup_dput(css->cgroup);
@@ -4165,6 +4171,11 @@  static void init_cgroup_css(struct cgroup_subsys_state *css,
 {
 	css->cgroup = cgrp;
 	css->flags = 0;
+	if (slab_is_available())
+		css->stacks = (struct css_stacks *)
+			__get_free_pages(GFP_KERNEL|__GFP_NOFAIL|__GFP_ZERO, 1);
+	else
+		css->stacks = 0;
 	if (cgrp == dummytop)
 		css->flags |= CSS_ROOT;
 	BUG_ON(cgrp->subsys[ss->subsys_id]);
@@ -4193,6 +4204,33 @@  static int online_css(struct cgroup_subsys *ss, struct cgroup *cgrp)
 	return ret;
 }
 
+void save_css_stack(struct cgroup_subsys_state *css)
+{
+	unsigned long entries[8];
+	unsigned int offset;
+	struct css_stacks *css_stacks;
+	struct stack_trace trace = {
+		.nr_entries = 0,
+		.entries = entries,
+		.max_entries = 8,
+		.skip = 0
+	};
+
+	css_stacks = css->stacks;
+	if (!css_stacks)
+		return;
+
+	memset(entries, 0, sizeof(entries));
+	offset = atomic_add_return(8*8, &css_stacks->offset) % (PAGE_SIZE*2);
+	if (offset == 0) {
+		offset += 8;
+		trace.max_entries = 7;
+	}
+	save_stack_trace(&trace);
+	memcpy(((char*)css_stacks)+offset, entries, trace.max_entries*8);
+}
+EXPORT_SYMBOL(save_css_stack);
+
 /* if the CSS is online, invoke ->pre_destory() on it and mark it offline */
 static void offline_css(struct cgroup_subsys *ss, struct cgroup *cgrp)
 	__releases(&cgroup_mutex) __acquires(&cgroup_mutex)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 0514f9b2b2308..c8d3c151ff99b 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -621,6 +621,7 @@  void sock_release_memcg(struct sock *sk)
 		struct mem_cgroup *memcg;
 		WARN_ON(!sk->sk_cgrp->memcg);
 		memcg = sk->sk_cgrp->memcg;
+		save_css_stack(&sk->sk_cgrp->memcg->css);
 		css_put(&sk->sk_cgrp->memcg->css);
 	}
 }
@@ -2990,9 +2991,25 @@  void memcg_css_release_check_kmem(struct cgroup_subsys_state *css)
 
 	memcg = mem_cgroup_from_cont(css->cgroup);
 	kmem_counter = page_counter_read(&memcg->kmem);
-	WARN_ONCE(kmem_counter,
+	if(WARN_ONCE(kmem_counter,
 		  "Last put on memcg %p kmem=%lu css->flags=%#lx",
-		  memcg, kmem_counter, css->flags);
+		  memcg, kmem_counter, css->flags)) {
+		struct css_stacks *css_stacks;
+		int i;
+
+		css_stacks = css->stacks;
+		if (css_stacks) {
+			pr_err("css_relese: css_stacks offset %d\n", atomic_read(&css_stacks->offset));
+			for (i = 0; i < PAGE_SIZE*2/8 - 1; i++) {
+				if (css_stacks->stacks[i])
+					pr_err("\t%pS\n", (void *)css_stacks->stacks[i]);
+				else
+					continue;
+			}
+		}
+		BUG();
+	}
+
 }
 EXPORT_SYMBOL(memcg_css_release_check_kmem);