Hi:

[CRYPTO] api: Retry when modules are removed

The module loading/unloading semantics here are such that as long as
you load a replacement before unloading a module, e.g.,

modprobe md5-new
rmmod -w md5

no operations on md5 should fail.

It turns out that there are a few cases where this assumption fails
for crypto instances.  This patch addresses these cases by letting
the API retry the operations if it fails because an underlying module
has been removed.  If no replacement module is loaded, then the retry
will terminate.

More specifically, each time a crypto_mod_get fails we mark the affected
algorithm and the instances leading to this as dying.  Dying algorithms
are skipped by all further lookups.  So eventually all algorithms related
to an unloading module will be marked as dying which terminates the retry
process.

At this point a new larval would be created which will succeed if a
replacement module is available.

Signed-off-by: Herbert Xu <[EMAIL PROTECTED]>

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[EMAIL PROTECTED]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
diff --git a/crypto/api.c b/crypto/api.c
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -26,6 +26,7 @@
 #include <linux/notifier.h>
 #include <linux/param.h>
 #include <linux/rwsem.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include "internal.h"
@@ -70,6 +71,11 @@ static inline int crypto_is_dead(struct 
        return alg->cra_flags & CRYPTO_ALG_DEAD;
 }
 
+static inline int crypto_is_moribund(struct crypto_alg *alg)
+{
+       return alg->cra_flags & (CRYPTO_ALG_DEAD | CRYPTO_ALG_DYING);
+}
+
 static inline int crypto_notify(unsigned long val, void *v)
 {
        return blocking_notifier_call_chain(&crypto_chain, val, v);
@@ -84,7 +90,7 @@ static struct crypto_alg *__crypto_alg_l
        list_for_each_entry(q, &crypto_alg_list, cra_list) {
                int exact, fuzzy;
 
-               if (crypto_is_dead(q))
+               if (crypto_is_moribund(q))
                        continue;
 
                if ((q->cra_flags ^ type) & mask)
@@ -132,7 +138,7 @@ static struct crypto_alg *crypto_larval_
 
        larval = kzalloc(sizeof(*larval), GFP_KERNEL);
        if (!larval)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        larval->mask = mask;
        larval->alg.cra_flags = CRYPTO_ALG_LARVAL | type;
@@ -174,8 +180,11 @@ static struct crypto_alg *crypto_larval_
 
        wait_for_completion_interruptible_timeout(&larval->completion, 60 * HZ);
        alg = larval->adult;
-       if (alg && !crypto_mod_get(alg))
-               alg = NULL;
+       if (alg) {
+               if (!crypto_mod_get(alg))
+                       alg = ERR_PTR(-EAGAIN);
+       } else
+               alg = ERR_PTR(-ENOENT);
        crypto_mod_put(&larval->alg);
 
        return alg;
@@ -204,9 +213,6 @@ static struct crypto_alg *crypto_alg_loo
 {
        struct crypto_alg *alg;
 
-       if (!name)
-               return NULL;
-
        down_read(&crypto_alg_sem);
        alg = __crypto_alg_lookup(name, type, mask);
        up_read(&crypto_alg_sem);
@@ -220,6 +226,9 @@ struct crypto_alg *crypto_alg_mod_lookup
        struct crypto_alg *larval;
        int ok;
 
+       if (!name)
+               return ERR_PTR(-ENOENT);
+
        mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD);
        type &= mask;
 
@@ -229,7 +238,7 @@ struct crypto_alg *crypto_alg_mod_lookup
                return crypto_is_larval(alg) ? crypto_larval_wait(alg) : alg;
 
        larval = crypto_larval_alloc(name, type, mask);
-       if (!larval || !crypto_is_larval(larval))
+       if (IS_ERR(larval) || !crypto_is_larval(larval))
                return larval;
 
        ok = crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval);
@@ -242,7 +251,7 @@ struct crypto_alg *crypto_alg_mod_lookup
                alg = crypto_larval_wait(larval);
        else {
                crypto_mod_put(larval);
-               alg = NULL;
+               alg = ERR_PTR(-ENOENT);
        }
        crypto_larval_kill(larval);
        return alg;
@@ -337,10 +346,18 @@ static unsigned int crypto_ctxsize(struc
        return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
 }
 
+static void crypto_shoot_alg(struct crypto_alg *alg)
+{
+       down_write(&crypto_alg_sem);
+       alg->cra_flags |= CRYPTO_ALG_DYING;
+       up_write(&crypto_alg_sem);
+}
+
 static struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 flags)
 {
        struct crypto_tfm *tfm = NULL;
        unsigned int tfm_size;
+       int err = -ENOMEM;
 
        tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
        tfm = kzalloc(tfm_size, GFP_KERNEL);
@@ -348,15 +365,20 @@ static struct crypto_tfm *__crypto_alloc
                goto out;
 
        tfm->__crt_alg = alg;
-       
-       if (crypto_init_flags(tfm, flags))
+
+       err = crypto_init_flags(tfm, flags);
+       if (err)
                goto out_free_tfm;
                
-       if (crypto_init_ops(tfm))
+       err = crypto_init_ops(tfm);
+       if (err)
                goto out_free_tfm;
 
-       if (alg->cra_init && alg->cra_init(tfm))
+       if (alg->cra_init && (err = alg->cra_init(tfm))) {
+               if (err == -EAGAIN)
+                       crypto_shoot_alg(alg);
                goto cra_init_failed;
+       }
 
        goto out;
 
@@ -364,7 +386,7 @@ cra_init_failed:
        crypto_exit_ops(tfm);
 out_free_tfm:
        kfree(tfm);
-       tfm = NULL;
+       tfm = ERR_PTR(err);
 out:
        return tfm;
 }
@@ -372,17 +394,25 @@ out:
 struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
 {
        struct crypto_tfm *tfm = NULL;
-       struct crypto_alg *alg;
+       int err;
 
-       alg = crypto_alg_mod_lookup(name, 0, 0);
-       if (!alg)
-               goto out;
+       do {
+               struct crypto_alg *alg;
 
-       tfm = __crypto_alloc_tfm(alg, flags);
-       if (!tfm)
-               crypto_mod_put(alg);
+               alg = crypto_alg_mod_lookup(name, 0, 0);
+               err = PTR_ERR(alg);
+               if (IS_ERR(alg))
+                       continue;
+
+               tfm = __crypto_alloc_tfm(alg, flags);
+               err = 0;
+               if (IS_ERR(tfm)) {
+                       crypto_mod_put(alg);
+                       err = PTR_ERR(tfm);
+                       tfm = NULL;
+               }
+       } while (err == -EAGAIN && !signal_pending(current));
 
-out:
        return tfm;
 }
 
@@ -442,7 +472,7 @@ static int crypto_check_alg(struct crypt
 static int __crypto_register_alg(struct crypto_alg *alg)
 {
        struct crypto_alg *q;
-       int ret = -ENOENT;
+       int ret = -EAGAIN;
 
        if (crypto_is_dead(alg))
                goto out;
@@ -682,12 +712,12 @@ EXPORT_SYMBOL_GPL(crypto_register_instan
 int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg,
                      struct crypto_instance *inst)
 {
-       int err = -ENOENT;
+       int err = -EAGAIN;
 
        spawn->inst = inst;
 
        down_write(&crypto_alg_sem);
-       if (!crypto_is_dead(alg)) {
+       if (!crypto_is_moribund(alg)) {
                list_add(&spawn->list, &alg->cra_users);
                spawn->alg = alg;
                err = 0;
@@ -709,22 +739,25 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn);
 struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn)
 {
        struct crypto_alg *alg;
+       struct crypto_alg *alg2;
        struct crypto_tfm *tfm;
 
        down_read(&crypto_alg_sem);
        alg = spawn->alg;
-       if (alg && !crypto_mod_get(alg))
-               alg = NULL;
+       alg2 = alg;
+       if (alg2)
+               alg2 = crypto_mod_get(alg2);
        up_read(&crypto_alg_sem);
 
-       if (!alg)
-               return ERR_PTR(-ENOENT);
+       if (!alg2) {
+               if (alg)
+                       crypto_shoot_alg(alg);
+               return ERR_PTR(-EAGAIN);
+       }
 
        tfm = __crypto_alloc_tfm(alg, 0);
-       if (!tfm) {
+       if (IS_ERR(tfm))
                crypto_mod_put(alg);
-               tfm = ERR_PTR(-ENOMEM);
-       }
 
        return tfm;
 }
@@ -735,7 +768,7 @@ int crypto_alg_available(const char *nam
        int ret = 0;
        struct crypto_alg *alg = crypto_alg_mod_lookup(name, 0, 0);
        
-       if (alg) {
+       if (!IS_ERR(alg)) {
                crypto_mod_put(alg);
                ret = 1;
        }
diff --git a/crypto/cryptomgr.c b/crypto/cryptomgr.c
--- a/crypto/cryptomgr.c
+++ b/crypto/cryptomgr.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/notifier.h>
 #include <linux/rtnetlink.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 
 #include "internal.h"
@@ -36,6 +37,7 @@ static int cryptomgr_probe(struct crypto
        const char *name = larval->alg.cra_name;
        const char *p;
        unsigned int len;
+       int err;
 
        for (p = name; isalnum(*p) || *p == '-' || *p == '_'; p++)
                ;
@@ -64,17 +66,17 @@ static int cryptomgr_probe(struct crypto
        memcpy(param.alg.data.name, name, len);
        param.alg.data.name[len] = 0;
 
-       inst = tmpl->alloc(&param, sizeof(param));
-       if (IS_ERR(inst))
-               inst = NULL;
-       else if (crypto_register_instance(tmpl, inst)) {
-               tmpl->free(inst);
-               inst = NULL;
-       }
+       do {
+               inst = tmpl->alloc(&param, sizeof(param));
+               if (IS_ERR(inst))
+                       err = PTR_ERR(inst);
+               else if ((err = crypto_register_instance(tmpl, inst)))
+                       tmpl->free(inst);
+       } while (err == -EAGAIN && !signal_pending(current));
 
        crypto_tmpl_put(tmpl);
 
-       if (!inst)
+       if (err)
                goto err;
 
        return NOTIFY_STOP;
diff --git a/crypto/hmac.c b/crypto/hmac.c
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -154,10 +154,10 @@ static struct crypto_instance *crypto_hm
 
        inst->alg.cra_flags = CRYPTO_ALG_TYPE_DIGEST;
 
-       err = -ENOENT;
        alg = crypto_alg_mod_lookup(alga->name, CRYPTO_ALG_TYPE_DIGEST,
                                    CRYPTO_ALG_TYPE_MASK);
-       if (!alg)
+       err = PTR_ERR(alg);
+       if (IS_ERR(alg))
                goto err_free_inst;
 
        err = -ENAMETOOLONG;
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -35,6 +35,7 @@
 
 #define CRYPTO_ALG_LARVAL              0x00000010
 #define CRYPTO_ALG_DEAD                        0x00000020
+#define CRYPTO_ALG_DYING               0x00000040
 
 /*
  * Transform masks and values (for crt_flags).
-
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to