diff options
-rw-r--r-- | mm/vmscan.c | 43 |
1 files changed, 21 insertions, 22 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c index 8fcc86f1d7bc..4375b1e9bd56 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -170,6 +170,20 @@ static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); #ifdef CONFIG_MEMCG_KMEM + +/* + * We allow subsystems to populate their shrinker-related + * LRU lists before register_shrinker_prepared() is called + * for the shrinker, since we don't want to impose + * restrictions on their internal registration order. + * In this case shrink_slab_memcg() may find corresponding + * bit is set in the shrinkers map. + * + * This value is used by the function to detect registering + * shrinkers and to skip do_shrink_slab() calls for them. + */ +#define SHRINKER_REGISTERING ((struct shrinker *)~0UL) + static DEFINE_IDR(shrinker_idr); static int shrinker_nr_max; @@ -179,7 +193,7 @@ static int prealloc_memcg_shrinker(struct shrinker *shrinker) down_write(&shrinker_rwsem); /* This may call shrinker, so it must use down_read_trylock() */ - id = idr_alloc(&shrinker_idr, shrinker, 0, 0, GFP_KERNEL); + id = idr_alloc(&shrinker_idr, SHRINKER_REGISTERING, 0, 0, GFP_KERNEL); if (id < 0) goto unlock; @@ -364,21 +378,6 @@ int prealloc_shrinker(struct shrinker *shrinker) if (!shrinker->nr_deferred) return -ENOMEM; - /* - * There is a window between prealloc_shrinker() - * and register_shrinker_prepared(). We don't want - * to clear bit of a shrinker in such the state - * in shrink_slab_memcg(), since this will impose - * restrictions on a code registering a shrinker - * (they would have to guarantee, their LRU lists - * are empty till shrinker is completely registered). - * So, we differ the situation, when 1)a shrinker - * is semi-registered (id is assigned, but it has - * not yet linked to shrinker_list) and 2)shrinker - * is not registered (id is not assigned). - */ - INIT_LIST_HEAD(&shrinker->list); - if (shrinker->flags & SHRINKER_MEMCG_AWARE) { if (prealloc_memcg_shrinker(shrinker)) goto free_deferred; @@ -408,6 +407,9 @@ void register_shrinker_prepared(struct shrinker *shrinker) { down_write(&shrinker_rwsem); list_add_tail(&shrinker->list, &shrinker_list); +#ifdef CONFIG_MEMCG_KMEM + idr_replace(&shrinker_idr, shrinker, shrinker->id); +#endif up_write(&shrinker_rwsem); } @@ -589,15 +591,12 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, struct shrinker *shrinker; shrinker = idr_find(&shrinker_idr, i); - if (unlikely(!shrinker)) { - clear_bit(i, map->map); + if (unlikely(!shrinker || shrinker == SHRINKER_REGISTERING)) { + if (!shrinker) + clear_bit(i, map->map); continue; } - /* See comment in prealloc_shrinker() */ - if (unlikely(list_empty(&shrinker->list))) - continue; - ret = do_shrink_slab(&sc, shrinker, priority); if (ret == SHRINK_EMPTY) { clear_bit(i, map->map); |