Skip to content

Commit b852990

Browse files
Vladimir Davydovtorvalds
authored andcommitted
memcg, slab: do not destroy children caches if parent has aliases
Currently we destroy children caches at the very beginning of kmem_cache_destroy(). This is wrong, because the root cache will not necessarily be destroyed in the end - if it has aliases (refcount > 0), kmem_cache_destroy() will simply decrement its refcount and return. In this case, at best we will get a bunch of warnings in dmesg, like this one: kmem_cache_destroy kmalloc-32:0: Slab cache still has objects CPU: 1 PID: 7139 Comm: modprobe Tainted: G B W 3.13.0+ #117 Call Trace: dump_stack+0x49/0x5b kmem_cache_destroy+0xdf/0xf0 kmem_cache_destroy_memcg_children+0x97/0xc0 kmem_cache_destroy+0xf/0xf0 xfs_mru_cache_uninit+0x21/0x30 [xfs] exit_xfs_fs+0x2e/0xc44 [xfs] SyS_delete_module+0x198/0x1f0 system_call_fastpath+0x16/0x1b At worst - if kmem_cache_destroy() will race with an allocation from a memcg cache - the kernel will panic. This patch fixes this by moving children caches destruction after the check if the cache has aliases. Plus, it forbids destroying a root cache if it still has children caches, because each children cache keeps a reference to its parent. Signed-off-by: Vladimir Davydov <vdavydov@parallels.com> Cc: Michal Hocko <mhocko@suse.cz> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: David Rientjes <rientjes@google.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: Glauber Costa <glommer@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 051dd46 commit b852990

File tree

3 files changed

+57
-37
lines changed

3 files changed

+57
-37
lines changed

include/linux/memcontrol.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ struct kmem_cache *
507507
__memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp);
508508

509509
void mem_cgroup_destroy_cache(struct kmem_cache *cachep);
510-
void kmem_cache_destroy_memcg_children(struct kmem_cache *s);
510+
int __kmem_cache_destroy_memcg_children(struct kmem_cache *s);
511511

512512
/**
513513
* memcg_kmem_newpage_charge: verify if a new kmem allocation is allowed.
@@ -661,10 +661,6 @@ memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)
661661
{
662662
return cachep;
663663
}
664-
665-
static inline void kmem_cache_destroy_memcg_children(struct kmem_cache *s)
666-
{
667-
}
668664
#endif /* CONFIG_MEMCG_KMEM */
669665
#endif /* _LINUX_MEMCONTROL_H */
670666

mm/memcontrol.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3321,15 +3321,10 @@ void mem_cgroup_destroy_cache(struct kmem_cache *cachep)
33213321
schedule_work(&cachep->memcg_params->destroy);
33223322
}
33233323

3324-
void kmem_cache_destroy_memcg_children(struct kmem_cache *s)
3324+
int __kmem_cache_destroy_memcg_children(struct kmem_cache *s)
33253325
{
33263326
struct kmem_cache *c;
3327-
int i;
3328-
3329-
if (!s->memcg_params)
3330-
return;
3331-
if (!s->memcg_params->is_root_cache)
3332-
return;
3327+
int i, failed = 0;
33333328

33343329
/*
33353330
* If the cache is being destroyed, we trust that there is no one else
@@ -3363,8 +3358,12 @@ void kmem_cache_destroy_memcg_children(struct kmem_cache *s)
33633358
c->memcg_params->dead = false;
33643359
cancel_work_sync(&c->memcg_params->destroy);
33653360
kmem_cache_destroy(c);
3361+
3362+
if (cache_from_memcg_idx(s, i))
3363+
failed++;
33663364
}
33673365
mutex_unlock(&activate_kmem_mutex);
3366+
return failed;
33683367
}
33693368

33703369
static void mem_cgroup_destroy_all_caches(struct mem_cgroup *memcg)

mm/slab_common.c

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -301,39 +301,64 @@ void kmem_cache_create_memcg(struct mem_cgroup *memcg, struct kmem_cache *root_c
301301
mutex_unlock(&slab_mutex);
302302
put_online_cpus();
303303
}
304+
305+
static int kmem_cache_destroy_memcg_children(struct kmem_cache *s)
306+
{
307+
int rc;
308+
309+
if (!s->memcg_params ||
310+
!s->memcg_params->is_root_cache)
311+
return 0;
312+
313+
mutex_unlock(&slab_mutex);
314+
rc = __kmem_cache_destroy_memcg_children(s);
315+
mutex_lock(&slab_mutex);
316+
317+
return rc;
318+
}
319+
#else
320+
static int kmem_cache_destroy_memcg_children(struct kmem_cache *s)
321+
{
322+
return 0;
323+
}
304324
#endif /* CONFIG_MEMCG_KMEM */
305325

306326
void kmem_cache_destroy(struct kmem_cache *s)
307327
{
308-
/* Destroy all the children caches if we aren't a memcg cache */
309-
kmem_cache_destroy_memcg_children(s);
310-
311328
get_online_cpus();
312329
mutex_lock(&slab_mutex);
330+
313331
s->refcount--;
314-
if (!s->refcount) {
315-
list_del(&s->list);
316-
memcg_unregister_cache(s);
317-
318-
if (!__kmem_cache_shutdown(s)) {
319-
mutex_unlock(&slab_mutex);
320-
if (s->flags & SLAB_DESTROY_BY_RCU)
321-
rcu_barrier();
322-
323-
memcg_free_cache_params(s);
324-
kfree(s->name);
325-
kmem_cache_free(kmem_cache, s);
326-
} else {
327-
list_add(&s->list, &slab_caches);
328-
memcg_register_cache(s);
329-
mutex_unlock(&slab_mutex);
330-
printk(KERN_ERR "kmem_cache_destroy %s: Slab cache still has objects\n",
331-
s->name);
332-
dump_stack();
333-
}
334-
} else {
335-
mutex_unlock(&slab_mutex);
332+
if (s->refcount)
333+
goto out_unlock;
334+
335+
if (kmem_cache_destroy_memcg_children(s) != 0)
336+
goto out_unlock;
337+
338+
list_del(&s->list);
339+
memcg_unregister_cache(s);
340+
341+
if (__kmem_cache_shutdown(s) != 0) {
342+
list_add(&s->list, &slab_caches);
343+
memcg_register_cache(s);
344+
printk(KERN_ERR "kmem_cache_destroy %s: "
345+
"Slab cache still has objects\n", s->name);
346+
dump_stack();
347+
goto out_unlock;
336348
}
349+
350+
mutex_unlock(&slab_mutex);
351+
if (s->flags & SLAB_DESTROY_BY_RCU)
352+
rcu_barrier();
353+
354+
memcg_free_cache_params(s);
355+
kfree(s->name);
356+
kmem_cache_free(kmem_cache, s);
357+
goto out_put_cpus;
358+
359+
out_unlock:
360+
mutex_unlock(&slab_mutex);
361+
out_put_cpus:
337362
put_online_cpus();
338363
}
339364
EXPORT_SYMBOL(kmem_cache_destroy);

0 commit comments

Comments
 (0)