Hi: [CRYPTO] api: Added spawns
Spawns lock a specific crypto algorithm in place. They can then be used with crypto_spawn_tfm to allocate a tfm for that algorithm. When the base algorithm of a spawn is deregistered, all its spawns will be automatically removed. 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 @@ -65,6 +65,11 @@ static inline int crypto_is_larval(struc return alg->cra_flags & CRYPTO_ALG_LARVAL; } +static inline int crypto_is_dead(struct crypto_alg *alg) +{ + return alg->cra_flags & CRYPTO_ALG_DEAD; +} + static inline int crypto_notify(unsigned long val, void *v) { return blocking_notifier_call_chain(&crypto_chain, val, v); @@ -79,6 +84,9 @@ 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)) + continue; + if ((q->cra_flags ^ type) & mask) continue; @@ -212,7 +220,7 @@ struct crypto_alg *crypto_alg_mod_lookup struct crypto_alg *larval; int ok; - mask &= ~CRYPTO_ALG_LARVAL; + mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD); type &= mask; alg = try_then_request_module(crypto_alg_lookup(name, type, mask), @@ -329,20 +337,15 @@ static unsigned int crypto_ctxsize(struc return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1)); } -struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) +static struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 flags) { struct crypto_tfm *tfm = NULL; - struct crypto_alg *alg; unsigned int tfm_size; - alg = crypto_alg_mod_lookup(name, 0, 0); - if (alg == NULL) - goto out; - tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags); tfm = kzalloc(tfm_size, GFP_KERNEL); if (tfm == NULL) - goto out_put; + goto out; tfm->__crt_alg = alg; @@ -362,8 +365,23 @@ cra_init_failed: out_free_tfm: kfree(tfm); tfm = NULL; -out_put: - crypto_mod_put(alg); +out: + return tfm; +} + +struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags) +{ + struct crypto_tfm *tfm = NULL; + struct crypto_alg *alg; + + alg = crypto_alg_mod_lookup(name, 0, 0); + if (!alg) + goto out; + + tfm = __crypto_alloc_tfm(alg, flags); + if (!tfm) + crypto_mod_put(alg); + out: return tfm; } @@ -424,8 +442,14 @@ static int crypto_check_alg(struct crypt static int __crypto_register_alg(struct crypto_alg *alg) { struct crypto_alg *q; - int ret = -EEXIST; + int ret = -ENOENT; + + if (crypto_is_dead(alg)) + goto out; + + INIT_LIST_HEAD(&alg->cra_users); + ret = -EEXIST; atomic_set(&alg->cra_refcnt, 1); list_for_each_entry(q, &crypto_alg_list, cra_list) { if (q == alg) @@ -468,16 +492,71 @@ int crypto_register_alg(struct crypto_al return err; } +static void crypto_destroy_instance(struct crypto_alg *alg) +{ + struct crypto_instance *inst = (void *)alg; + struct crypto_template *tmpl = inst->tmpl; + + tmpl->free(inst); + crypto_tmpl_put(tmpl); +} + +static int crypto_remove_alg(struct crypto_alg *alg, struct list_head *list) +{ + struct crypto_spawn *spawn, *n; + LIST_HEAD(spawns); + + if (unlikely(list_empty(&alg->cra_list))) + return -ENOENT; + + alg->cra_flags |= CRYPTO_ALG_DEAD; + + crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg); + list_del_init(&alg->cra_list); + list_splice(&alg->cra_users, &spawns); + + list_for_each_entry_safe(spawn, n, &spawns, list) { + struct crypto_instance *inst = spawn->inst; + struct crypto_template *tmpl = inst->tmpl; + + list_del_init(&spawn->list); + spawn->alg = NULL; + + if (crypto_is_dead(&inst->alg)) + continue; + + inst->alg.cra_flags |= CRYPTO_ALG_DEAD; + if (!tmpl || !crypto_tmpl_get(tmpl)) + continue; + + crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg); + list_move(&inst->alg.cra_list, list); + hlist_del(&inst->list); + list_splice(&inst->alg.cra_users, spawns.prev); + inst->alg.cra_destroy = crypto_destroy_instance; + } + + return 0; +} + +static void crypto_remove_final(struct list_head *list) +{ + struct crypto_alg *alg; + struct crypto_alg *n; + + list_for_each_entry_safe(alg, n, list, cra_list) { + list_del_init(&alg->cra_list); + crypto_alg_put(alg); + } +} + int crypto_unregister_alg(struct crypto_alg *alg) { - int ret = -ENOENT; + int ret; + LIST_HEAD(list); down_write(&crypto_alg_sem); - if (likely(!list_empty(&alg->cra_list))) { - list_del_init(&alg->cra_list); - ret = 0; - } - crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg); + ret = crypto_remove_alg(alg, &list); up_write(&crypto_alg_sem); if (ret) @@ -487,6 +566,7 @@ int crypto_unregister_alg(struct crypto_ if (alg->cra_destroy) alg->cra_destroy(alg); + crypto_remove_final(&list); return 0; } @@ -516,6 +596,7 @@ void crypto_unregister_template(struct c struct crypto_instance *inst; struct hlist_node *p, *n; struct hlist_head *list; + LIST_HEAD(users); down_write(&crypto_alg_sem); @@ -524,9 +605,8 @@ void crypto_unregister_template(struct c list = &tmpl->instances; hlist_for_each_entry(inst, p, list, list) { - BUG_ON(list_empty(&inst->alg.cra_list)); - list_del_init(&inst->alg.cra_list); - crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg); + int err = crypto_remove_alg(&inst->alg, &users); + BUG_ON(err); } crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl); @@ -537,6 +617,7 @@ void crypto_unregister_template(struct c BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1); tmpl->free(inst); } + crypto_remove_final(&users); } EXPORT_SYMBOL_GPL(crypto_unregister_template); @@ -596,6 +677,57 @@ err: } EXPORT_SYMBOL_GPL(crypto_register_instance); +int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, + struct crypto_instance *inst) +{ + int err = -ENOENT; + + spawn->inst = inst; + + down_write(&crypto_alg_sem); + if (!crypto_is_dead(alg)) { + list_add(&spawn->list, &alg->cra_users); + spawn->alg = alg; + err = 0; + } + up_write(&crypto_alg_sem); + + return err; +} +EXPORT_SYMBOL_GPL(crypto_init_spawn); + +void crypto_drop_spawn(struct crypto_spawn *spawn) +{ + down_write(&crypto_alg_sem); + list_del(&spawn->list); + up_write(&crypto_alg_sem); +} +EXPORT_SYMBOL_GPL(crypto_drop_spawn); + +struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn) +{ + struct crypto_alg *alg; + struct crypto_tfm *tfm; + + down_read(&crypto_alg_sem); + alg = spawn->alg; + if (alg && !crypto_mod_get(alg)) + alg = NULL; + up_read(&crypto_alg_sem); + + if (!alg) + return ERR_PTR(-ENOENT); + + tfm = __crypto_alloc_tfm(alg, 0); + if (!tfm) { + crypto_mod_put(alg); + tfm = ERR_PTR(-ENOMEM); + } + + return tfm; +} +EXPORT_SYMBOL_GPL(crypto_spawn_tfm); + int crypto_alg_available(const char *name, u32 flags) { int ret = 0; diff --git a/crypto/internal.h b/crypto/internal.h --- a/crypto/internal.h +++ b/crypto/internal.h @@ -65,6 +65,12 @@ struct crypto_larval { u32 mask; }; +struct crypto_spawn { + struct list_head list; + struct crypto_alg *alg; + struct crypto_instance *inst; +}; + extern struct list_head crypto_alg_list; extern struct rw_semaphore crypto_alg_sem; @@ -163,6 +169,11 @@ int crypto_register_instance(struct cryp int crypto_register_notifier(struct notifier_block *nb); int crypto_unregister_notifier(struct notifier_block *nb); +int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, + struct crypto_instance *inst); +void crypto_drop_spawn(struct crypto_spawn *spawn); +struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn); + static inline void *crypto_instance_ctx(struct crypto_instance *inst) { return inst->__ctx; diff --git a/include/linux/crypto.h b/include/linux/crypto.h --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -34,6 +34,7 @@ #define CRYPTO_ALG_TYPE_COMPRESS 0x00000004 #define CRYPTO_ALG_LARVAL 0x00000010 +#define CRYPTO_ALG_DEAD 0x00000020 /* * Transform masks and values (for crt_flags). @@ -124,6 +125,8 @@ struct compress_alg { struct crypto_alg { struct list_head cra_list; + struct list_head cra_users; + u32 cra_flags; unsigned int cra_blocksize; unsigned int cra_ctxsize; - 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
