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

Reply via email to