memcg: aggregate memcg cache values in slabinfo
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>
This commit is contained in:
parent
2293315293
commit
749c54151a
@ -420,6 +420,11 @@ static inline void sock_release_memcg(struct sock *sk)
|
|||||||
|
|
||||||
#ifdef CONFIG_MEMCG_KMEM
|
#ifdef CONFIG_MEMCG_KMEM
|
||||||
extern struct static_key memcg_kmem_enabled_key;
|
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)
|
static inline bool memcg_kmem_enabled(void)
|
||||||
{
|
{
|
||||||
return static_key_false(&memcg_kmem_enabled_key);
|
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);
|
return __memcg_kmem_get_cache(cachep, gfp);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
#define for_each_memcg_cache_index(_idx) \
|
||||||
|
for (; NULL; )
|
||||||
|
|
||||||
static inline bool memcg_kmem_enabled(void)
|
static inline bool memcg_kmem_enabled(void)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -220,6 +220,10 @@ struct memcg_cache_params {
|
|||||||
|
|
||||||
int memcg_update_all_caches(int num_memcgs);
|
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
|
* Common kmalloc functions provided by all allocators
|
||||||
*/
|
*/
|
||||||
|
@ -572,7 +572,8 @@ static void disarm_sock_keys(struct mem_cgroup *memcg)
|
|||||||
* increase it.
|
* increase it.
|
||||||
*/
|
*/
|
||||||
static DEFINE_IDA(kmem_limited_groups);
|
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
|
* 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
|
* 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)];
|
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)
|
static int memcg_charge_kmem(struct mem_cgroup *memcg, gfp_t gfp, u64 size)
|
||||||
{
|
{
|
||||||
struct res_counter *fail_res;
|
struct res_counter *fail_res;
|
||||||
@ -5822,6 +5844,12 @@ static struct cftype mem_cgroup_files[] = {
|
|||||||
.trigger = mem_cgroup_reset,
|
.trigger = mem_cgroup_reset,
|
||||||
.read = mem_cgroup_read,
|
.read = mem_cgroup_read,
|
||||||
},
|
},
|
||||||
|
#ifdef CONFIG_SLABINFO
|
||||||
|
{
|
||||||
|
.name = "kmem.slabinfo",
|
||||||
|
.read_seq_string = mem_cgroup_slabinfo_read,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
{ }, /* terminate */
|
{ }, /* terminate */
|
||||||
};
|
};
|
||||||
|
27
mm/slab.h
27
mm/slab.h
@ -138,6 +138,23 @@ static inline bool slab_equal_or_root(struct kmem_cache *s,
|
|||||||
return (p == s) ||
|
return (p == s) ||
|
||||||
(s->memcg_params && (p == s->memcg_params->root_cache));
|
(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
|
#else
|
||||||
static inline bool is_root_cache(struct kmem_cache *s)
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
|
static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
|
||||||
|
@ -322,7 +322,7 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
|
|||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_SLABINFO
|
#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
|
* 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);
|
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;
|
struct slabinfo sinfo;
|
||||||
|
|
||||||
memset(&sinfo, 0, sizeof(sinfo));
|
memset(&sinfo, 0, sizeof(sinfo));
|
||||||
get_slabinfo(s, &sinfo);
|
get_slabinfo(s, &sinfo);
|
||||||
|
|
||||||
|
memcg_accumulate_slabinfo(s, &sinfo);
|
||||||
|
|
||||||
seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d",
|
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));
|
sinfo.objects_per_slab, (1 << sinfo.cache_order));
|
||||||
|
|
||||||
seq_printf(m, " : tunables %4u %4u %4u",
|
seq_printf(m, " : tunables %4u %4u %4u",
|
||||||
@ -387,6 +414,15 @@ static int s_show(struct seq_file *m, void *p)
|
|||||||
return 0;
|
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
|
* slabinfo_op - iterator that generates /proc/slabinfo
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user