Re: [PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
On 10/29/2012 07:26 PM, JoonSoo Kim wrote: > 2012/10/19 Glauber Costa : >> +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) >> +{ >> + struct kmem_cache *c; >> + int i; >> + >> + if (!s->memcg_params) >> + return; >> + if (!s->memcg_params->is_root_cache) >> + return; >> + >> + /* >> +* If the cache is being destroyed, we trust that there is no one >> else >> +* requesting objects from it. Even if there are, the sanity checks >> in >> +* kmem_cache_destroy should caught this ill-case. >> +* >> +* Still, we don't want anyone else freeing memcg_caches under our >> +* noses, which can happen if a new memcg comes to life. As usual, >> +* we'll take the set_limit_mutex to protect ourselves against this. >> +*/ >> + mutex_lock(_limit_mutex); >> + for (i = 0; i < memcg_limited_groups_array_size; i++) { >> + c = s->memcg_params->memcg_caches[i]; >> + if (c) >> + kmem_cache_destroy(c); >> + } >> + mutex_unlock(_limit_mutex); >> +} > > It may cause NULL deref. > Look at the following scenario. > > 1. some memcg slab caches has remained object. > 2. start to destroy memcg. > 3. schedule_delayed_work(kmem_cache_destroy_work_func, @delay 60hz) > 4. all remained object is freed. > 5. start to destroy root cache. > 6. kmem_cache_destroy makes 's->memcg_params->memcg_caches[i]" NULL!! > 7. Start delayed work function. > 8. cachep in kmem_cache_destroy_work_func() may be NULL > > Thanks. > Thanks for spotting. This is the same problem we have in memcg_cache_destroy(), which I solved by not respawning the worker. In here, I believe it should be possible to just cancel all remaining pending work, since we are now in the process of deleting the caches ourselves. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
On 10/29/2012 07:26 PM, JoonSoo Kim wrote: 2012/10/19 Glauber Costa glom...@parallels.com: +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ + struct kmem_cache *c; + int i; + + if (!s-memcg_params) + return; + if (!s-memcg_params-is_root_cache) + return; + + /* +* If the cache is being destroyed, we trust that there is no one else +* requesting objects from it. Even if there are, the sanity checks in +* kmem_cache_destroy should caught this ill-case. +* +* Still, we don't want anyone else freeing memcg_caches under our +* noses, which can happen if a new memcg comes to life. As usual, +* we'll take the set_limit_mutex to protect ourselves against this. +*/ + mutex_lock(set_limit_mutex); + for (i = 0; i memcg_limited_groups_array_size; i++) { + c = s-memcg_params-memcg_caches[i]; + if (c) + kmem_cache_destroy(c); + } + mutex_unlock(set_limit_mutex); +} It may cause NULL deref. Look at the following scenario. 1. some memcg slab caches has remained object. 2. start to destroy memcg. 3. schedule_delayed_work(kmem_cache_destroy_work_func, @delay 60hz) 4. all remained object is freed. 5. start to destroy root cache. 6. kmem_cache_destroy makes 's-memcg_params-memcg_caches[i] NULL!! 7. Start delayed work function. 8. cachep in kmem_cache_destroy_work_func() may be NULL Thanks. Thanks for spotting. This is the same problem we have in memcg_cache_destroy(), which I solved by not respawning the worker. In here, I believe it should be possible to just cancel all remaining pending work, since we are now in the process of deleting the caches ourselves. -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
2012/10/19 Glauber Costa : > +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) > +{ > + struct kmem_cache *c; > + int i; > + > + if (!s->memcg_params) > + return; > + if (!s->memcg_params->is_root_cache) > + return; > + > + /* > +* If the cache is being destroyed, we trust that there is no one else > +* requesting objects from it. Even if there are, the sanity checks in > +* kmem_cache_destroy should caught this ill-case. > +* > +* Still, we don't want anyone else freeing memcg_caches under our > +* noses, which can happen if a new memcg comes to life. As usual, > +* we'll take the set_limit_mutex to protect ourselves against this. > +*/ > + mutex_lock(_limit_mutex); > + for (i = 0; i < memcg_limited_groups_array_size; i++) { > + c = s->memcg_params->memcg_caches[i]; > + if (c) > + kmem_cache_destroy(c); > + } > + mutex_unlock(_limit_mutex); > +} It may cause NULL deref. Look at the following scenario. 1. some memcg slab caches has remained object. 2. start to destroy memcg. 3. schedule_delayed_work(kmem_cache_destroy_work_func, @delay 60hz) 4. all remained object is freed. 5. start to destroy root cache. 6. kmem_cache_destroy makes 's->memcg_params->memcg_caches[i]" NULL!! 7. Start delayed work function. 8. cachep in kmem_cache_destroy_work_func() may be NULL Thanks. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
2012/10/19 Glauber Costa glom...@parallels.com: +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ + struct kmem_cache *c; + int i; + + if (!s-memcg_params) + return; + if (!s-memcg_params-is_root_cache) + return; + + /* +* If the cache is being destroyed, we trust that there is no one else +* requesting objects from it. Even if there are, the sanity checks in +* kmem_cache_destroy should caught this ill-case. +* +* Still, we don't want anyone else freeing memcg_caches under our +* noses, which can happen if a new memcg comes to life. As usual, +* we'll take the set_limit_mutex to protect ourselves against this. +*/ + mutex_lock(set_limit_mutex); + for (i = 0; i memcg_limited_groups_array_size; i++) { + c = s-memcg_params-memcg_caches[i]; + if (c) + kmem_cache_destroy(c); + } + mutex_unlock(set_limit_mutex); +} It may cause NULL deref. Look at the following scenario. 1. some memcg slab caches has remained object. 2. start to destroy memcg. 3. schedule_delayed_work(kmem_cache_destroy_work_func, @delay 60hz) 4. all remained object is freed. 5. start to destroy root cache. 6. kmem_cache_destroy makes 's-memcg_params-memcg_caches[i] NULL!! 7. Start delayed work function. 8. cachep in kmem_cache_destroy_work_func() may be NULL Thanks. -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
This enables us to remove all the children of a kmem_cache being destroyed, if for example the kernel module it's being used in gets unloaded. Otherwise, the children will still point to the destroyed parent. Signed-off-by: Suleiman Souhlal Signed-off-by: Glauber Costa CC: Christoph Lameter CC: Pekka Enberg CC: Michal Hocko CC: Kamezawa Hiroyuki CC: Johannes Weiner CC: Tejun Heo --- include/linux/memcontrol.h | 5 + mm/memcontrol.c| 32 ++-- mm/slab_common.c | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 9152d49..5c1d234 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -427,6 +427,7 @@ struct kmem_cache * __memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp); void mem_cgroup_destroy_cache(struct kmem_cache *cachep); +void kmem_cache_destroy_memcg_children(struct kmem_cache *s); /** * memcg_kmem_newpage_charge: verify if a new kmem allocation is allowed. @@ -576,6 +577,10 @@ memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp) { return cachep; } + +static inline void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ +} #endif /* CONFIG_MEMCG_KMEM */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0359b3a..f5089b3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2715,6 +2715,8 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg, memcg_check_events(memcg, page); } +static DEFINE_MUTEX(set_limit_mutex); + #ifdef CONFIG_MEMCG_KMEM static inline bool memcg_can_account_kmem(struct mem_cgroup *memcg) { @@ -3095,6 +3097,34 @@ out: return new_cachep; } +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ + struct kmem_cache *c; + int i; + + if (!s->memcg_params) + return; + if (!s->memcg_params->is_root_cache) + return; + + /* +* If the cache is being destroyed, we trust that there is no one else +* requesting objects from it. Even if there are, the sanity checks in +* kmem_cache_destroy should caught this ill-case. +* +* Still, we don't want anyone else freeing memcg_caches under our +* noses, which can happen if a new memcg comes to life. As usual, +* we'll take the set_limit_mutex to protect ourselves against this. +*/ + mutex_lock(_limit_mutex); + for (i = 0; i < memcg_limited_groups_array_size; i++) { + c = s->memcg_params->memcg_caches[i]; + if (c) + kmem_cache_destroy(c); + } + mutex_unlock(_limit_mutex); +} + struct create_work { struct mem_cgroup *memcg; struct kmem_cache *cachep; @@ -4192,8 +4222,6 @@ void mem_cgroup_print_bad_page(struct page *page) } #endif -static DEFINE_MUTEX(set_limit_mutex); - static int mem_cgroup_resize_limit(struct mem_cgroup *memcg, unsigned long long val) { diff --git a/mm/slab_common.c b/mm/slab_common.c index fcf59d7..c02faf5 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -214,6 +214,9 @@ EXPORT_SYMBOL(kmem_cache_create); void kmem_cache_destroy(struct kmem_cache *s) { + /* Destroy all the children caches if we aren't a memcg cache */ + kmem_cache_destroy_memcg_children(s); + get_online_cpus(); mutex_lock(_mutex); s->refcount--; -- 1.7.11.7 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v5 13/18] memcg/sl[au]b Track all the memcg children of a kmem_cache.
This enables us to remove all the children of a kmem_cache being destroyed, if for example the kernel module it's being used in gets unloaded. Otherwise, the children will still point to the destroyed parent. Signed-off-by: Suleiman Souhlal sulei...@google.com Signed-off-by: Glauber Costa glom...@parallels.com CC: Christoph Lameter c...@linux.com CC: Pekka Enberg penb...@cs.helsinki.fi CC: Michal Hocko mho...@suse.cz CC: Kamezawa Hiroyuki kamezawa.hir...@jp.fujitsu.com CC: Johannes Weiner han...@cmpxchg.org CC: Tejun Heo t...@kernel.org --- include/linux/memcontrol.h | 5 + mm/memcontrol.c| 32 ++-- mm/slab_common.c | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 9152d49..5c1d234 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -427,6 +427,7 @@ struct kmem_cache * __memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp); void mem_cgroup_destroy_cache(struct kmem_cache *cachep); +void kmem_cache_destroy_memcg_children(struct kmem_cache *s); /** * memcg_kmem_newpage_charge: verify if a new kmem allocation is allowed. @@ -576,6 +577,10 @@ memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp) { return cachep; } + +static inline void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ +} #endif /* CONFIG_MEMCG_KMEM */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0359b3a..f5089b3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2715,6 +2715,8 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg, memcg_check_events(memcg, page); } +static DEFINE_MUTEX(set_limit_mutex); + #ifdef CONFIG_MEMCG_KMEM static inline bool memcg_can_account_kmem(struct mem_cgroup *memcg) { @@ -3095,6 +3097,34 @@ out: return new_cachep; } +void kmem_cache_destroy_memcg_children(struct kmem_cache *s) +{ + struct kmem_cache *c; + int i; + + if (!s-memcg_params) + return; + if (!s-memcg_params-is_root_cache) + return; + + /* +* If the cache is being destroyed, we trust that there is no one else +* requesting objects from it. Even if there are, the sanity checks in +* kmem_cache_destroy should caught this ill-case. +* +* Still, we don't want anyone else freeing memcg_caches under our +* noses, which can happen if a new memcg comes to life. As usual, +* we'll take the set_limit_mutex to protect ourselves against this. +*/ + mutex_lock(set_limit_mutex); + for (i = 0; i memcg_limited_groups_array_size; i++) { + c = s-memcg_params-memcg_caches[i]; + if (c) + kmem_cache_destroy(c); + } + mutex_unlock(set_limit_mutex); +} + struct create_work { struct mem_cgroup *memcg; struct kmem_cache *cachep; @@ -4192,8 +4222,6 @@ void mem_cgroup_print_bad_page(struct page *page) } #endif -static DEFINE_MUTEX(set_limit_mutex); - static int mem_cgroup_resize_limit(struct mem_cgroup *memcg, unsigned long long val) { diff --git a/mm/slab_common.c b/mm/slab_common.c index fcf59d7..c02faf5 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -214,6 +214,9 @@ EXPORT_SYMBOL(kmem_cache_create); void kmem_cache_destroy(struct kmem_cache *s) { + /* Destroy all the children caches if we aren't a memcg cache */ + kmem_cache_destroy_memcg_children(s); + get_online_cpus(); mutex_lock(slab_mutex); s-refcount--; -- 1.7.11.7 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/