memcg: aggregate memcg cache values in slabinfo
authorGlauber Costa <glommer@parallels.com>
Tue, 18 Dec 2012 22:23:01 +0000 (14:23 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Dec 2012 23:02:14 +0000 (15:02 -0800)
When we create caches in memcgs, we need to display their usage
information somewhere.  We'll adopt a scheme similar to /proc/meminfo,
with aggregate totals shown in the global file, and per-group information
stored in the group itself.

For the time being, only reads are allowed in the per-group cache.

Signed-off-by: Glauber Costa <glommer@parallels.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Frederic Weisbecker <fweisbec@redhat.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: JoonSoo Kim <js1304@gmail.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Cc: Rik van Riel <riel@redhat.com>
Cc: Suleiman Souhlal <suleiman@google.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/memcontrol.h
include/linux/slab.h
mm/memcontrol.c
mm/slab.h
mm/slab_common.c

index e119f3ef793c8da22abbe10f9da2d95cd368b897..8dc7c746b44fb77173b2f27425e675bb09991dcf 100644 (file)
@@ -420,6 +420,11 @@ static inline void sock_release_memcg(struct sock *sk)
 
 #ifdef CONFIG_MEMCG_KMEM
 extern struct static_key memcg_kmem_enabled_key;
+
+extern int memcg_limited_groups_array_size;
+#define for_each_memcg_cache_index(_idx)       \
+       for ((_idx) = 0; i < memcg_limited_groups_array_size; (_idx)++)
+
 static inline bool memcg_kmem_enabled(void)
 {
        return static_key_false(&memcg_kmem_enabled_key);
@@ -557,6 +562,9 @@ memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)
        return __memcg_kmem_get_cache(cachep, gfp);
 }
 #else
+#define for_each_memcg_cache_index(_idx)       \
+       for (; NULL; )
+
 static inline bool memcg_kmem_enabled(void)
 {
        return false;
index 869efb8d2377bfbed557a90b3da246d248055847..b9278663f22a0842219b82d98d7db047392f1a59 100644 (file)
@@ -220,6 +220,10 @@ struct memcg_cache_params {
 
 int memcg_update_all_caches(int num_memcgs);
 
+struct seq_file;
+int cache_show(struct kmem_cache *s, struct seq_file *m);
+void print_slabinfo_header(struct seq_file *m);
+
 /*
  * Common kmalloc functions provided by all allocators
  */
index 7633e0d429e05bfde1ddfb116931e4d0e87d61a4..a32d83c2e353f64db62519baeb738d8ddb90280a 100644 (file)
@@ -572,7 +572,8 @@ static void disarm_sock_keys(struct mem_cgroup *memcg)
  * increase it.
  */
 static DEFINE_IDA(kmem_limited_groups);
-static int memcg_limited_groups_array_size;
+int memcg_limited_groups_array_size;
+
 /*
  * MIN_SIZE is different than 1, because we would like to avoid going through
  * the alloc/free process all the time. In a small machine, 4 kmem-limited
@@ -2794,6 +2795,27 @@ static struct kmem_cache *memcg_params_to_cache(struct memcg_cache_params *p)
        return cachep->memcg_params->memcg_caches[memcg_cache_id(p->memcg)];
 }
 
+#ifdef CONFIG_SLABINFO
+static int mem_cgroup_slabinfo_read(struct cgroup *cont, struct cftype *cft,
+                                       struct seq_file *m)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
+       struct memcg_cache_params *params;
+
+       if (!memcg_can_account_kmem(memcg))
+               return -EIO;
+
+       print_slabinfo_header(m);
+
+       mutex_lock(&memcg->slab_caches_mutex);
+       list_for_each_entry(params, &memcg->memcg_slab_caches, list)
+               cache_show(memcg_params_to_cache(params), m);
+       mutex_unlock(&memcg->slab_caches_mutex);
+
+       return 0;
+}
+#endif
+
 static int memcg_charge_kmem(struct mem_cgroup *memcg, gfp_t gfp, u64 size)
 {
        struct res_counter *fail_res;
@@ -5822,6 +5844,12 @@ static struct cftype mem_cgroup_files[] = {
                .trigger = mem_cgroup_reset,
                .read = mem_cgroup_read,
        },
+#ifdef CONFIG_SLABINFO
+       {
+               .name = "kmem.slabinfo",
+               .read_seq_string = mem_cgroup_slabinfo_read,
+       },
+#endif
 #endif
        { },    /* terminate */
 };
index 43d8a38b534f18fc09c3c4973974ca4f21b00ec6..ec5dae1c8e75100654b93eb03f2d627ed6a26194 100644 (file)
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -138,6 +138,23 @@ static inline bool slab_equal_or_root(struct kmem_cache *s,
        return (p == s) ||
                (s->memcg_params && (p == s->memcg_params->root_cache));
 }
+
+/*
+ * We use suffixes to the name in memcg because we can't have caches
+ * created in the system with the same name. But when we print them
+ * locally, better refer to them with the base name
+ */
+static inline const char *cache_name(struct kmem_cache *s)
+{
+       if (!is_root_cache(s))
+               return s->memcg_params->root_cache->name;
+       return s->name;
+}
+
+static inline struct kmem_cache *cache_from_memcg(struct kmem_cache *s, int idx)
+{
+       return s->memcg_params->memcg_caches[idx];
+}
 #else
 static inline bool is_root_cache(struct kmem_cache *s)
 {
@@ -163,6 +180,16 @@ static inline bool slab_equal_or_root(struct kmem_cache *s,
 {
        return true;
 }
+
+static inline const char *cache_name(struct kmem_cache *s)
+{
+       return s->name;
+}
+
+static inline struct kmem_cache *cache_from_memcg(struct kmem_cache *s, int idx)
+{
+       return NULL;
+}
 #endif
 
 static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
index 080a43804bf1aa11fd50e9ee249416ed0b9c1f11..081f1b8d9a7b21ae5fb4824fd3e08200fc7f0071 100644 (file)
@@ -322,7 +322,7 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
 
 
 #ifdef CONFIG_SLABINFO
-static void print_slabinfo_header(struct seq_file *m)
+void print_slabinfo_header(struct seq_file *m)
 {
        /*
         * Output format version, so at least we can change it
@@ -366,16 +366,43 @@ static void s_stop(struct seq_file *m, void *p)
        mutex_unlock(&slab_mutex);
 }
 
-static int s_show(struct seq_file *m, void *p)
+static void
+memcg_accumulate_slabinfo(struct kmem_cache *s, struct slabinfo *info)
+{
+       struct kmem_cache *c;
+       struct slabinfo sinfo;
+       int i;
+
+       if (!is_root_cache(s))
+               return;
+
+       for_each_memcg_cache_index(i) {
+               c = cache_from_memcg(s, i);
+               if (!c)
+                       continue;
+
+               memset(&sinfo, 0, sizeof(sinfo));
+               get_slabinfo(c, &sinfo);
+
+               info->active_slabs += sinfo.active_slabs;
+               info->num_slabs += sinfo.num_slabs;
+               info->shared_avail += sinfo.shared_avail;
+               info->active_objs += sinfo.active_objs;
+               info->num_objs += sinfo.num_objs;
+       }
+}
+
+int cache_show(struct kmem_cache *s, struct seq_file *m)
 {
-       struct kmem_cache *s = list_entry(p, struct kmem_cache, list);
        struct slabinfo sinfo;
 
        memset(&sinfo, 0, sizeof(sinfo));
        get_slabinfo(s, &sinfo);
 
+       memcg_accumulate_slabinfo(s, &sinfo);
+
        seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d",
-                  s->name, sinfo.active_objs, sinfo.num_objs, s->size,
+                  cache_name(s), sinfo.active_objs, sinfo.num_objs, s->size,
                   sinfo.objects_per_slab, (1 << sinfo.cache_order));
 
        seq_printf(m, " : tunables %4u %4u %4u",
@@ -387,6 +414,15 @@ static int s_show(struct seq_file *m, void *p)
        return 0;
 }
 
+static int s_show(struct seq_file *m, void *p)
+{
+       struct kmem_cache *s = list_entry(p, struct kmem_cache, list);
+
+       if (!is_root_cache(s))
+               return 0;
+       return cache_show(s, m);
+}
+
 /*
  * slabinfo_op - iterator that generates /proc/slabinfo
  *