|
@@ -169,6 +169,50 @@ unsigned long vm_total_pages;
|
|
|
static LIST_HEAD(shrinker_list);
|
|
|
static DECLARE_RWSEM(shrinker_rwsem);
|
|
|
|
|
|
+#ifdef CONFIG_MEMCG_KMEM
|
|
|
+static DEFINE_IDR(shrinker_idr);
|
|
|
+static int shrinker_nr_max;
|
|
|
+
|
|
|
+static int prealloc_memcg_shrinker(struct shrinker *shrinker)
|
|
|
+{
|
|
|
+ int id, ret = -ENOMEM;
|
|
|
+
|
|
|
+ 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);
|
|
|
+ if (id < 0)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ if (id >= shrinker_nr_max)
|
|
|
+ shrinker_nr_max = id + 1;
|
|
|
+ shrinker->id = id;
|
|
|
+ ret = 0;
|
|
|
+unlock:
|
|
|
+ up_write(&shrinker_rwsem);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void unregister_memcg_shrinker(struct shrinker *shrinker)
|
|
|
+{
|
|
|
+ int id = shrinker->id;
|
|
|
+
|
|
|
+ BUG_ON(id < 0);
|
|
|
+
|
|
|
+ down_write(&shrinker_rwsem);
|
|
|
+ idr_remove(&shrinker_idr, id);
|
|
|
+ up_write(&shrinker_rwsem);
|
|
|
+}
|
|
|
+#else /* CONFIG_MEMCG_KMEM */
|
|
|
+static int prealloc_memcg_shrinker(struct shrinker *shrinker)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void unregister_memcg_shrinker(struct shrinker *shrinker)
|
|
|
+{
|
|
|
+}
|
|
|
+#endif /* CONFIG_MEMCG_KMEM */
|
|
|
+
|
|
|
#ifdef CONFIG_MEMCG
|
|
|
static bool global_reclaim(struct scan_control *sc)
|
|
|
{
|
|
@@ -313,11 +357,28 @@ int prealloc_shrinker(struct shrinker *shrinker)
|
|
|
shrinker->nr_deferred = kzalloc(size, GFP_KERNEL);
|
|
|
if (!shrinker->nr_deferred)
|
|
|
return -ENOMEM;
|
|
|
+
|
|
|
+ if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
|
|
|
+ if (prealloc_memcg_shrinker(shrinker))
|
|
|
+ goto free_deferred;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
+
|
|
|
+free_deferred:
|
|
|
+ kfree(shrinker->nr_deferred);
|
|
|
+ shrinker->nr_deferred = NULL;
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
void free_prealloced_shrinker(struct shrinker *shrinker)
|
|
|
{
|
|
|
+ if (!shrinker->nr_deferred)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (shrinker->flags & SHRINKER_MEMCG_AWARE)
|
|
|
+ unregister_memcg_shrinker(shrinker);
|
|
|
+
|
|
|
kfree(shrinker->nr_deferred);
|
|
|
shrinker->nr_deferred = NULL;
|
|
|
}
|
|
@@ -347,6 +408,8 @@ void unregister_shrinker(struct shrinker *shrinker)
|
|
|
{
|
|
|
if (!shrinker->nr_deferred)
|
|
|
return;
|
|
|
+ if (shrinker->flags & SHRINKER_MEMCG_AWARE)
|
|
|
+ unregister_memcg_shrinker(shrinker);
|
|
|
down_write(&shrinker_rwsem);
|
|
|
list_del(&shrinker->list);
|
|
|
up_write(&shrinker_rwsem);
|