|
@@ -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);
|