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(¶m, sizeof(param)); - if (IS_ERR(inst)) - inst = NULL; - else if (crypto_register_instance(tmpl, inst)) { - tmpl->free(inst); - inst = NULL; - } + do { + inst = tmpl->alloc(¶m, 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
