|
@@ -246,6 +246,14 @@ struct zs_pool {
|
|
|
atomic_long_t pages_allocated;
|
|
|
|
|
|
struct zs_pool_stats stats;
|
|
|
+
|
|
|
+ /* Compact classes */
|
|
|
+ struct shrinker shrinker;
|
|
|
+ /*
|
|
|
+ * To signify that register_shrinker() was successful
|
|
|
+ * and unregister_shrinker() will not Oops.
|
|
|
+ */
|
|
|
+ bool shrinker_enabled;
|
|
|
#ifdef CONFIG_ZSMALLOC_STAT
|
|
|
struct dentry *stat_dentry;
|
|
|
#endif
|
|
@@ -1778,6 +1786,69 @@ void zs_pool_stats(struct zs_pool *pool, struct zs_pool_stats *stats)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(zs_pool_stats);
|
|
|
|
|
|
+static unsigned long zs_shrinker_scan(struct shrinker *shrinker,
|
|
|
+ struct shrink_control *sc)
|
|
|
+{
|
|
|
+ unsigned long pages_freed;
|
|
|
+ struct zs_pool *pool = container_of(shrinker, struct zs_pool,
|
|
|
+ shrinker);
|
|
|
+
|
|
|
+ pages_freed = pool->stats.pages_compacted;
|
|
|
+ /*
|
|
|
+ * Compact classes and calculate compaction delta.
|
|
|
+ * Can run concurrently with a manually triggered
|
|
|
+ * (by user) compaction.
|
|
|
+ */
|
|
|
+ pages_freed = zs_compact(pool) - pages_freed;
|
|
|
+
|
|
|
+ return pages_freed ? pages_freed : SHRINK_STOP;
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long zs_shrinker_count(struct shrinker *shrinker,
|
|
|
+ struct shrink_control *sc)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct size_class *class;
|
|
|
+ unsigned long pages_to_free = 0;
|
|
|
+ struct zs_pool *pool = container_of(shrinker, struct zs_pool,
|
|
|
+ shrinker);
|
|
|
+
|
|
|
+ if (!pool->shrinker_enabled)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = zs_size_classes - 1; i >= 0; i--) {
|
|
|
+ class = pool->size_class[i];
|
|
|
+ if (!class)
|
|
|
+ continue;
|
|
|
+ if (class->index != i)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ spin_lock(&class->lock);
|
|
|
+ pages_to_free += zs_can_compact(class);
|
|
|
+ spin_unlock(&class->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ return pages_to_free;
|
|
|
+}
|
|
|
+
|
|
|
+static void zs_unregister_shrinker(struct zs_pool *pool)
|
|
|
+{
|
|
|
+ if (pool->shrinker_enabled) {
|
|
|
+ unregister_shrinker(&pool->shrinker);
|
|
|
+ pool->shrinker_enabled = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int zs_register_shrinker(struct zs_pool *pool)
|
|
|
+{
|
|
|
+ pool->shrinker.scan_objects = zs_shrinker_scan;
|
|
|
+ pool->shrinker.count_objects = zs_shrinker_count;
|
|
|
+ pool->shrinker.batch = 0;
|
|
|
+ pool->shrinker.seeks = DEFAULT_SEEKS;
|
|
|
+
|
|
|
+ return register_shrinker(&pool->shrinker);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* zs_create_pool - Creates an allocation pool to work from.
|
|
|
* @flags: allocation flags used to allocate pool metadata
|
|
@@ -1863,6 +1934,12 @@ struct zs_pool *zs_create_pool(char *name, gfp_t flags)
|
|
|
if (zs_pool_stat_create(name, pool))
|
|
|
goto err;
|
|
|
|
|
|
+ /*
|
|
|
+ * Not critical, we still can use the pool
|
|
|
+ * and user can trigger compaction manually.
|
|
|
+ */
|
|
|
+ if (zs_register_shrinker(pool) == 0)
|
|
|
+ pool->shrinker_enabled = true;
|
|
|
return pool;
|
|
|
|
|
|
err:
|
|
@@ -1875,6 +1952,7 @@ void zs_destroy_pool(struct zs_pool *pool)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
+ zs_unregister_shrinker(pool);
|
|
|
zs_pool_stat_destroy(pool);
|
|
|
|
|
|
for (i = 0; i < zs_size_classes; i++) {
|