aboutsummaryrefslogtreecommitdiff
path: root/mm/slab.h
diff options
context:
space:
mode:
authorRoman Gushchin2020-08-06 23:21:27 -0700
committerLinus Torvalds2020-08-07 11:33:25 -0700
commit10befea91b61c4e2c2d1df06a2e978d182fcf792 (patch)
treecc5c9a0e4d351194ccde996f5455248f45b5d56c /mm/slab.h
parent15999eef7f25e2ea6a1c33f026166f472c5714e9 (diff)
mm: memcg/slab: use a single set of kmem_caches for all allocations
Instead of having two sets of kmem_caches: one for system-wide and non-accounted allocations and the second one shared by all accounted allocations, we can use just one. The idea is simple: space for obj_cgroup metadata can be allocated on demand and filled only for accounted allocations. It allows to remove a bunch of code which is required to handle kmem_cache clones for accounted allocations. There is no more need to create them, accumulate statistics, propagate attributes, etc. It's a quite significant simplification. Also, because the total number of slab_caches is reduced almost twice (not all kmem_caches have a memcg clone), some additional memory savings are expected. On my devvm it additionally saves about 3.5% of slab memory. [guro@fb.com: fix build on MIPS] Link: http://lkml.kernel.org/r/20200717214810.3733082-1-guro@fb.com Suggested-by: Johannes Weiner <hannes@cmpxchg.org> Signed-off-by: Roman Gushchin <guro@fb.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Vlastimil Babka <vbabka@suse.cz> Reviewed-by: Shakeel Butt <shakeelb@google.com> Cc: Christoph Lameter <cl@linux.com> Cc: Michal Hocko <mhocko@kernel.org> Cc: Tejun Heo <tj@kernel.org> Cc: Naresh Kamboju <naresh.kamboju@linaro.org> Link: http://lkml.kernel.org/r/20200623174037.3951353-18-guro@fb.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/slab.h')
-rw-r--r--mm/slab.h194
1 files changed, 42 insertions, 152 deletions
diff --git a/mm/slab.h b/mm/slab.h
index 7500a707121b..ec8e22ee6544 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -30,28 +30,6 @@ struct kmem_cache {
struct list_head list; /* List of all slab caches on the system */
};
-#else /* !CONFIG_SLOB */
-
-/*
- * This is the main placeholder for memcg-related information in kmem caches.
- * Both the root cache and the child cache will have it. Some fields are used
- * in both cases, other are specific to root caches.
- *
- * @root_cache: Common to root and child caches. NULL for root, pointer to
- * the root cache for children.
- *
- * The following fields are specific to root caches.
- *
- * @memcg_cache: pointer to memcg kmem cache, used by all non-root memory
- * cgroups.
- * @work: work struct used to create the non-root cache.
- */
-struct memcg_cache_params {
- struct kmem_cache *root_cache;
-
- struct kmem_cache *memcg_cache;
- struct work_struct work;
-};
#endif /* CONFIG_SLOB */
#ifdef CONFIG_SLAB
@@ -196,7 +174,6 @@ int __kmem_cache_shutdown(struct kmem_cache *);
void __kmem_cache_release(struct kmem_cache *);
int __kmem_cache_shrink(struct kmem_cache *);
void slab_kmem_cache_release(struct kmem_cache *);
-void kmem_cache_shrink_all(struct kmem_cache *s);
struct seq_file;
struct file;
@@ -263,43 +240,6 @@ static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t fla
}
#ifdef CONFIG_MEMCG_KMEM
-static inline bool is_root_cache(struct kmem_cache *s)
-{
- return !s->memcg_params.root_cache;
-}
-
-static inline bool slab_equal_or_root(struct kmem_cache *s,
- struct kmem_cache *p)
-{
- return p == s || 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))
- s = s->memcg_params.root_cache;
- return s->name;
-}
-
-static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s)
-{
- if (is_root_cache(s))
- return s;
- return s->memcg_params.root_cache;
-}
-
-static inline struct kmem_cache *memcg_cache(struct kmem_cache *s)
-{
- if (is_root_cache(s))
- return s->memcg_params.memcg_cache;
- return NULL;
-}
-
static inline struct obj_cgroup **page_obj_cgroups(struct page *page)
{
/*
@@ -317,21 +257,8 @@ static inline bool page_has_obj_cgroups(struct page *page)
return ((unsigned long)page->obj_cgroups & 0x1UL);
}
-static inline int memcg_alloc_page_obj_cgroups(struct page *page,
- struct kmem_cache *s, gfp_t gfp)
-{
- unsigned int objects = objs_per_slab_page(s, page);
- void *vec;
-
- vec = kcalloc_node(objects, sizeof(struct obj_cgroup *), gfp,
- page_to_nid(page));
- if (!vec)
- return -ENOMEM;
-
- kmemleak_not_leak(vec);
- page->obj_cgroups = (struct obj_cgroup **) ((unsigned long)vec | 0x1UL);
- return 0;
-}
+int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s,
+ gfp_t gfp);
static inline void memcg_free_page_obj_cgroups(struct page *page)
{
@@ -348,38 +275,25 @@ static inline size_t obj_full_size(struct kmem_cache *s)
return s->size + sizeof(struct obj_cgroup *);
}
-static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s,
- struct obj_cgroup **objcgp,
- size_t objects, gfp_t flags)
+static inline struct obj_cgroup *memcg_slab_pre_alloc_hook(struct kmem_cache *s,
+ size_t objects,
+ gfp_t flags)
{
- struct kmem_cache *cachep;
struct obj_cgroup *objcg;
if (memcg_kmem_bypass())
- return s;
-
- cachep = READ_ONCE(s->memcg_params.memcg_cache);
- if (unlikely(!cachep)) {
- /*
- * If memcg cache does not exist yet, we schedule it's
- * asynchronous creation and let the current allocation
- * go through with the root cache.
- */
- queue_work(system_wq, &s->memcg_params.work);
- return s;
- }
+ return NULL;
objcg = get_obj_cgroup_from_current();
if (!objcg)
- return s;
+ return NULL;
if (obj_cgroup_charge(objcg, flags, objects * obj_full_size(s))) {
obj_cgroup_put(objcg);
- cachep = NULL;
+ return NULL;
}
- *objcgp = objcg;
- return cachep;
+ return objcg;
}
static inline void mod_objcg_state(struct obj_cgroup *objcg,
@@ -398,15 +312,27 @@ static inline void mod_objcg_state(struct obj_cgroup *objcg,
static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s,
struct obj_cgroup *objcg,
- size_t size, void **p)
+ gfp_t flags, size_t size,
+ void **p)
{
struct page *page;
unsigned long off;
size_t i;
+ if (!objcg)
+ return;
+
+ flags &= ~__GFP_ACCOUNT;
for (i = 0; i < size; i++) {
if (likely(p[i])) {
page = virt_to_head_page(p[i]);
+
+ if (!page_has_obj_cgroups(page) &&
+ memcg_alloc_page_obj_cgroups(page, s, flags)) {
+ obj_cgroup_uncharge(objcg, obj_full_size(s));
+ continue;
+ }
+
off = obj_to_index(s, page, p[i]);
obj_cgroup_get(objcg);
page_obj_cgroups(page)[off] = objcg;
@@ -425,13 +351,19 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page,
struct obj_cgroup *objcg;
unsigned int off;
- if (!memcg_kmem_enabled() || is_root_cache(s))
+ if (!memcg_kmem_enabled())
+ return;
+
+ if (!page_has_obj_cgroups(page))
return;
off = obj_to_index(s, page, p);
objcg = page_obj_cgroups(page)[off];
page_obj_cgroups(page)[off] = NULL;
+ if (!objcg)
+ return;
+
obj_cgroup_uncharge(objcg, obj_full_size(s));
mod_objcg_state(objcg, page_pgdat(page), cache_vmstat_idx(s),
-obj_full_size(s));
@@ -439,35 +371,7 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page,
obj_cgroup_put(objcg);
}
-extern void slab_init_memcg_params(struct kmem_cache *);
-
#else /* CONFIG_MEMCG_KMEM */
-static inline bool is_root_cache(struct kmem_cache *s)
-{
- return true;
-}
-
-static inline bool slab_equal_or_root(struct kmem_cache *s,
- struct kmem_cache *p)
-{
- return s == p;
-}
-
-static inline const char *cache_name(struct kmem_cache *s)
-{
- return s->name;
-}
-
-static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s)
-{
- return s;
-}
-
-static inline struct kmem_cache *memcg_cache(struct kmem_cache *s)
-{
- return NULL;
-}
-
static inline bool page_has_obj_cgroups(struct page *page)
{
return false;
@@ -488,16 +392,17 @@ static inline void memcg_free_page_obj_cgroups(struct page *page)
{
}
-static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s,
- struct obj_cgroup **objcgp,
- size_t objects, gfp_t flags)
+static inline struct obj_cgroup *memcg_slab_pre_alloc_hook(struct kmem_cache *s,
+ size_t objects,
+ gfp_t flags)
{
return NULL;
}
static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s,
struct obj_cgroup *objcg,
- size_t size, void **p)
+ gfp_t flags, size_t size,
+ void **p)
{
}
@@ -505,11 +410,6 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page,
void *p)
{
}
-
-static inline void slab_init_memcg_params(struct kmem_cache *s)
-{
-}
-
#endif /* CONFIG_MEMCG_KMEM */
static inline struct kmem_cache *virt_to_cache(const void *obj)
@@ -523,27 +423,18 @@ static inline struct kmem_cache *virt_to_cache(const void *obj)
return page->slab_cache;
}
-static __always_inline int charge_slab_page(struct page *page,
- gfp_t gfp, int order,
- struct kmem_cache *s)
+static __always_inline void charge_slab_page(struct page *page,
+ gfp_t gfp, int order,
+ struct kmem_cache *s)
{
- if (memcg_kmem_enabled() && !is_root_cache(s)) {
- int ret;
-
- ret = memcg_alloc_page_obj_cgroups(page, s, gfp);
- if (ret)
- return ret;
- }
-
mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s),
PAGE_SIZE << order);
- return 0;
}
static __always_inline void uncharge_slab_page(struct page *page, int order,
struct kmem_cache *s)
{
- if (memcg_kmem_enabled() && !is_root_cache(s))
+ if (memcg_kmem_enabled())
memcg_free_page_obj_cgroups(page);
mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s),
@@ -555,12 +446,11 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x)
struct kmem_cache *cachep;
if (!IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) &&
- !memcg_kmem_enabled() &&
!kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS))
return s;
cachep = virt_to_cache(x);
- if (WARN(cachep && !slab_equal_or_root(cachep, s),
+ if (WARN(cachep && cachep != s,
"%s: Wrong slab cache. %s but object is from %s\n",
__func__, s->name, cachep->name))
print_tracking(cachep, x);
@@ -613,7 +503,7 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s,
if (memcg_kmem_enabled() &&
((flags & __GFP_ACCOUNT) || (s->flags & SLAB_ACCOUNT)))
- return memcg_slab_pre_alloc_hook(s, objcgp, size, flags);
+ *objcgp = memcg_slab_pre_alloc_hook(s, size, flags);
return s;
}
@@ -632,8 +522,8 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s,
s->flags, flags);
}
- if (memcg_kmem_enabled() && !is_root_cache(s))
- memcg_slab_post_alloc_hook(s, objcg, size, p);
+ if (memcg_kmem_enabled())
+ memcg_slab_post_alloc_hook(s, objcg, flags, size, p);
}
#ifndef CONFIG_SLOB