list_lru: organize all list_lrus to list
authorVladimir Davydov <vdavydov@parallels.com>
Thu, 12 Feb 2015 22:59:07 +0000 (14:59 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Feb 2015 02:54:09 +0000 (18:54 -0800)
To make list_lru memcg aware, we need all list_lrus to be kept on a list
protected by a mutex, so that we could sleep while walking over the
list.

Therefore after this change list_lru_destroy may sleep.  Fortunately,
there is only one user that calls it from an atomic context - it's
put_super - and we can easily fix it by calling list_lru_destroy before
put_super in destroy_locked_super - anyway we don't longer need lrus by
that time.

Another point that should be noted is that list_lru_destroy is allowed
to be called on an uninitialized zeroed-out object, in which case it is
a no-op.  Before this patch this was guaranteed by kfree, but now we
need an explicit check there.

Signed-off-by: Vladimir Davydov <vdavydov@parallels.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Greg Thelen <gthelen@google.com>
Cc: Glauber Costa <glommer@gmail.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.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>
fs/super.c
include/linux/list_lru.h
mm/list_lru.c

index a2b735a42e74d7eb5aaa88b854714590ff636629..b027849d92d22cddc8aae0a2289b9262879bac36 100644 (file)
@@ -282,6 +282,14 @@ void deactivate_locked_super(struct super_block *s)
                unregister_shrinker(&s->s_shrink);
                fs->kill_sb(s);
 
+               /*
+                * Since list_lru_destroy() may sleep, we cannot call it from
+                * put_super(), where we hold the sb_lock. Therefore we destroy
+                * the lru lists right now.
+                */
+               list_lru_destroy(&s->s_dentry_lru);
+               list_lru_destroy(&s->s_inode_lru);
+
                put_filesystem(fs);
                put_super(s);
        } else {
index 53c1d6b78270ab4aa6578773c582d77fef035428..ee9486ac06214ed6f3f4c070be414b086b2e15db 100644 (file)
@@ -31,6 +31,9 @@ struct list_lru_node {
 
 struct list_lru {
        struct list_lru_node    *node;
+#ifdef CONFIG_MEMCG_KMEM
+       struct list_head        list;
+#endif
 };
 
 void list_lru_destroy(struct list_lru *lru);
index 07e198c778888f91f12625aaa44de5735f15bb70..a9021cb3ccdebe7204ef3207106ba33a1b17e145 100644 (file)
@@ -9,6 +9,34 @@
 #include <linux/mm.h>
 #include <linux/list_lru.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
+
+#ifdef CONFIG_MEMCG_KMEM
+static LIST_HEAD(list_lrus);
+static DEFINE_MUTEX(list_lrus_mutex);
+
+static void list_lru_register(struct list_lru *lru)
+{
+       mutex_lock(&list_lrus_mutex);
+       list_add(&lru->list, &list_lrus);
+       mutex_unlock(&list_lrus_mutex);
+}
+
+static void list_lru_unregister(struct list_lru *lru)
+{
+       mutex_lock(&list_lrus_mutex);
+       list_del(&lru->list);
+       mutex_unlock(&list_lrus_mutex);
+}
+#else
+static void list_lru_register(struct list_lru *lru)
+{
+}
+
+static void list_lru_unregister(struct list_lru *lru)
+{
+}
+#endif /* CONFIG_MEMCG_KMEM */
 
 bool list_lru_add(struct list_lru *lru, struct list_head *item)
 {
@@ -137,12 +165,18 @@ int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key)
                INIT_LIST_HEAD(&lru->node[i].list);
                lru->node[i].nr_items = 0;
        }
+       list_lru_register(lru);
        return 0;
 }
 EXPORT_SYMBOL_GPL(list_lru_init_key);
 
 void list_lru_destroy(struct list_lru *lru)
 {
+       /* Already destroyed or not yet initialized? */
+       if (!lru->node)
+               return;
+       list_lru_unregister(lru);
        kfree(lru->node);
+       lru->node = NULL;
 }
 EXPORT_SYMBOL_GPL(list_lru_destroy);