[RFC] simple system for enable/disable slabs being tracked by memcg.

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

 



Hi.

This is a proposal I've got for how to finally settle down the
question of which slabs should be tracked. The patch I am providing
is for discussion only, and should apply ontop of Suleiman's latest
version posted to the list.

The idea is to create a new file, memory.kmem.slabs_allowed.
I decided not to overload the slabinfo file for that, but I can,
if you ultimately want to. I just think it is cleaner this way.
As a small rationale, I'd like to somehow show which caches are
available but disabled. And yet, keep the format compatible with
/proc/slabinfo.

Reading from this file will provide this information
Writers should write a string:
 [+-]cache_name

The wild card * is accepted, but only that. I am leaving
any complex processing to userspace.

The * wildcard, though, is nice. It allows us to do:
 -* (disable all)
 +cache1
 +cache2

and so on.

Part of this patch is actually converting the slab pointers in memcg
to a complex memcg-specific structure that can hold a disabled pointer.

We could actually store it in a free bit in the address, but that is
a first version. Let me know if this is how you would like me to tackle
this.

With a system like this (either this, or something alike), my opposition
to Suleiman's idea of tracking everything under the sun basically vanishes,
since I can then selectively disable most of them.

I still prefer a special kmalloc call than a GFP flag, though.

Signed-off-by: Glauber Costa <glommer@xxxxxxxxxxxxx>
CC: Suleiman Souhlal <suleiman@xxxxxxxxxx>
CC: Hiroyouki Kamezawa <kamezawa.hiroyu@xxxxxxxxxxxxxx>
CC: Johannes Weiner <hannes@xxxxxxxxxxx>
CC: Michal Hocko <mhocko@xxxxxxx>
---
 include/linux/memcontrol.h |   17 ++++++++
 include/linux/slab.h       |   13 ++++++
 mm/memcontrol.c            |   87 ++++++++++++++++++++++++++++++++++----
 mm/slab.c                  |   99 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 207 insertions(+), 9 deletions(-)

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index f5458b0..acd38a5 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -427,6 +427,9 @@ bool mem_cgroup_charge_slab(struct kmem_cache *cachep, gfp_t gfp, size_t size);
 void mem_cgroup_uncharge_slab(struct kmem_cache *cachep, size_t size);
 void mem_cgroup_flush_cache_create_queue(void);
 void mem_cgroup_remove_child_kmem_cache(struct kmem_cache *cachep, int id);
+int mem_cgroup_slab_allowed(struct mem_cgroup *memcg, int id);
+void mem_cgroup_slab_allow(struct mem_cgroup *memcg, int id);
+void mem_cgroup_slab_disallow(struct mem_cgroup *memcg, int id);
 #else /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
 static inline void sock_update_memcg(struct sock *sk)
 {
@@ -456,6 +459,20 @@ static inline void
 mem_cgroup_flush_cache_create_queue(void)
 {
 }
+
+int mem_cgroup_slab_allowed(struct mem_cgroup *memcg, int id)
+{
+	return 0;
+}
+
+void mem_cgroup_slab_disallow(struct mem_cgroup *memcg, int id)
+{
+}
+
+void mem_cgroup_slab_allow(struct mem_cgroup *memcg, int id)
+{
+}
+
 #endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
 #endif /* _LINUX_MEMCONTROL_H */
 
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 0ff5ee2..3106843 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -380,6 +380,8 @@ void kmem_cache_drop_ref(struct kmem_cache *cachep);
 
 void *kmalloc_no_account(size_t size, gfp_t flags);
 
+int mem_cgroup_tune_slab(struct mem_cgroup *mem, const char *buffer);
+int mem_cgroup_probe_slab(struct mem_cgroup *mem, struct seq_file *m);
 #else /* !CONFIG_CGROUP_MEM_RES_CTLR_KMEM || !CONFIG_SLAB */
 
 #define MAX_KMEM_CACHE_TYPES 0
@@ -407,6 +409,17 @@ mem_cgroup_slabinfo(struct mem_cgroup *mem, struct seq_file *m)
 	return 0;
 }
 
+static inline int mem_cgroup_tune_slab(struct mem_cgroup *mem, const char *buffer)
+{
+	return 0;
+}
+
+static inline int mem_cgroup_probe_slab(struct mem_cgroup *mem, const char *buffer)
+{
+	return 0;
+}
+
+
 #endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM && CONFIG_SLAB */
 
 #endif	/* _LINUX_SLAB_H */
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index ba042d9..e8c6a92 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -226,6 +226,11 @@ enum memcg_flags {
 					 */
 };
 
+struct memcg_slab {
+	struct kmem_cache *cache;
+	bool disabled;
+};
+
 /*
  * The memory controller data structure. The memory controller controls both
  * page cache and RSS per cgroup. We would eventually like to provide
@@ -305,7 +310,7 @@ struct mem_cgroup {
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
 	/* Slab accounting */
-	struct kmem_cache *slabs[MAX_KMEM_CACHE_TYPES];
+	struct memcg_slab slabs[MAX_KMEM_CACHE_TYPES];
 #endif
 };
 
@@ -4671,6 +4676,21 @@ static int mem_cgroup_independent_kmem_limit_write(struct cgroup *cgrp,
 	return 0;
 }
 
+int mem_cgroup_slab_allowed(struct mem_cgroup *memcg, int idx)
+{
+	return !memcg->slabs[idx].disabled;
+}
+
+void mem_cgroup_slab_allow(struct mem_cgroup *memcg, int idx)
+{
+	memcg->slabs[idx].disabled = false;
+}
+
+void mem_cgroup_slab_disallow(struct mem_cgroup *memcg, int idx)
+{
+	memcg->slabs[idx].disabled = true;
+}
+
 static int
 mem_cgroup_slabinfo_show(struct cgroup *cgroup, struct cftype *ctf,
     struct seq_file *m)
@@ -4685,6 +4705,35 @@ mem_cgroup_slabinfo_show(struct cgroup *cgroup, struct cftype *ctf,
 	return mem_cgroup_slabinfo(mem, m);
 }
 
+static int mem_cgroup_slabs_read(struct cgroup *cgroup, struct cftype *ctf,
+				 struct seq_file *m)
+{
+	struct mem_cgroup *mem;
+
+	mem  = mem_cgroup_from_cont(cgroup);
+
+	if (mem == root_mem_cgroup)
+		return -EINVAL;
+
+	if (!list_empty(&cgroup->children))
+		return -EBUSY;
+
+	return mem_cgroup_probe_slab(mem, m);
+}
+
+static int mem_cgroup_slabs_write(struct cgroup *cgroup, struct cftype *cft,
+				  const char *buffer)
+{
+	struct mem_cgroup *mem;
+
+	mem  = mem_cgroup_from_cont(cgroup);
+
+	if (mem == root_mem_cgroup)
+		return -EINVAL;
+
+	return mem_cgroup_tune_slab(mem, buffer);
+}
+
 static struct cftype kmem_cgroup_files[] = {
 	{
 		.name = "kmem.independent_kmem_limit",
@@ -4706,6 +4755,12 @@ static struct cftype kmem_cgroup_files[] = {
 		.name = "kmem.slabinfo",
 		.read_seq_string = mem_cgroup_slabinfo_show,
 	},
+	{
+		.name = "kmem.slabs_allowed",
+		.read_seq_string = mem_cgroup_slabs_read,
+		.write_string = mem_cgroup_slabs_write,
+	},
+
 };
 
 static int register_kmem_files(struct cgroup *cont, struct cgroup_subsys *ss)
@@ -5765,7 +5820,7 @@ memcg_create_kmem_cache(struct mem_cgroup *memcg, struct kmem_cache *cachep)
 	 * This should behave as a write barrier, so we should be fine
 	 * with RCU.
 	 */
-	if (cmpxchg(&memcg->slabs[idx], NULL, new_cachep) != NULL) {
+	if (cmpxchg(&memcg->slabs[idx].cache, NULL, new_cachep) != NULL) {
 		kmem_cache_destroy(new_cachep);
 		return cachep;
 	}
@@ -5838,6 +5893,7 @@ memcg_create_cache_enqueue(struct mem_cgroup *memcg, struct kmem_cache *cachep)
 {
 	struct create_work *cw;
 	unsigned long flags;
+	int idx;
 
 	spin_lock_irqsave(&create_queue_lock, flags);
 	list_for_each_entry(cw, &create_queue, list) {
@@ -5848,6 +5904,14 @@ memcg_create_cache_enqueue(struct mem_cgroup *memcg, struct kmem_cache *cachep)
 	}
 	spin_unlock_irqrestore(&create_queue_lock, flags);
 
+	/*
+	 * If this cache is disabled, it basically means we are doing
+	 * global accounting for that particular cache. So skip it
+	 */
+	idx = cachep->memcg_params.id;
+	if (memcg->slabs[idx].disabled)
+		return;
+
 	/* The corresponding put will be done in the workqueue. */
 	if (!css_tryget(&memcg->css))
 		return;
@@ -5912,18 +5976,18 @@ mem_cgroup_get_kmem_cache(struct kmem_cache *cachep, gfp_t gfp)
 
 	VM_BUG_ON(idx == -1);
 
-	if (rcu_access_pointer(memcg->slabs[idx]) == NULL) {
+	if (rcu_access_pointer(memcg->slabs[idx].cache) == NULL) {
 		memcg_create_cache_enqueue(memcg, cachep);
 		return cachep;
 	}
 
-	return rcu_dereference(memcg->slabs[idx]);
+	return rcu_dereference(memcg->slabs[idx].cache);
 }
 
 void
 mem_cgroup_remove_child_kmem_cache(struct kmem_cache *cachep, int id)
 {
-	rcu_assign_pointer(cachep->memcg_params.memcg->slabs[id], NULL);
+	rcu_assign_pointer(cachep->memcg_params.memcg->slabs[id].cache, NULL);
 }
 
 bool
@@ -5966,10 +6030,15 @@ mem_cgroup_uncharge_slab(struct kmem_cache *cachep, size_t size)
 static void
 memcg_slab_init(struct mem_cgroup *memcg)
 {
+	struct mem_cgroup *parent = parent_mem_cgroup(memcg);
 	int i;
 
-	for (i = 0; i < MAX_KMEM_CACHE_TYPES; i++)
-		rcu_assign_pointer(memcg->slabs[i], NULL);
+	for (i = 0; i < MAX_KMEM_CACHE_TYPES; i++) {
+		rcu_assign_pointer(memcg->slabs[i].cache, NULL);
+		if (parent)
+			memcg->slabs[i].disabled =
+			parent->slabs[i].disabled;
+	}
 }
 
 /*
@@ -5988,9 +6057,9 @@ memcg_slab_move(struct mem_cgroup *memcg)
 	mem_cgroup_flush_cache_create_queue();
 
 	for (i = 0; i < MAX_KMEM_CACHE_TYPES; i++) {
-		cachep = rcu_access_pointer(memcg->slabs[i]);
+		cachep = rcu_access_pointer(memcg->slabs[i].cache);
 		if (cachep != NULL) {
-			rcu_assign_pointer(memcg->slabs[i], NULL);
+			rcu_assign_pointer(memcg->slabs[i].cache, NULL);
 			cachep->memcg_params.memcg = NULL;
 
 			/* The space for this is already allocated */
diff --git a/mm/slab.c b/mm/slab.c
index 1b35799..1bf13f1 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -4766,6 +4766,105 @@ mem_cgroup_slabinfo(struct mem_cgroup *mem, struct seq_file *m)
 
 	return 0;
 }
+
+int mem_cgroup_probe_slab(struct mem_cgroup *memcg, struct seq_file *m)
+{
+	struct kmem_cache *cachep;
+
+	seq_printf(m, "Available slabs:\n");
+
+	mutex_lock(&cache_chain_mutex);
+	list_for_each_entry(cachep, &cache_chain, next) {
+		bool allowed;
+		if (cachep->memcg_params.id == -1)
+			continue;
+
+		if (!(cachep->flags & SLAB_MEMCG_ACCT))
+			continue;
+
+		allowed = !mem_cgroup_slab_allowed(memcg, cachep->memcg_params.id);
+
+		seq_printf(m, "%c %-17s\n", allowed ? '*' : ' ', cachep->name);
+	}
+	mutex_unlock(&cache_chain_mutex);
+
+	return 0;
+}
+
+/*
+ * selects which slabs are tracked in this memcg, from the pool of available
+ * slabs.
+ *
+ * Not worth to implement a full regex parser. Pre-processing can be done in
+ * userspace if needed. It helps, however, to at least have a * wildcard for
+ * groups of cache, like size-*.
+ *
+ * The first character is either a + or a -, meaning either add or remove
+ * a particular cache from the list of tracked caches.
+ */
+int mem_cgroup_tune_slab(struct mem_cgroup *mem, const char *buffer)
+{
+	struct kmem_cache *cachep;
+	int op;
+	int ret = -EINVAL;
+
+	if (!buffer)
+		return ret;
+
+	if (*buffer == '+' )
+		op = 1;
+	else if (*buffer == '-')
+		op = 0;
+	else
+		return ret;
+	
+	buffer++;
+
+	mutex_lock(&cache_chain_mutex);
+	list_for_each_entry(cachep, &cache_chain, next) {
+		const char *cname = cachep->name;
+		const char *ptr = buffer;
+		const char *next = NULL;
+
+		if (cachep->memcg_params.id == -1)
+			continue;
+
+		while (*ptr && *cname) {
+			if (*ptr == '*') {
+				if (!next) {
+					next = ptr;
+					next++;
+				} 
+				if (*next == *cname) {
+					next = NULL;
+					ptr++;	
+					continue;
+				}
+				cname++;
+				if (!*cname)
+					ptr++;
+				continue;
+			} else if (*ptr != *cname)
+				break;
+			ptr++;
+			cname++;
+		}
+		if (*cname || *ptr)
+			continue;
+		ret = 0;
+
+		if (op == 0)
+			mem_cgroup_slab_disallow(mem, cachep->memcg_params.id);
+		else if (op == 1)
+			mem_cgroup_slab_allow(mem, cachep->memcg_params.id);
+		else
+			BUG();
+	}
+
+	mutex_unlock(&cache_chain_mutex);
+	return ret;
+}
+
 #endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
 
 #ifdef CONFIG_DEBUG_SLAB_LEAK
-- 
1.7.4.1

--
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/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
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]