kmemcg-fixes
Subsystems (4)
cover kmemcg-fixes
Hi,
This patch-set fixes several bugs here and there in the implementation of kmem accounting for memory cgroups and hopefully makes the code look a bit clearer.
Links to discussion threads that led to this patch-set:
http://www.spinics.net/lists/cgroups/msg09512.html
http://www.spinics.net/lists/cgroups/msg09695.html
http://www.spinics.net/lists/cgroups/msg09796.html
Any comments are highly appreciated.
Thanks.
Vladimir Davydov (11):
slab: cleanup kmem_cache_create_memcg() error handling memcg, slab: kmem_cache_create_memcg(): fix memleak on fail path memcg, slab: cleanup memcg cache initialization/destruction memcg, slab: fix barrier usage when accessing memcg_caches
memcg: fix possible NULL deref while traversing memcg_slab_caches
list
memcg, slab: fix races in per-memcg cache creation/destruction
memcg: get rid of kmem_cache_dup
slab: do not panic if we fail to create memcg cache memcg, slab: RCU protect memcg_params for root caches
memcg: remove KMEM_ACCOUNTED_ACTIVATED flag
memcg: rework memcg_update_kmem_limit synchronization
include/linux/memcontrol.h | 23 +-- include/linux/slab.h | 9 +- mm/memcontrol.c | 405 +++++++++++++++++++++----------------------- mm/slab.h | 26 ++- mm/slab_common.c | 90 ++++++---- 5 files changed, 292 insertions(+), 261 deletions(-)
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 1/11 slab: cleanup kmem_cache_create_memcg() error handling
Currently kmem_cache_create_memcg() backoffs on failure inside conditionals, without using gotos. This results in the rollback code duplication, which makes the function look cumbersome even though on error we should only free the allocated cache. Since in the next patch I am going to add yet another rollback function call on error path there, let's employ labels instead of conditionals for undoing any changes on failure to keep things clean.
--- mm/slab_common.c | 65 ++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-)
mm/slab_common.c +31 -34
@@ -171,13 +171,14 @@ kmem_cache_create_memcg(struct mem_cgroup *memcg, const char *name, size_t size, struct kmem_cache *parent_cache) {struct kmem_cache *s =NULL ; -int err =0 ; +int err;get_online_cpus ();mutex_lock (&slab_mutex); -if (!kmem_cache_sanity_check (memcg, name, size) ==0 ) - goto out_locked; + err =kmem_cache_sanity_check (memcg, name, size); +if (err) + goto out_unlock;/* * Some allocators will constraint the set of valid flags to a subset
@@ -189,45 +190,38 @@ kmem_cache_create_memcg(struct mem_cgroup *memcg, const char *name, size_t size, s =__kmem_cache_alias (memcg, name, size, align, flags, ctor);if (s) - goto out_locked; + goto out_unlock; + err = -ENOMEM ; s =kmem_cache_zalloc (kmem_cache,GFP_KERNEL ); -if (s) { - s->object_size = s->size = size; - s->align =calculate_alignment (flags, align, size); - s->ctor = ctor; +if (!s) + goto out_unlock; -if (memcg_register_cache (memcg, s, parent_cache)) { -kmem_cache_free (kmem_cache, s); - err = -ENOMEM ; - goto out_locked; - } + s->object_size = s->size = size; + s->align =calculate_alignment (flags, align, size); + s->ctor = ctor; - s->name =kstrdup (name,GFP_KERNEL ); -if (!s->name) { -kmem_cache_free (kmem_cache, s); - err = -ENOMEM ; - goto out_locked; - } + s->name =kstrdup (name,GFP_KERNEL ); +if (!s->name) + goto out_free_cache; - err =__kmem_cache_create (s, flags); -if (!err) { - s->refcount =1 ; -list_add (&s->list, &slab_caches); -memcg_cache_list_add (memcg, s); - }else { -kfree (s->name); -kmem_cache_free (kmem_cache, s); - } - }else - err = -ENOMEM ; + err =memcg_register_cache (memcg, s, parent_cache); +if (err) + goto out_free_cache; + + err =__kmem_cache_create (s, flags); +if (err) + goto out_free_cache; + + s->refcount =1 ; +list_add (&s->list, &slab_caches); +memcg_cache_list_add (memcg, s); -out_locked: +out_unlock:mutex_unlock (&slab_mutex);put_online_cpus ();if (err) { -if (flags &SLAB_PANIC )panic ("kmem_cache_create: Failed to create slab '%s'. Error %d\n" , name, err);
@@ -236,11 +230,14 @@ out_locked: name, err);dump_stack (); } -return NULL ; } -return s; + +out_free_cache: +kfree (s->name); +kmem_cache_free (kmem_cache, s); + goto out_unlock; } struct kmem_cache *
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 2/11 memcg, slab: kmem_cache_create_memcg(): fix memleak on fail path
In reply to Vladimir DavydovWe do not free the cache's memcg_params if __kmem_cache_create fails. Fix this.
Plus, rename memcg_register_cache() to memcg_alloc_cache_params(), because it actually does not register the cache anywhere, but simply initialize kmem_cache::memcg_params.
--- include/linux/memcontrol.h | 14 +++++++++----- mm/memcontrol.c | 11 ++++++++--- mm/slab_common.c | 3 ++- 3 files changed, 19 insertions(+), 9 deletions(-)
include/linux/memcontrol.h +9 -5
@@ -497,8 +497,9 @@ void __memcg_kmem_commit_charge(struct page *page,void __memcg_kmem_uncharge_pages (struct page *page,int order);int memcg_cache_id (struct mem_cgroup *memcg); -int memcg_register_cache (struct mem_cgroup *memcg,struct kmem_cache *s, -struct kmem_cache *root_cache); +int memcg_alloc_cache_params (struct mem_cgroup *memcg,struct kmem_cache *s, +struct kmem_cache *root_cache); +void memcg_free_cache_params (struct kmem_cache *s);void memcg_release_cache (struct kmem_cache *cachep);void memcg_cache_list_add (struct mem_cgroup *memcg,struct kmem_cache *cachep);
@@ -640,13 +641,16 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)return -1 ; } -static inline int -memcg_register_cache (struct mem_cgroup *memcg,struct kmem_cache *s, -struct kmem_cache *root_cache) +static inline int memcg_alloc_cache_params (struct mem_cgroup *memcg, +struct kmem_cache *s,struct kmem_cache *root_cache) {return 0 ; } +static inline void memcg_free_cache_params (struct kmem_cache *s); +{ +} +static inline void memcg_release_cache (struct kmem_cache *cachep) { }
mm/memcontrol.c +8 -3
@@ -3195,8 +3195,8 @@ int memcg_update_cache_size(struct kmem_cache *s, int num_groups)return 0 ; } -int memcg_register_cache (struct mem_cgroup *memcg,struct kmem_cache *s, -struct kmem_cache *root_cache) +int memcg_alloc_cache_params (struct mem_cgroup *memcg,struct kmem_cache *s, +struct kmem_cache *root_cache) {size_t size;
@@ -3224,6 +3224,11 @@ int memcg_register_cache(struct mem_cgroup *memcg, struct kmem_cache *s,return 0 ; } +void memcg_free_cache_params (struct kmem_cache *s) +{ +kfree (s->memcg_params); +} +void memcg_release_cache (struct kmem_cache *s) {struct kmem_cache *root;
@@ -3252,7 +3257,7 @@ void memcg_release_cache(struct kmem_cache *s)css_put (&memcg->css); out: -kfree (s->memcg_params); +memcg_free_cache_params (s); }/*
mm/slab_common.c +2 -1
@@ -205,7 +205,7 @@ kmem_cache_create_memcg(struct mem_cgroup *memcg, const char *name, size_t size,if (!s->name) goto out_free_cache; - err =memcg_register_cache (memcg, s, parent_cache); + err =memcg_alloc_cache_params (memcg, s, parent_cache);if (err) goto out_free_cache;
@@ -235,6 +235,7 @@ out_unlock:return s; out_free_cache: +memcg_free_cache_params (s);kfree (s->name);kmem_cache_free (kmem_cache, s); goto out_unlock;
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 3/11 memcg, slab: cleanup memcg cache initialization/destruction
In reply to Vladimir DavydovCurrently, we have rather a messy function set relating to per-memcg kmem cache initialization/destruction.
Per-memcg caches are created in memcg_create_kmem_cache(). This function calls kmem_cache_create_memcg() to allocate and initialize a kmem cache and then "registers" the new cache in the memcg_params::memcg_caches array of the parent cache.
During its work-flow, kmem_cache_create_memcg() executes the following memcg-related functions:
- memcg_alloc_cache_params(), to initialize memcg_params of the newly created cache;
- memcg_cache_list_add(), to add the new cache to the memcg_slab_caches list.
On the other hand, kmem_cache_destroy() called on a cache destruction only calls memcg_release_cache(), which does all the work: it cleans the reference to the cache in its parent's memcg_params::memcg_caches, removes the cache from the memcg_slab_caches list, and frees memcg_params.
Such an inconsistency between destruction and initialization paths make the code difficult to read, so let's clean this up a bit.
This patch moves all the code relating to registration of per-memcg caches (adding to memcg list, setting the pointer to a cache from its parent) to the newly created memcg_register_cache() and memcg_unregister_cache() functions making the initialization and destruction paths look symmetrical.
--- include/linux/memcontrol.h | 9 +++---- mm/memcontrol.c | 64 +++++++++++++++++++++----------------------- mm/slab_common.c | 5 ++-- 3 files changed, 37 insertions(+), 41 deletions(-)
include/linux/memcontrol.h +4 -5
@@ -500,8 +500,8 @@ int memcg_cache_id(struct mem_cgroup *memcg);int memcg_alloc_cache_params (struct mem_cgroup *memcg,struct kmem_cache *s,struct kmem_cache *root_cache);void memcg_free_cache_params (struct kmem_cache *s); -void memcg_release_cache (struct kmem_cache *cachep); -void memcg_cache_list_add (struct mem_cgroup *memcg,struct kmem_cache *cachep); +void memcg_register_cache (struct kmem_cache *s); +void memcg_unregister_cache (struct kmem_cache *s);int memcg_update_cache_size (struct kmem_cache *s,int num_groups);void memcg_update_array_size (int num_groups);
@@ -651,12 +651,11 @@ static inline void memcg_free_cache_params(struct kmem_cache *s); { } -static inline void memcg_release_cache (struct kmem_cache *cachep) +static inline void memcg_register_cache (struct kmem_cache *s) { } -static inline void memcg_cache_list_add (struct mem_cgroup *memcg, -struct kmem_cache *s) +static inline void memcg_unregister_cache (struct kmem_cache *s) { }
mm/memcontrol.c +30 -34
107 lines not on this page · raw message
mm/slab_common.c +3 -2
@@ -215,7 +215,7 @@ kmem_cache_create_memcg(struct mem_cgroup *memcg, const char *name, size_t size, s->refcount =1 ;list_add (&s->list, &slab_caches); -memcg_cache_list_add (memcg, s); +memcg_register_cache (s); out_unlock:mutex_unlock (&slab_mutex);
@@ -265,7 +265,8 @@ void kmem_cache_destroy(struct kmem_cache *s)if (s->flags &SLAB_DESTROY_BY_RCU )rcu_barrier (); -memcg_release_cache (s); +memcg_unregister_cache (s); +memcg_free_cache_params (s);kfree (s->name);kmem_cache_free (kmem_cache, s); }else {
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 4/11 memcg, slab: fix barrier usage when accessing memcg_caches
In reply to Vladimir DavydovEach root kmem_cache has pointers to per-memcg caches stored in its memcg_params::memcg_caches array. Whenever we want to allocate a slab for a memcg, we access this array to get per-memcg cache to allocate from (see memcg_kmem_get_cache()). The access must be lock-free for performance reasons, so we should use barriers to assert the kmem_cache is up-to-date.
First, we should place a write barrier immediately before setting the pointer to it in the memcg_caches array in order to make sure nobody will see a partially initialized object. Second, we should issue a read barrier before dereferencing the pointer to conform to the write barrier.
However, currently the barrier usage looks rather strange. We have a write barrier *after* setting the pointer and a read barrier *before* reading the pointer, which is incorrect. This patch fixes this.
--- mm/memcontrol.c | 24 ++++++++++-------------- mm/slab.h | 12 +++++++++++- 2 files changed, 21 insertions(+), 15 deletions(-)
mm/memcontrol.c +10 -14
@@ -3238,12 +3238,14 @@ void memcg_register_cache(struct kmem_cache *s)list_add (&s->memcg_params->list, &memcg->memcg_slab_caches);mutex_unlock (&memcg->slab_caches_mutex); - root->memcg_params->memcg_caches[id] = s;/* -* the readers won't lock, make sure everybody sees the updated value, -* so they won't put stuff in the queue again for no reason +* Since readers won't lock (see cache_from_memcg_idx()), we need a +* barrier here to ensure nobody will see the kmem_cache partially +* initialized. */ -wmb (); +smp_wmb (); + + root->memcg_params->memcg_caches[id] = s; }void memcg_unregister_cache (struct kmem_cache *s)
@@ -3569,7 +3571,7 @@ struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp) {struct mem_cgroup *memcg; -int idx; +struct kmem_cache *memcg_cachep;VM_BUG_ON (!cachep->memcg_params);VM_BUG_ON (!cachep->memcg_params->is_root_cache);
@@ -3583,15 +3585,9 @@ struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep,if (!memcg_can_account_kmem (memcg)) goto out; - idx =memcg_cache_id (memcg); - -/* -* barrier to mare sure we're always seeing the up to date value. The -* code updating memcg_caches will issue a write barrier to match this. -*/ -read_barrier_depends (); -if (likely (cache_from_memcg_idx (cachep, idx))) { - cachep =cache_from_memcg_idx (cachep, idx); + memcg_cachep =cache_from_memcg_idx (cachep,memcg_cache_id (memcg)); +if (likely (memcg_cachep)) { + cachep = memcg_cachep; goto out; }
mm/slab.h +11 -1
@@ -163,9 +163,19 @@ static inline const char *cache_name(struct kmem_cache *s)static inline struct kmem_cache *cache_from_memcg_idx (struct kmem_cache *s,int idx) { +struct kmem_cache *cachep; +if (!s->memcg_params)return NULL ; -return s->memcg_params->memcg_caches[idx]; + cachep = s->memcg_params->memcg_caches[idx]; + +/* +* Make sure we will access the up-to-date value. The code updating +* memcg_caches issues a write barrier to match this (see +* memcg_register_cache()). +*/ +smp_read_barrier_depends (); +return cachep; }static inline struct kmem_cache *memcg_root_cache (struct kmem_cache *s)
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 5/11 memcg: fix possible NULL deref while traversing memcg_slab_caches list
In reply to Vladimir DavydovAll caches of the same memory cgroup are linked in the memcg_slab_caches list via kmem_cache::memcg_params::list. This list is traversed, for example, when we read memory.kmem.slabinfo. Since the list actually consists of memcg_cache_params objects, we have to convert an element of the list to a kmem_cache object using memcg_params_to_cache(), which obtains the pointer to the cache from the memcg_params::memcg_caches array of the corresponding root cache. That said the pointer to a kmem_cache in its parent's memcg_params must be initialized before adding the cache to the list, and cleared only after it has been unlinked. Currently it is vice-versa, which can result in a NULL ptr dereference while traversing the memcg_slab_caches list. This patch restores the correct order.
--- mm/memcontrol.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-)
mm/memcontrol.c +18 -7
@@ -3234,9 +3234,6 @@ void memcg_register_cache(struct kmem_cache *s)css_get (&memcg->css); -mutex_lock (&memcg->slab_caches_mutex); -list_add (&s->memcg_params->list, &memcg->memcg_slab_caches); -mutex_unlock (&memcg->slab_caches_mutex);/* * Since readers won't lock (see cache_from_memcg_idx()), we need a
@@ -3245,7 +3242,16 @@ void memcg_register_cache(struct kmem_cache *s)*/ smp_wmb (); +/* +* Initialize the pointer to this cache in its parent's memcg_params +* before adding it to the memcg_slab_caches list, otherwise we can +* fail to convert memcg_params_to_cache() while traversing the list. +*/ root->memcg_params->memcg_caches[id] = s; + +mutex_lock (&memcg->slab_caches_mutex); +list_add (&s->memcg_params->list, &memcg->memcg_slab_caches); +mutex_unlock (&memcg->slab_caches_mutex); }void memcg_unregister_cache (struct kmem_cache *s)
@@ -3257,16 +3263,21 @@ void memcg_unregister_cache(struct kmem_cache *s)if (is_root_cache (s))return ; - memcg = s->memcg_params->memcg; - id =memcg_cache_id (memcg); - root = s->memcg_params->root_cache; - root->memcg_params->memcg_caches[id] =NULL ; + memcg = s->memcg_params->memcg; + id =memcg_cache_id (memcg);mutex_lock (&memcg->slab_caches_mutex);list_del (&s->memcg_params->list);mutex_unlock (&memcg->slab_caches_mutex); +/* +* Clear the pointer to this cache in its parent's memcg_params only +* after removing it from the memcg_slab_caches list, otherwise we can +* fail to convert memcg_params_to_cache() while traversing the list. +*/ + root->memcg_params->memcg_caches[id] =NULL ; +css_put (&memcg->css); }
Signature · 1 line
1.7.10.4
PATCH RESEND 6/11 memcg, slab: fix races in per-memcg cache creation/destruction
In reply to Vladimir DavydovWe obtain a per-memcg cache from a root kmem_cache by dereferencing an entry of the root cache's memcg_params::memcg_caches array. If we find no cache for a memcg there on allocation, we initiate the memcg cache creation (see memcg_kmem_get_cache()). The cache creation proceeds asynchronously in memcg_create_kmem_cache() in order to avoid lock clashes, so there can be several threads trying to create the same kmem_cache concurrently, but only one of them may succeed. However, due to a race in the code, it is not always true. The point is that the memcg_caches array can be relocated when we activate kmem accounting for a memcg (see memcg_update_all_caches(), memcg_update_cache_size()). If memcg_update_cache_size() and memcg_create_kmem_cache() proceed concurrently as described below, we can leak a kmem_cache.
Asume two threads schedule creation of the same kmem_cache. One of them successfully creates it. Another one should fail then, but if memcg_create_kmem_cache() interleaves with memcg_update_cache_size() as follows, it won't:
memcg_create_kmem_cache() memcg_update_cache_size()
(called w/o mutexes held) (called with slab_mutex,
set_limit_mutex held)
------------------------- -------------------------
mutex_lock(&memcg_cache_mutex)
s->memcg_params=kzalloc(...)
new_cachep=cache_from_memcg_idx(cachep,idx)
// new_cachep==NULL => proceed to creation
s->memcg_params->memcg_caches[i]
=cur_params->memcg_caches[i] // kmem_cache_create_memcg takes slab_mutex
// so we will hang around until
// memcg_update_cache_size finishes, but
// nothing will prevent it from succeeding so
// memcg_caches[idx] will be overwritten in
// memcg_register_cache!
new_cachep = kmem_cache_create_memcg(...)
mutex_unlock(&memcg_cache_mutex)
Let's fix this by moving the check for existence of the memcg cache to kmem_cache_create_memcg() to be called under the slab_mutex and make it return NULL if so.
A similar race is possible when destroying a memcg cache (see kmem_cache_destroy()). Since memcg_unregister_cache(), which clears the pointer in the memcg_caches array, is called w/o protection, we can race with memcg_update_cache_size() and omit clearing the pointer. Therefore memcg_unregister_cache() should be moved before we release the slab_mutex.
--- mm/memcontrol.c | 23 ++++++++++++++--------- mm/slab_common.c | 14 +++++++++++++- 2 files changed, 27 insertions(+), 10 deletions(-)
mm/memcontrol.c +14 -9
@@ -3228,6 +3228,12 @@ void memcg_register_cache(struct kmem_cache *s)if (is_root_cache (s))return ; +/* +* Holding the slab_mutex assures nobody will touch the memcg_caches +* array while we are modifying it. +*/ +lockdep_assert_held (&slab_mutex); + root = s->memcg_params->root_cache; memcg = s->memcg_params->memcg; id =memcg_cache_id (memcg);
@@ -3247,6 +3253,7 @@ void memcg_register_cache(struct kmem_cache *s)* before adding it to the memcg_slab_caches list, otherwise we can * fail to convert memcg_params_to_cache() while traversing the list. */ +VM_BUG_ON (root->memcg_params->memcg_caches[id]); root->memcg_params->memcg_caches[id] = s;mutex_lock (&memcg->slab_caches_mutex);
@@ -3263,6 +3270,12 @@ void memcg_unregister_cache(struct kmem_cache *s)if (is_root_cache (s))return ; +/* +* Holding the slab_mutex assures nobody will touch the memcg_caches +* array while we are modifying it. +*/ +lockdep_assert_held (&slab_mutex); + root = s->memcg_params->root_cache; memcg = s->memcg_params->memcg; id =memcg_cache_id (memcg);
@@ -3276,6 +3289,7 @@ void memcg_unregister_cache(struct kmem_cache *s)* after removing it from the memcg_slab_caches list, otherwise we can * fail to convert memcg_params_to_cache() while traversing the list. */ +VM_BUG_ON (!root->memcg_params->memcg_caches[id]); root->memcg_params->memcg_caches[id] =NULL ;css_put (&memcg->css);
@@ -3428,22 +3442,13 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg, struct kmem_cache *cachep) {struct kmem_cache *new_cachep; -int idx;BUG_ON (!memcg_can_account_kmem (memcg)); - idx =memcg_cache_id (memcg); -mutex_lock (&memcg_cache_mutex); - new_cachep =cache_from_memcg_idx (cachep, idx); -if (new_cachep) - goto out; - new_cachep =kmem_cache_dup (memcg, cachep);if (new_cachep ==NULL ) new_cachep = cachep; - -out:mutex_unlock (&memcg_cache_mutex);return new_cachep; }
mm/slab_common.c +13 -1
@@ -180,6 +180,18 @@ kmem_cache_create_memcg(struct mem_cgroup *memcg, const char *name, size_t size,if (err) goto out_unlock; +if (memcg) { +/* +* Since per-memcg caches are created asynchronously on first +* allocation (see memcg_kmem_get_cache()), several threads can +* try to create the same cache, but only one of them may +* succeed. Therefore if we get here and see the cache has +* already been created, we silently return NULL. +*/ +if (cache_from_memcg_idx (parent_cache,memcg_cache_id (memcg))) + goto out_unlock; + } +/* * Some allocators will constraint the set of valid flags to a subset * of all flags. We expect them to define CACHE_CREATE_MASK in this
@@ -261,11 +273,11 @@ void kmem_cache_destroy(struct kmem_cache *s)list_del (&s->list);if (!__kmem_cache_shutdown (s)) { +memcg_unregister_cache (s);mutex_unlock (&slab_mutex);if (s->flags &SLAB_DESTROY_BY_RCU )rcu_barrier (); -memcg_unregister_cache (s);memcg_free_cache_params (s);kfree (s->name);kmem_cache_free (kmem_cache, s);
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 7/11 memcg: get rid of kmem_cache_dup
In reply to Vladimir Davydovkmem_cache_dup() is only called from memcg_create_kmem_cache(). The latter, in fact, does nothing besides this, so let's fold kmem_cache_dup() into memcg_create_kmem_cache().
This patch also makes the memcg_cache_mutex private to memcg_create_kmem_cache(), because it is not used anywhere else.
--- mm/memcontrol.c | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-)
mm/memcontrol.c +8 -31
@@ -3391,27 +3391,16 @@ void mem_cgroup_destroy_cache(struct kmem_cache *cachep)schedule_work (&cachep->memcg_params->destroy); } -/* -* This lock protects updaters, not readers. We want readers to be as fast as -* they can, and they will either see NULL or a valid cache value. Our model -* allow them to see NULL, in which case the root memcg will be selected. -* -* We need this lock because multiple allocations to the same cache from a non -* will span more than one worker. Only one of them can create the cache. -*/ -static DEFINE_MUTEX (memcg_cache_mutex); - -/* -* Called with memcg_cache_mutex held -*/ -static struct kmem_cache *kmem_cache_dup (struct mem_cgroup *memcg, -struct kmem_cache *s) +static struct kmem_cache *memcg_create_kmem_cache (struct mem_cgroup *memcg, +struct kmem_cache *s) {struct kmem_cache *new;static char *tmp_name =NULL ; +static DEFINE_MUTEX (mutex);/* protects tmp_name */ -lockdep_assert_held (&memcg_cache_mutex); +BUG_ON (!memcg_can_account_kmem (memcg)); +mutex_lock (&mutex);/* * kmem_cache_create_memcg duplicates the given name and * cgroup_name for this name requires RCU context.
@@ -3434,25 +3423,13 @@ static struct kmem_cache *kmem_cache_dup(struct mem_cgroup *memcg,if (new) new->allocflags |= __GFP_KMEMCG; +else + new = s; +mutex_unlock (&mutex);return new; } -static struct kmem_cache *memcg_create_kmem_cache (struct mem_cgroup *memcg, -struct kmem_cache *cachep) -{ -struct kmem_cache *new_cachep; - -BUG_ON (!memcg_can_account_kmem (memcg)); - -mutex_lock (&memcg_cache_mutex); - new_cachep =kmem_cache_dup (memcg, cachep); -if (new_cachep ==NULL ) - new_cachep = cachep; -mutex_unlock (&memcg_cache_mutex); -return new_cachep; -} -void kmem_cache_destroy_memcg_children (struct kmem_cache *s) {struct kmem_cache *c;
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 8/11 slab: do not panic if we fail to create memcg cache
In reply to Vladimir DavydovThere is no point in flooding logs with warnings or especially crashing the system if we fail to create a cache for a memcg. In this case we will be accounting the memcg allocation to the root cgroup until we succeed to create its own cache, but it isn't that critical.
--- mm/slab_common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
mm/slab_common.c +8 -1
@@ -233,7 +233,14 @@ out_unlock:mutex_unlock (&slab_mutex);put_online_cpus (); -if (err) { +/* +* There is no point in flooding logs with warnings or especially +* crashing the system if we fail to create a cache for a memcg. In +* this case we will be accounting the memcg allocation to the root +* cgroup until we succeed to create its own cache, but it isn't that +* critical. +*/ +if (err && !memcg) {if (flags &SLAB_PANIC )panic ("kmem_cache_create: Failed to create slab '%s'. Error %d\n" , name, err);
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 9/11 memcg, slab: RCU protect memcg_params for root caches
In reply to Vladimir DavydovWe relocate root cache's memcg_params whenever we need to grow the memcg_caches array to accommodate all kmem-active memory cgroups. Currently on relocation we free the old version immediately, which can lead to use-after-free, because the memcg_caches array is accessed lock-free (see cache_from_memcg_idx()). This patch fixes this by making memcg_params RCU-protected for root caches.
--- include/linux/slab.h | 9 +++++++-- mm/memcontrol.c | 15 ++++++++------- mm/slab.h | 16 +++++++++++++++- 3 files changed, 30 insertions(+), 10 deletions(-)
include/linux/slab.h +7 -2
@@ -513,7 +513,9 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)* * Both the root cache and the child caches will have it. For the root cache, * this will hold a dynamically allocated array large enough to hold -* information about the currently limited memcgs in the system. +* information about the currently limited memcgs in the system. To allow the +* array to be accessed without taking any locks, on relocation we free the old +* version only after a grace period. * * Child caches will hold extra metadata needed for its operation. Fields are: *
@@ -528,7 +530,10 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) struct memcg_cache_params { bool is_root_cache; union { - struct kmem_cache *memcg_caches[0]; + struct { + struct rcu_head rcu_head; + struct kmem_cache *memcg_caches[0]; + }; struct { struct mem_cgroup *memcg; struct list_head list;
mm/memcontrol.c +8 -7
@@ -3142,18 +3142,17 @@ int memcg_update_cache_size(struct kmem_cache *s, int num_groups)if (num_groups > memcg_limited_groups_array_size) {int i; +struct memcg_cache_params *new_params;ssize_t size =memcg_caches_array_size (num_groups); size *=sizeof (void *); size += offsetof(struct memcg_cache_params , memcg_caches); - s->memcg_params =kzalloc (size,GFP_KERNEL ); -if (!s->memcg_params) { - s->memcg_params = cur_params; + new_params =kzalloc (size,GFP_KERNEL ); +if (!new_params)return -ENOMEM ; - } - s->memcg_params->is_root_cache = true; + new_params->is_root_cache = true;/* * There is the chance it will be bigger than
@@ -3167,7 +3166,7 @@ int memcg_update_cache_size(struct kmem_cache *s, int num_groups)for (i =0 ; i < memcg_limited_groups_array_size; i++) {if (!cur_params->memcg_caches[i])continue ; - s->memcg_params->memcg_caches[i] = + new_params->memcg_caches[i] = cur_params->memcg_caches[i]; }
@@ -3180,7 +3179,9 @@ int memcg_update_cache_size(struct kmem_cache *s, int num_groups)* bigger than the others. And all updates will reset this * anyway. */ -kfree (cur_params); +rcu_assign_pointer (s->memcg_params, new_params); +if (cur_params) +kfree_rcu (cur_params, rcu_head); }return 0 ; }
mm/slab.h +15 -1
@@ -160,14 +160,28 @@ static inline const char *cache_name(struct kmem_cache *s)return s->name; } +/* +* Note, we protect with RCU only the memcg_caches array, not per-memcg caches. +* That said the caller must assure the memcg's cache won't go away. Since once +* created a memcg's cache is destroyed only along with the root cache, it is +* true if we are going to allocate from the cache or hold a reference to the +* root cache by other means. Otherwise, we should hold either the slab_mutex +* or the memcg's slab_caches_mutex while calling this function and accessing +* the returned value. +*/ static inline struct kmem_cache *cache_from_memcg_idx (struct kmem_cache *s,int idx) {struct kmem_cache *cachep; +struct memcg_cache_params *params;if (!s->memcg_params)return NULL ; - cachep = s->memcg_params->memcg_caches[idx]; + +rcu_read_lock (); + params =rcu_dereference (s->memcg_params); + cachep = params->memcg_caches[idx]; +rcu_read_unlock ();/* * Make sure we will access the up-to-date value. The code updating
Signature · 1 line
1.7.10.4
PATCH RESEND 10/11 memcg: remove KMEM_ACCOUNTED_ACTIVATED flag
In reply to Vladimir DavydovCurrently we have two state bits in mem_cgroup::kmem_account_flags regarding kmem accounting activation, ACTIVATED and ACTIVE. We start kmem accounting only if both flags are set (memcg_can_account_kmem()), plus throughout the code there are several places where we check only the ACTIVE flag, but we never check the ACTIVATED flag alone. These flags are both set from memcg_update_kmem_limit() under the set_limit_mutex, the ACTIVE flag always being set after ACTIVATED, and they never get cleared. That said checking if both flags are set is equivalent to checking only for the ACTIVE flag, and since there is no ACTIVATED flag checks, we can safely remove the ACTIVATED flag, and nothing will change.
Let's try to understand what was the reason for introducing these flags. The purpose of the ACTIVE flag is clear - it states that kmem should be accounting to the cgroup. The only requirement for it is that it should be set after we have fully initialized kmem accounting bits for the cgroup and patched all static branches relating to kmem accounting. Since we always check if static branch is enabled before actually considering if we should account (otherwise we wouldn't benefit from static branching), this guarantees us that we won't skip a commit or uncharge after a charge due to an unpatched static branch.
Now let's move on to the ACTIVATED bit. As I proved in the beginning of this message, it is absolutely useless, and removing it will change nothing. So what was the reason introducing it?
The ACTIVATED flag was introduced by commit a8964b9b ("memcg: use static branches when code not in use") in order to guarantee that static_key_slow_inc(&memcg_kmem_enabled_key) would be called only once for each memory cgroup when its kmem accounting was activated. The point was that at that time the memcg_update_kmem_limit() function's work-flow looked like this:
bool must_inc_static_branch = false;
cgroup_lock();
mutex_lock(&set_limit_mutex);
if (!memcg->kmem_account_flags && val != RESOURCE_MAX) {
/* The kmem limit is set for the first time */
ret = res_counter_set_limit(&memcg->kmem, val);
memcg_kmem_set_activated(memcg);
must_inc_static_branch = true;
} else
ret = res_counter_set_limit(&memcg->kmem, val);
mutex_unlock(&set_limit_mutex);
cgroup_unlock();
if (must_inc_static_branch) {
/* We can't do this under cgroup_lock */
static_key_slow_inc(&memcg_kmem_enabled_key);
memcg_kmem_set_active(memcg);
}So that without the ACTIVATED flag we could race with other threads trying to set the limit and increment the static branching ref-counter more than once. Today we call the whole memcg_update_kmem_limit() function under the set_limit_mutex and this race is impossible.
As now we understand why the ACTIVATED bit was introduced and why we don't need it now, and know that removing it will change nothing anyway, let's get rid of it.
--- mm/memcontrol.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-)
mm/memcontrol.c +2 -26
@@ -343,15 +343,10 @@ static size_t memcg_size(void)/* internal only representation about the status of kmem accounting. */ enum { -KMEM_ACCOUNTED_ACTIVE =0 ,/* accounted by this cgroup itself */ -KMEM_ACCOUNTED_ACTIVATED ,/* static key enabled. */ +KMEM_ACCOUNTED_ACTIVE ,/* accounted by this cgroup itself */ KMEM_ACCOUNTED_DEAD ,/* dead memcg with pending kmem charges */ }; -/* We account when limit is on, but only after call sites are patched */ -#define KMEM_ACCOUNTED_MASK \ - ((1 << KMEM_ACCOUNTED_ACTIVE) | (1 << KMEM_ACCOUNTED_ACTIVATED)) -#ifdef CONFIG_MEMCG_KMEM static inline void memcg_kmem_set_active (struct mem_cgroup *memcg) {
@@ -363,16 +358,6 @@ static bool memcg_kmem_is_active(struct mem_cgroup *memcg)return test_bit (KMEM_ACCOUNTED_ACTIVE , &memcg->kmem_account_flags); } -static void memcg_kmem_set_activated (struct mem_cgroup *memcg) -{ -set_bit (KMEM_ACCOUNTED_ACTIVATED , &memcg->kmem_account_flags); -} - -static void memcg_kmem_clear_activated (struct mem_cgroup *memcg) -{ -clear_bit (KMEM_ACCOUNTED_ACTIVATED , &memcg->kmem_account_flags); -} -static void memcg_kmem_mark_dead (struct mem_cgroup *memcg) {/*
@@ -2959,7 +2944,7 @@ static DEFINE_MUTEX(set_limit_mutex);static inline bool memcg_can_account_kmem (struct mem_cgroup *memcg) {return !mem_cgroup_disabled () && !mem_cgroup_is_root (memcg) && - (memcg->kmem_account_flags &KMEM_ACCOUNTED_MASK ); +memcg_kmem_is_active (memcg); }/*
@@ -3084,19 +3069,10 @@ int memcg_update_cache_sizes(struct mem_cgroup *memcg) 0, MEMCG_CACHES_MAX_SIZE, GFP_KERNEL);if (num <0 )return num; -/* -* After this point, kmem_accounted (that we test atomically in -* the beginning of this conditional), is no longer 0. This -* guarantees only one process will set the following boolean -* to true. We don't need test_and_set because we're protected -* by the set_limit_mutex anyway. -*/ -memcg_kmem_set_activated (memcg); ret =memcg_update_all_caches (num+1 );if (ret) {ida_simple_remove (&kmem_limited_groups, num); -memcg_kmem_clear_activated (memcg);return ret; }
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH RESEND 11/11 memcg: rework memcg_update_kmem_limit synchronization
In reply to Vladimir DavydovCurrently we take both the memcg_create_mutex and the set_limit_mutex when we enable kmem accounting for a memory cgroup, which makes kmem activation events serialize with both memcg creations and other memcg limit updates (memory.limit, memory.memsw.limit). However, there is no point in such strict synchronization rules there.
First, the set_limit_mutex was introduced to keep the memory.limit and memory.memsw.limit values in sync. Since memory.kmem.limit can be set independently of them, it is better to introduce a separate mutex to synchronize against concurrent kmem limit updates.
Second, we take the memcg_create_mutex in order to make sure all children of this memcg will be kmem-active as well. For achieving that, it is enough to hold this mutex only while checking if memcg_has_children() though. This guarantees that if a child is added after we checked that the memcg has no children, the newly added cgroup will see its parent kmem-active (of course if the latter succeeded), and call kmem activation for itself.
This patch simplifies the locking rules of memcg_update_kmem_limit() according to these considerations.
--- mm/memcontrol.c | 198 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 106 insertions(+), 92 deletions(-)
mm/memcontrol.c +106 -92
@@ -2941,6 +2941,8 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,static DEFINE_MUTEX (set_limit_mutex);#ifdef CONFIG_MEMCG_KMEM +static DEFINE_MUTEX (activate_kmem_mutex); +static inline bool memcg_can_account_kmem (struct mem_cgroup *memcg) { return !mem_cgroup_disabled() && !mem_cgroup_is_root(memcg) &&
@@ -3054,34 +3056,6 @@ int memcg_cache_id(struct mem_cgroup *memcg)return memcg ? memcg->kmemcg_id :-1 ; } -/* -* This ends up being protected by the set_limit mutex, during normal -* operation, because that is its main call site. -* -* But when we create a new cache, we can call this as well if its parent -* is kmem-limited. That will have to hold set_limit_mutex as well. -*/ -int memcg_update_cache_sizes (struct mem_cgroup *memcg) -{ -int num, ret; - - num =ida_simple_get (&kmem_limited_groups, -0 ,MEMCG_CACHES_MAX_SIZE ,GFP_KERNEL ); -if (num <0 ) -return num; - - ret =memcg_update_all_caches (num+1 ); -if (ret) { -ida_simple_remove (&kmem_limited_groups, num); -return ret; - } - - memcg->kmemcg_id = num; -INIT_LIST_HEAD (&memcg->memcg_slab_caches); -mutex_init (&memcg->slab_caches_mutex); -return 0 ; -} -static size_t memcg_caches_array_size (int num_groups) {ssize_t size;
@@ -3424,9 +3398,10 @@ void kmem_cache_destroy_memcg_children(struct kmem_cache *s)* * 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. +* we'll take the activate_kmem_mutex to protect ourselves against +* this. */ -mutex_lock (&set_limit_mutex); +mutex_lock (&activate_kmem_mutex);for_each_memcg_cache_index (i) { c =cache_from_memcg_idx (s, i);if (!c)
@@ -3449,7 +3424,7 @@ void kmem_cache_destroy_memcg_children(struct kmem_cache *s)cancel_work_sync (&c->memcg_params->destroy);kmem_cache_destroy (c); } -mutex_unlock (&set_limit_mutex); +mutex_unlock (&activate_kmem_mutex); }struct create_work {
@@ -5116,11 +5091,23 @@ static ssize_t mem_cgroup_read(struct cgroup_subsys_state *css,return simple_read_from_buffer (buf, nbytes, ppos, str, len); } -static int memcg_update_kmem_limit (struct cgroup_subsys_state *css,u64 val) -{ -int ret = -EINVAL ;#ifdef CONFIG_MEMCG_KMEM -struct mem_cgroup *memcg =mem_cgroup_from_css (css); +/* should be called with activate_kmem_mutex held */ +static int __memcg_activate_kmem (struct mem_cgroup *memcg, +unsigned long long limit) +{ +int err =0 ; +int memcg_id; + +if (memcg_kmem_is_active (memcg)) +return 0 ; + +/* +* We are going to allocate memory for data shared by all memory +* cgroups so let's stop accounting here. +*/ +memcg_stop_kmem_account (); +/* * For simplicity, we won't allow this to be disabled. It also can't * be changed if the cgroup has children already, or if tasks had
@@ -5134,72 +5121,101 @@ static int memcg_update_kmem_limit(struct cgroup_subsys_state *css, u64 val)* of course permitted. */ mutex_lock (&memcg_create_mutex); -mutex_lock (&set_limit_mutex); -if (!memcg->kmem_account_flags && val !=RES_COUNTER_MAX ) { -if (cgroup_task_count (css->cgroup) ||memcg_has_children (memcg)) { - ret = -EBUSY ; - goto out; - } - ret =res_counter_set_limit (&memcg->kmem, val); -VM_BUG_ON (ret); +if (cgroup_task_count (memcg->css.cgroup) ||memcg_has_children (memcg)) + err = -EBUSY ; +mutex_unlock (&memcg_create_mutex); +if (err) + goto out; - ret =memcg_update_cache_sizes (memcg); -if (ret) { -res_counter_set_limit (&memcg->kmem,RES_COUNTER_MAX ); - goto out; - } -static_key_slow_inc (&memcg_kmem_enabled_key); -/* -* setting the active bit after the inc will guarantee no one -* starts accounting before all call sites are patched -*/ -memcg_kmem_set_active (memcg); - }else - ret =res_counter_set_limit (&memcg->kmem, val); + memcg_id =ida_simple_get (&kmem_limited_groups, +0 ,MEMCG_CACHES_MAX_SIZE ,GFP_KERNEL ); +if (memcg_id <0 ) { + err = memcg_id; + goto out; + } + +/* +* Make sure we have enough space for this cgroup in each root cache's +* memcg_params. +*/ + err =memcg_update_all_caches (memcg_id +1 ); +if (err) + goto out_rmid; + + memcg->kmemcg_id = memcg_id; +INIT_LIST_HEAD (&memcg->memcg_slab_caches); +mutex_init (&memcg->slab_caches_mutex); + +/* +* We couldn't have accounted to this cgroup, because it hasn't got the +* active bit set yet, so this should succeed. +*/ + err =res_counter_set_limit (&memcg->kmem, limit); +VM_BUG_ON (err); + +static_key_slow_inc (&memcg_kmem_enabled_key); +/* +* Setting the active bit after enabling static branching will +* guarantee no one starts accounting before all call sites are +* patched. +*/ +memcg_kmem_set_active (memcg); out: -mutex_unlock (&set_limit_mutex); -mutex_unlock (&memcg_create_mutex); -#endif +memcg_resume_kmem_account (); +return err; + +out_rmid: +ida_simple_remove (&kmem_limited_groups, memcg_id); + goto out; +} + +static int memcg_activate_kmem (struct mem_cgroup *memcg, +unsigned long long limit) +{ +int ret; + +mutex_lock (&activate_kmem_mutex); + ret =__memcg_activate_kmem (memcg, limit); +mutex_unlock (&activate_kmem_mutex); +return ret; +} + +static int memcg_update_kmem_limit (struct mem_cgroup *memcg, +unsigned long long val) +{ +int ret; + +if (!memcg_kmem_is_active (memcg)) + ret =memcg_activate_kmem (memcg, val); +else + ret =res_counter_set_limit (&memcg->kmem, val);return ret; } -#ifdef CONFIG_MEMCG_KMEM static int memcg_propagate_kmem (struct mem_cgroup *memcg) { -int ret =0 ; +int ret;struct mem_cgroup *parent =parent_mem_cgroup (memcg); -if (!parent) - goto out; - memcg->kmem_account_flags = parent->kmem_account_flags; -/* -* When that happen, we need to disable the static branch only on those -* memcgs that enabled it. To achieve this, we would be forced to -* complicate the code by keeping track of which memcgs were the ones -* that actually enabled limits, and which ones got it from its -* parents. -* -* It is a lot simpler just to do static_key_slow_inc() on every child -* that is accounted. -*/ -if (!memcg_kmem_is_active (memcg)) - goto out; +if (!parent) +return 0 ; +mutex_lock (&activate_kmem_mutex);/* -* __mem_cgroup_free() will issue static_key_slow_dec() because this -* memcg is active already. If the later initialization fails then the -* cgroup core triggers the cleanup so we do not have to do it here. +* If the parent cgroup is not kmem-active now, it cannot be activated +* after this point, because it has at least one child already. */ -static_key_slow_inc (&memcg_kmem_enabled_key); - -mutex_lock (&set_limit_mutex); -memcg_stop_kmem_account (); - ret =memcg_update_cache_sizes (memcg); -memcg_resume_kmem_account (); -mutex_unlock (&set_limit_mutex); -out: +if (memcg_kmem_is_active (parent)) + ret =__memcg_activate_kmem (memcg,RES_COUNTER_MAX ); +mutex_unlock (&activate_kmem_mutex);return ret; } +#else +static int memcg_update_kmem_limit (struct mem_cgroup *memcg, +unsigned long long val) +{ +return -EINVAL ; +}#endif /* CONFIG_MEMCG_KMEM */ /*
@@ -5233,7 +5249,7 @@ static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft,else if (type == _MEMSWAP) ret =mem_cgroup_resize_memsw_limit (memcg, val);else if (type == _KMEM) - ret =memcg_update_kmem_limit (css, val); + ret =memcg_update_kmem_limit (memcg, val);else return -EINVAL ;break ;
@@ -6248,7 +6264,6 @@ mem_cgroup_css_online(struct cgroup_subsys_state *css) {struct mem_cgroup *memcg =mem_cgroup_from_css (css);struct mem_cgroup *parent =mem_cgroup_from_css (css_parent (css)); -int error =0 ;if (css->cgroup->id >MEM_CGROUP_ID_MAX )return -ENOSPC ;
@@ -6283,10 +6298,9 @@ mem_cgroup_css_online(struct cgroup_subsys_state *css)if (parent != root_mem_cgroup) mem_cgroup_subsys.broken_hierarchy = true; } - - error =memcg_init_kmem (memcg, &mem_cgroup_subsys);mutex_unlock (&memcg_create_mutex); -return error; + +return memcg_init_kmem (memcg, &mem_cgroup_subsys); }/*
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>