Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

list_lru: organize all list_lrus to list

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>

authored by

Vladimir Davydov and committed by
Linus Torvalds
c0a5b560 ff0b67ef

+45
+8
fs/super.c
··· 282 282 unregister_shrinker(&s->s_shrink); 283 283 fs->kill_sb(s); 284 284 285 + /* 286 + * Since list_lru_destroy() may sleep, we cannot call it from 287 + * put_super(), where we hold the sb_lock. Therefore we destroy 288 + * the lru lists right now. 289 + */ 290 + list_lru_destroy(&s->s_dentry_lru); 291 + list_lru_destroy(&s->s_inode_lru); 292 + 285 293 put_filesystem(fs); 286 294 put_super(s); 287 295 } else {
+3
include/linux/list_lru.h
··· 31 31 32 32 struct list_lru { 33 33 struct list_lru_node *node; 34 + #ifdef CONFIG_MEMCG_KMEM 35 + struct list_head list; 36 + #endif 34 37 }; 35 38 36 39 void list_lru_destroy(struct list_lru *lru);
+34
mm/list_lru.c
··· 9 9 #include <linux/mm.h> 10 10 #include <linux/list_lru.h> 11 11 #include <linux/slab.h> 12 + #include <linux/mutex.h> 13 + 14 + #ifdef CONFIG_MEMCG_KMEM 15 + static LIST_HEAD(list_lrus); 16 + static DEFINE_MUTEX(list_lrus_mutex); 17 + 18 + static void list_lru_register(struct list_lru *lru) 19 + { 20 + mutex_lock(&list_lrus_mutex); 21 + list_add(&lru->list, &list_lrus); 22 + mutex_unlock(&list_lrus_mutex); 23 + } 24 + 25 + static void list_lru_unregister(struct list_lru *lru) 26 + { 27 + mutex_lock(&list_lrus_mutex); 28 + list_del(&lru->list); 29 + mutex_unlock(&list_lrus_mutex); 30 + } 31 + #else 32 + static void list_lru_register(struct list_lru *lru) 33 + { 34 + } 35 + 36 + static void list_lru_unregister(struct list_lru *lru) 37 + { 38 + } 39 + #endif /* CONFIG_MEMCG_KMEM */ 12 40 13 41 bool list_lru_add(struct list_lru *lru, struct list_head *item) 14 42 { ··· 165 137 INIT_LIST_HEAD(&lru->node[i].list); 166 138 lru->node[i].nr_items = 0; 167 139 } 140 + list_lru_register(lru); 168 141 return 0; 169 142 } 170 143 EXPORT_SYMBOL_GPL(list_lru_init_key); 171 144 172 145 void list_lru_destroy(struct list_lru *lru) 173 146 { 147 + /* Already destroyed or not yet initialized? */ 148 + if (!lru->node) 149 + return; 150 + list_lru_unregister(lru); 174 151 kfree(lru->node); 152 + lru->node = NULL; 175 153 } 176 154 EXPORT_SYMBOL_GPL(list_lru_destroy);