]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - mm/zswap.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next
[karo-tx-linux.git] / mm / zswap.c
index cabf09e0128beebdee2b8a959361fe6464fb3469..d39581a076c3aed1e9db7b2408c3f8da9ef60415 100644 (file)
@@ -76,6 +76,8 @@ static u64 zswap_duplicate_entry;
 * tunables
 **********************************/
 
+#define ZSWAP_PARAM_UNSET ""
+
 /* Enable/disable zswap (disabled by default) */
 static bool zswap_enabled;
 static int zswap_enabled_param_set(const char *,
@@ -185,6 +187,9 @@ static bool zswap_init_started;
 /* fatal error during init */
 static bool zswap_init_failed;
 
+/* init completed, but couldn't create the initial pool */
+static bool zswap_has_pool;
+
 /*********************************
 * helpers and fwd declarations
 **********************************/
@@ -366,10 +371,9 @@ static int zswap_dstmem_prepare(unsigned int cpu)
        u8 *dst;
 
        dst = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu));
-       if (!dst) {
-               pr_err("can't allocate compressor buffer\n");
+       if (!dst)
                return -ENOMEM;
-       }
+
        per_cpu(zswap_dstmem, cpu) = dst;
        return 0;
 }
@@ -424,7 +428,8 @@ static struct zswap_pool *__zswap_pool_current(void)
        struct zswap_pool *pool;
 
        pool = list_first_or_null_rcu(&zswap_pools, typeof(*pool), list);
-       WARN_ON(!pool);
+       WARN_ONCE(!pool && zswap_has_pool,
+                 "%s: no page storage pool!\n", __func__);
 
        return pool;
 }
@@ -443,7 +448,7 @@ static struct zswap_pool *zswap_pool_current_get(void)
        rcu_read_lock();
 
        pool = __zswap_pool_current();
-       if (!pool || !zswap_pool_get(pool))
+       if (!zswap_pool_get(pool))
                pool = NULL;
 
        rcu_read_unlock();
@@ -459,7 +464,9 @@ static struct zswap_pool *zswap_pool_last_get(void)
 
        list_for_each_entry_rcu(pool, &zswap_pools, list)
                last = pool;
-       if (!WARN_ON(!last) && !zswap_pool_get(last))
+       WARN_ONCE(!last && zswap_has_pool,
+                 "%s: no page storage pool!\n", __func__);
+       if (!zswap_pool_get(last))
                last = NULL;
 
        rcu_read_unlock();
@@ -495,11 +502,20 @@ static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
        gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM;
        int ret;
 
+       if (!zswap_has_pool) {
+               /* if either are unset, pool initialization failed, and we
+                * need both params to be set correctly before trying to
+                * create a pool.
+                */
+               if (!strcmp(type, ZSWAP_PARAM_UNSET))
+                       return NULL;
+               if (!strcmp(compressor, ZSWAP_PARAM_UNSET))
+                       return NULL;
+       }
+
        pool = kzalloc(sizeof(*pool), GFP_KERNEL);
-       if (!pool) {
-               pr_err("pool alloc failed\n");
+       if (!pool)
                return NULL;
-       }
 
        /* unique name for each pool specifically required by zsmalloc */
        snprintf(name, 38, "zswap%x", atomic_inc_return(&zswap_pools_count));
@@ -544,28 +560,40 @@ error:
 
 static __init struct zswap_pool *__zswap_pool_create_fallback(void)
 {
-       if (!crypto_has_comp(zswap_compressor, 0, 0)) {
-               if (!strcmp(zswap_compressor, ZSWAP_COMPRESSOR_DEFAULT)) {
-                       pr_err("default compressor %s not available\n",
-                              zswap_compressor);
-                       return NULL;
-               }
+       bool has_comp, has_zpool;
+
+       has_comp = crypto_has_comp(zswap_compressor, 0, 0);
+       if (!has_comp && strcmp(zswap_compressor, ZSWAP_COMPRESSOR_DEFAULT)) {
                pr_err("compressor %s not available, using default %s\n",
                       zswap_compressor, ZSWAP_COMPRESSOR_DEFAULT);
                param_free_charp(&zswap_compressor);
                zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT;
+               has_comp = crypto_has_comp(zswap_compressor, 0, 0);
        }
-       if (!zpool_has_pool(zswap_zpool_type)) {
-               if (!strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) {
-                       pr_err("default zpool %s not available\n",
-                              zswap_zpool_type);
-                       return NULL;
-               }
+       if (!has_comp) {
+               pr_err("default compressor %s not available\n",
+                      zswap_compressor);
+               param_free_charp(&zswap_compressor);
+               zswap_compressor = ZSWAP_PARAM_UNSET;
+       }
+
+       has_zpool = zpool_has_pool(zswap_zpool_type);
+       if (!has_zpool && strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) {
                pr_err("zpool %s not available, using default %s\n",
                       zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT);
                param_free_charp(&zswap_zpool_type);
                zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+               has_zpool = zpool_has_pool(zswap_zpool_type);
        }
+       if (!has_zpool) {
+               pr_err("default zpool %s not available\n",
+                      zswap_zpool_type);
+               param_free_charp(&zswap_zpool_type);
+               zswap_zpool_type = ZSWAP_PARAM_UNSET;
+       }
+
+       if (!has_comp || !has_zpool)
+               return NULL;
 
        return zswap_pool_create(zswap_zpool_type, zswap_compressor);
 }
@@ -582,6 +610,9 @@ static void zswap_pool_destroy(struct zswap_pool *pool)
 
 static int __must_check zswap_pool_get(struct zswap_pool *pool)
 {
+       if (!pool)
+               return 0;
+
        return kref_get_unless_zero(&pool->kref);
 }
 
@@ -639,7 +670,7 @@ static int __zswap_param_set(const char *val, const struct kernel_param *kp,
        }
 
        /* no change required */
-       if (!strcmp(s, *(char **)kp->arg))
+       if (!strcmp(s, *(char **)kp->arg) && zswap_has_pool)
                return 0;
 
        /* if this is load-time (pre-init) param setting,
@@ -670,21 +701,26 @@ static int __zswap_param_set(const char *val, const struct kernel_param *kp,
        pool = zswap_pool_find_get(type, compressor);
        if (pool) {
                zswap_pool_debug("using existing", pool);
+               WARN_ON(pool == zswap_pool_current());
                list_del_rcu(&pool->list);
-       } else {
-               spin_unlock(&zswap_pools_lock);
-               pool = zswap_pool_create(type, compressor);
-               spin_lock(&zswap_pools_lock);
        }
 
+       spin_unlock(&zswap_pools_lock);
+
+       if (!pool)
+               pool = zswap_pool_create(type, compressor);
+
        if (pool)
                ret = param_set_charp(s, kp);
        else
                ret = -EINVAL;
 
+       spin_lock(&zswap_pools_lock);
+
        if (!ret) {
                put_pool = zswap_pool_current();
                list_add_rcu(&pool->list, &zswap_pools);
+               zswap_has_pool = true;
        } else if (pool) {
                /* add the possibly pre-existing pool to the end of the pools
                 * list; if it's new (and empty) then it'll be removed and
@@ -696,6 +732,17 @@ static int __zswap_param_set(const char *val, const struct kernel_param *kp,
 
        spin_unlock(&zswap_pools_lock);
 
+       if (!zswap_has_pool && !pool) {
+               /* if initial pool creation failed, and this pool creation also
+                * failed, maybe both compressor and zpool params were bad.
+                * Allow changing this param, so pool creation will succeed
+                * when the other param is changed. We already verified this
+                * param is ok in the zpool_has_pool() or crypto_has_comp()
+                * checks above.
+                */
+               ret = param_set_charp(s, kp);
+       }
+
        /* drop the ref from either the old current pool,
         * or the new pool we failed to add
         */
@@ -724,6 +771,10 @@ static int zswap_enabled_param_set(const char *val,
                pr_err("can't enable, initialization failed\n");
                return -ENODEV;
        }
+       if (!zswap_has_pool && zswap_init_started) {
+               pr_err("can't enable, no pool configured\n");
+               return -ENODEV;
+       }
 
        return param_set_bool(val, kp);
 }
@@ -1104,7 +1155,7 @@ static void zswap_frontswap_init(unsigned type)
 {
        struct zswap_tree *tree;
 
-       tree = kzalloc(sizeof(struct zswap_tree), GFP_KERNEL);
+       tree = kzalloc(sizeof(*tree), GFP_KERNEL);
        if (!tree) {
                pr_err("alloc failed, zswap disabled for swap type %d\n", type);
                return;
@@ -1205,22 +1256,21 @@ static int __init init_zswap(void)
                goto hp_fail;
 
        pool = __zswap_pool_create_fallback();
-       if (!pool) {
+       if (pool) {
+               pr_info("loaded using pool %s/%s\n", pool->tfm_name,
+                       zpool_get_type(pool->zpool));
+               list_add(&pool->list, &zswap_pools);
+               zswap_has_pool = true;
+       } else {
                pr_err("pool creation failed\n");
-               goto pool_fail;
+               zswap_enabled = false;
        }
-       pr_info("loaded using pool %s/%s\n", pool->tfm_name,
-               zpool_get_type(pool->zpool));
-
-       list_add(&pool->list, &zswap_pools);
 
        frontswap_register_ops(&zswap_frontswap_ops);
        if (zswap_debugfs_init())
                pr_warn("debugfs initialization failed\n");
        return 0;
 
-pool_fail:
-       cpuhp_remove_state_nocalls(CPUHP_MM_ZSWP_POOL_PREPARE);
 hp_fail:
        cpuhp_remove_state(CPUHP_MM_ZSWP_MEM_PREPARE);
 dstmem_fail: