2007-11-22 08:48:55

by Herbert Xu

[permalink] [raw]
Subject: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

[CRYPTO] chainiv: Add chain IV generator

The chain IV generator is the one we've been using in the IPsec stack.
It simply starts out with a random IV, then uses the last block of each
encrypted packet's cipher text as the IV for the next packet.

It can only be used by synchronous ciphers since we have to make sure
that we don't start the encryption of the next packet until the last
one has completed.

It does have the advantage of using very little CPU time since it doesn't
have to generate anything at all.

Signed-off-by: Herbert Xu <[email protected]>
---

crypto/Makefile | 1
crypto/chainiv.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 154 insertions(+)

diff --git a/crypto/Makefile b/crypto/Makefile
index b8b3296..8d6afb4 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_AEAD) += aead.o
crypto_blkcipher-objs := ablkcipher.o
crypto_blkcipher-objs += blkcipher.o
obj-$(CONFIG_CRYPTO_BLKCIPHER) += crypto_blkcipher.o
+obj-$(CONFIG_CRYPTO_BLKCIPHER) += chainiv.o

crypto_hash-objs := hash.o
obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
diff --git a/crypto/chainiv.c b/crypto/chainiv.c
new file mode 100644
index 0000000..25aa244
--- /dev/null
+++ b/crypto/chainiv.c
@@ -0,0 +1,153 @@
+/*
+ * chainiv: Chain IV Generator
+ *
+ * Generate IVs simply be using the last block of the previous encryption.
+ * This is mainly useful for CBC with a synchronous algorithm.
+ *
+ * Copyright (c) 2007 Herbert Xu <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <crypto/algapi.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+struct chainiv_ctx {
+ struct crypto_ablkcipher *cipher;
+ spinlock_t lock;
+ char iv[];
+};
+
+static int chainiv_givcrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+ struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
+ unsigned int ivsize;
+ int err;
+
+ ablkcipher_request_set_tfm(subreq, ctx->cipher);
+ ablkcipher_request_set_callback(subreq, req->base.flags &
+ ~CRYPTO_TFM_REQ_MAY_SLEEP,
+ req->base.complete, req->base.data);
+ ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
+ req->info);
+
+ spin_lock_bh(&ctx->lock);
+
+ ivsize = crypto_ablkcipher_ivsize(geniv);
+
+ memcpy(req->giv, ctx->iv, ivsize);
+ memcpy(req->info, ctx->iv, ivsize);
+
+ err = crypto_ablkcipher_encrypt(subreq);
+ if (err)
+ goto unlock;
+
+ memcpy(ctx->iv, req->info, ivsize);
+
+unlock:
+ spin_unlock_bh(&ctx->lock);
+
+ return err;
+}
+
+static int chainiv_givcrypt_first(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+ struct crypto_ablkcipher *cipher = ctx->cipher;
+
+ spin_lock_bh(&ctx->lock);
+ if (crypto_ablkcipher_crt(cipher)->givcrypt != chainiv_givcrypt_first)
+ goto unlock;
+
+ crypto_ablkcipher_crt(cipher)->givcrypt = chainiv_givcrypt;
+ get_random_bytes(ctx->iv, crypto_ablkcipher_ivsize(geniv));
+
+unlock:
+ spin_unlock_bh(&ctx->lock);
+
+ return chainiv_givcrypt(req);
+}
+
+static int chainiv_init(struct crypto_tfm *tfm)
+{
+ struct crypto_instance *inst = (void *)tfm->__crt_alg;
+ struct crypto_spawn *spawn = crypto_instance_ctx(inst);
+ struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_ablkcipher *cipher;
+
+ cipher = crypto_spawn_nivcipher(spawn);
+ if (IS_ERR(cipher))
+ return PTR_ERR(cipher);
+
+ ctx->cipher = cipher;
+ spin_lock_init(&ctx->lock);
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
+ crypto_ablkcipher_reqsize(cipher);
+
+ return 0;
+}
+
+static void chainiv_exit(struct crypto_tfm *tfm)
+{
+ struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+ crypto_free_ablkcipher(ctx->cipher);
+}
+
+static struct crypto_template crypto_chainiv_tmpl;
+
+static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
+{
+ struct crypto_instance *inst;
+
+ inst = givcipher_alloc_inst(&crypto_chainiv_tmpl, tb, 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(inst))
+ goto out;
+
+ inst->alg.cra_ablkcipher.givcrypt = chainiv_givcrypt_first;
+
+ inst->alg.cra_init = chainiv_init;
+ inst->alg.cra_exit = chainiv_exit;
+
+ inst->alg.cra_ctxsize = sizeof(struct chainiv_ctx);
+ inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
+
+out:
+ return inst;
+}
+
+static struct crypto_template chainiv_tmpl = {
+ .name = "chainiv",
+ .alloc = chainiv_alloc,
+ .free = givcipher_free_inst,
+ .module = THIS_MODULE,
+};
+
+static int __init chainiv_module_init(void)
+{
+ return crypto_register_template(&chainiv_tmpl);
+}
+
+static void __exit chainiv_module_exit(void)
+{
+ crypto_unregister_template(&chainiv_tmpl);
+}
+
+module_init(chainiv_module_init);
+module_exit(chainiv_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Chain IV Generator");


2007-11-22 11:17:50

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Thu, Nov 22, 2007 at 04:48:43PM +0800, Herbert Xu ([email protected]) wrote:
> +static int chainiv_givcrypt(struct ablkcipher_request *req)
> +{
> + struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
> + struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
> + struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
> + unsigned int ivsize;
> + int err;
> +
> + ablkcipher_request_set_tfm(subreq, ctx->cipher);
> + ablkcipher_request_set_callback(subreq, req->base.flags &
> + ~CRYPTO_TFM_REQ_MAY_SLEEP,
> + req->base.complete, req->base.data);
> + ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
> + req->info);
> +
> + spin_lock_bh(&ctx->lock);

Crypto hardware can access iv in interrupt context and thus this can get
wrong data.

> + ivsize = crypto_ablkcipher_ivsize(geniv);
> +
> + memcpy(req->giv, ctx->iv, ivsize);
> + memcpy(req->info, ctx->iv, ivsize);
> +
> + err = crypto_ablkcipher_encrypt(subreq);
> + if (err)
> + goto unlock;

Are you sure that crypto operation has to be limited to be performed
with turned off bottom halves? I believe this is a huge limitation for
those ablkcipher devices which are not async actually...

--
Evgeniy Polyakov

2007-11-22 11:26:16

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Thu, Nov 22, 2007 at 02:17:11PM +0300, Evgeniy Polyakov wrote:
>
> > + spin_lock_bh(&ctx->lock);
>
> Crypto hardware can access iv in interrupt context and thus this can get
> wrong data.

This lock only guards against other callers of this function.
It doesn't care about how you do the underlying encryption.
You can do it in softirq context, hardirq context, or offload
it to the moon :)

> Are you sure that crypto operation has to be limited to be performed
> with turned off bottom halves? I believe this is a huge limitation for
> those ablkcipher devices which are not async actually...

This only applies to givcrypt which is only used by IPsec where
we already do everything under a bh lock :)

New users should specify the IV generator explicitly as is done
in dm-crypt.

In any case, this (the choice of chainiv as the default for sync
blkcipher) is something that we can change pretty easily down
the track without affecting anything else.

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

2007-11-22 12:05:16

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Thu, Nov 22, 2007 at 07:26:13PM +0800, Herbert Xu ([email protected]) wrote:
> On Thu, Nov 22, 2007 at 02:17:11PM +0300, Evgeniy Polyakov wrote:
> >
> > > + spin_lock_bh(&ctx->lock);
> >
> > Crypto hardware can access iv in interrupt context and thus this can get
> > wrong data.
>
> This lock only guards against other callers of this function.
> It doesn't care about how you do the underlying encryption.
> You can do it in softirq context, hardirq context, or offload
> it to the moon :)

What if dm-crypt will use the same interface (or other bulk-processing
user) will use it with software crypto? Or was it specially designed for
ipsec only?

> > Are you sure that crypto operation has to be limited to be performed
> > with turned off bottom halves? I believe this is a huge limitation for
> > those ablkcipher devices which are not async actually...
>
> This only applies to givcrypt which is only used by IPsec where
> we already do everything under a bh lock :)
>
> New users should specify the IV generator explicitly as is done
> in dm-crypt.

I.e. it is an ipsec helper only and should not be used by other users?

--
Evgeniy Polyakov

2007-11-22 12:28:45

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Thu, Nov 22, 2007 at 03:05:00PM +0300, Evgeniy Polyakov wrote:
>
> What if dm-crypt will use the same interface (or other bulk-processing
> user) will use it with software crypto? Or was it specially designed for
> ipsec only?

dm-crypt (when we convert it to using givcrypt instead of its
own hard-coded IV generators) will specify the IV generators
explicitly which would bypass chainiv.

> I.e. it is an ipsec helper only and should not be used by other users?

Yes new applications should use explicit IV generators. As
I said I wouldn't be against changing the default for sync away
from chainiv if something more appropriate comes up.

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

2007-11-25 12:31:48

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Thu, Nov 22, 2007 at 02:17:11PM +0300, Evgeniy Polyakov wrote:
>
> Are you sure that crypto operation has to be limited to be performed
> with turned off bottom halves? I believe this is a huge limitation for
> those ablkcipher devices which are not async actually...

OK, one night I suddenly had this idea that we can postpone the
uncommon collision case to process context. Here's the patch.

So as long as we're doing things one-by-one nothing changes.

However, once we see contention we move the work into a work
queue. This should be better compared to what we do now which
is to have the contending CPU spin waiting for the other CPU to
finish its crypto operation.

We only move back once things quiten down.

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/chainiv.c b/crypto/chainiv.c
index 25aa244..d2882b5 100644
--- a/crypto/chainiv.c
+++ b/crypto/chainiv.c
@@ -20,45 +20,107 @@
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/workqueue.h>
+
+enum {
+ CHAINIV_STATE_INUSE = 0,
+};

struct chainiv_ctx {
struct crypto_ablkcipher *cipher;
+ unsigned long state;
+
spinlock_t lock;
+ struct crypto_queue queue;
+
+ struct work_struct postponed;
+ int err;
+
char iv[];
};

-static int chainiv_givcrypt(struct ablkcipher_request *req)
+static int chainiv_schedule_work(struct chainiv_ctx *ctx)
+{
+ int queued;
+
+ if (!ctx->queue.qlen) {
+ smp_mb__before_clear_bit();
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
+
+ if (!ctx->queue.qlen ||
+ test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto out;
+ }
+
+ queued = schedule_work(&ctx->postponed);
+ BUG_ON(!queued);
+
+out:
+ return ctx->err;
+}
+
+static int chainiv_postpone_request(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+ int err;
+
+ spin_lock_bh(&ctx->lock);
+ err = ablkcipher_enqueue_request(&ctx->queue, req);
+ spin_unlock_bh(&ctx->lock);
+
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ return err;
+
+ ctx->err = err;
+ return chainiv_schedule_work(ctx);
+}
+
+static int chainiv_givcrypt_tail(struct ablkcipher_request *req)
{
struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
unsigned int ivsize;
- int err;

ablkcipher_request_set_tfm(subreq, ctx->cipher);
- ablkcipher_request_set_callback(subreq, req->base.flags &
- ~CRYPTO_TFM_REQ_MAY_SLEEP,
+ ablkcipher_request_set_callback(subreq, req->base.flags,
req->base.complete, req->base.data);
ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
req->info);

- spin_lock_bh(&ctx->lock);
-
ivsize = crypto_ablkcipher_ivsize(geniv);

memcpy(req->giv, ctx->iv, ivsize);
memcpy(req->info, ctx->iv, ivsize);

- err = crypto_ablkcipher_encrypt(subreq);
- if (err)
- goto unlock;
+ ctx->err = crypto_ablkcipher_encrypt(subreq);
+ if (ctx->err)
+ goto out;

memcpy(ctx->iv, req->info, ivsize);

-unlock:
- spin_unlock_bh(&ctx->lock);
+out:
+ return chainiv_schedule_work(ctx);
+}

- return err;
+static int chainiv_givcrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto postpone;
+
+ if (ctx->queue.qlen) {
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
+ goto postpone;
+ }
+
+ return chainiv_givcrypt_tail(req);
+
+postpone:
+ return chainiv_postpone_request(req);
}

static int chainiv_givcrypt_first(struct ablkcipher_request *req)
@@ -67,19 +129,43 @@ static int chainiv_givcrypt_first(struct ablkcipher_request *req)
struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
struct crypto_ablkcipher *cipher = ctx->cipher;

- spin_lock_bh(&ctx->lock);
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto out;
+
if (crypto_ablkcipher_crt(cipher)->givcrypt != chainiv_givcrypt_first)
goto unlock;

crypto_ablkcipher_crt(cipher)->givcrypt = chainiv_givcrypt;
get_random_bytes(ctx->iv, crypto_ablkcipher_ivsize(geniv));

+ return chainiv_givcrypt_tail(req);
+
unlock:
- spin_unlock_bh(&ctx->lock);
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);

+out:
return chainiv_givcrypt(req);
}

+static void chainiv_do_postponed(struct work_struct *work)
+{
+ struct chainiv_ctx *ctx = container_of(work, struct chainiv_ctx,
+ postponed);
+ struct ablkcipher_request *req;
+
+ /* Only handle one request to avoid hogging keventd. */
+ spin_lock_bh(&ctx->lock);
+ req = ablkcipher_dequeue_request(&ctx->queue);
+ spin_unlock_bh(&ctx->lock);
+
+ if (!req) {
+ chainiv_schedule_work(ctx);
+ return;
+ }
+
+ chainiv_givcrypt_tail(req);
+}
+
static int chainiv_init(struct crypto_tfm *tfm)
{
struct crypto_instance *inst = (void *)tfm->__crt_alg;
@@ -94,6 +180,9 @@ static int chainiv_init(struct crypto_tfm *tfm)
ctx->cipher = cipher;
spin_lock_init(&ctx->lock);

+ crypto_init_queue(&ctx->queue, 100);
+ INIT_WORK(&ctx->postponed, chainiv_do_postponed);
+
tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
crypto_ablkcipher_reqsize(cipher);

@@ -103,6 +192,9 @@ static int chainiv_init(struct crypto_tfm *tfm)
static void chainiv_exit(struct crypto_tfm *tfm)
{
struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ BUG_ON(test_bit(CHAINIV_STATE_INUSE, &ctx->state) || ctx->queue.qlen);
+
crypto_free_ablkcipher(ctx->cipher);
}

@@ -117,6 +209,8 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
if (IS_ERR(inst))
goto out;

+ inst->alg.cra_flags |= CRYPTO_ALG_ASYNC;
+
inst->alg.cra_ablkcipher.givcrypt = chainiv_givcrypt_first;

inst->alg.cra_init = chainiv_init;

2007-11-25 12:58:37

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Sun, Nov 25, 2007 at 08:31:41PM +0800, Herbert Xu wrote:
>
> OK, one night I suddenly had this idea that we can postpone the
> uncommon collision case to process context. Here's the patch.

Small improvement, set the may-sleep flag when postponed. So
now it can sleep where it couldn't before, ironic huh :)

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/chainiv.c b/crypto/chainiv.c
index 25aa244..49457fa 100644
--- a/crypto/chainiv.c
+++ b/crypto/chainiv.c
@@ -20,45 +20,108 @@
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/workqueue.h>
+
+enum {
+ CHAINIV_STATE_INUSE = 0,
+};

struct chainiv_ctx {
struct crypto_ablkcipher *cipher;
+ unsigned long state;
+
spinlock_t lock;
+ struct crypto_queue queue;
+
+ struct work_struct postponed;
+ int err;
+
char iv[];
};

-static int chainiv_givcrypt(struct ablkcipher_request *req)
+static int chainiv_schedule_work(struct chainiv_ctx *ctx)
+{
+ int queued;
+
+ if (!ctx->queue.qlen) {
+ smp_mb__before_clear_bit();
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
+
+ if (!ctx->queue.qlen ||
+ test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto out;
+ }
+
+ queued = schedule_work(&ctx->postponed);
+ BUG_ON(!queued);
+
+out:
+ return ctx->err;
+}
+
+static int chainiv_postpone_request(struct ablkcipher_request *req)
{
struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
- struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
- unsigned int ivsize;
int err;

- ablkcipher_request_set_tfm(subreq, ctx->cipher);
- ablkcipher_request_set_callback(subreq, req->base.flags &
- ~CRYPTO_TFM_REQ_MAY_SLEEP,
- req->base.complete, req->base.data);
- ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
- req->info);
-
spin_lock_bh(&ctx->lock);
+ err = ablkcipher_enqueue_request(&ctx->queue, req);
+ spin_unlock_bh(&ctx->lock);
+
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ return err;
+
+ ctx->err = err;
+ return chainiv_schedule_work(ctx);
+}
+
+static int chainiv_givcrypt_tail(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+ struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
+ unsigned int ivsize;

ivsize = crypto_ablkcipher_ivsize(geniv);

memcpy(req->giv, ctx->iv, ivsize);
memcpy(req->info, ctx->iv, ivsize);

- err = crypto_ablkcipher_encrypt(subreq);
- if (err)
- goto unlock;
+ ctx->err = crypto_ablkcipher_encrypt(subreq);
+ if (ctx->err)
+ goto out;

memcpy(ctx->iv, req->info, ivsize);

-unlock:
- spin_unlock_bh(&ctx->lock);
+out:
+ return chainiv_schedule_work(ctx);
+}
+
+static int chainiv_givcrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *geniv = crypto_ablkcipher_reqtfm(req);
+ struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+ struct ablkcipher_request *subreq = ablkcipher_request_ctx(req);
+
+ ablkcipher_request_set_tfm(subreq, ctx->cipher);
+ ablkcipher_request_set_callback(subreq, req->base.flags,
+ req->base.complete, req->base.data);
+ ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
+ req->info);
+
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto postpone;

- return err;
+ if (ctx->queue.qlen) {
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
+ goto postpone;
+ }
+
+ return chainiv_givcrypt_tail(req);
+
+postpone:
+ return chainiv_postpone_request(req);
}

static int chainiv_givcrypt_first(struct ablkcipher_request *req)
@@ -67,19 +130,47 @@ static int chainiv_givcrypt_first(struct ablkcipher_request *req)
struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
struct crypto_ablkcipher *cipher = ctx->cipher;

- spin_lock_bh(&ctx->lock);
+ if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
+ goto out;
+
if (crypto_ablkcipher_crt(cipher)->givcrypt != chainiv_givcrypt_first)
goto unlock;

crypto_ablkcipher_crt(cipher)->givcrypt = chainiv_givcrypt;
get_random_bytes(ctx->iv, crypto_ablkcipher_ivsize(geniv));

+ return chainiv_givcrypt_tail(req);
+
unlock:
- spin_unlock_bh(&ctx->lock);
+ clear_bit(CHAINIV_STATE_INUSE, &ctx->state);

+out:
return chainiv_givcrypt(req);
}

+static void chainiv_do_postponed(struct work_struct *work)
+{
+ struct chainiv_ctx *ctx = container_of(work, struct chainiv_ctx,
+ postponed);
+ struct ablkcipher_request *req;
+ struct ablkcipher_request *subreq;
+
+ /* Only handle one request to avoid hogging keventd. */
+ spin_lock_bh(&ctx->lock);
+ req = ablkcipher_dequeue_request(&ctx->queue);
+ spin_unlock_bh(&ctx->lock);
+
+ if (!req) {
+ chainiv_schedule_work(ctx);
+ return;
+ }
+
+ subreq = ablkcipher_request_ctx(req);
+ subreq->base.flags |= CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ chainiv_givcrypt_tail(req);
+}
+
static int chainiv_init(struct crypto_tfm *tfm)
{
struct crypto_instance *inst = (void *)tfm->__crt_alg;
@@ -94,6 +185,9 @@ static int chainiv_init(struct crypto_tfm *tfm)
ctx->cipher = cipher;
spin_lock_init(&ctx->lock);

+ crypto_init_queue(&ctx->queue, 100);
+ INIT_WORK(&ctx->postponed, chainiv_do_postponed);
+
tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
crypto_ablkcipher_reqsize(cipher);

@@ -103,6 +197,9 @@ static int chainiv_init(struct crypto_tfm *tfm)
static void chainiv_exit(struct crypto_tfm *tfm)
{
struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ BUG_ON(test_bit(CHAINIV_STATE_INUSE, &ctx->state) || ctx->queue.qlen);
+
crypto_free_ablkcipher(ctx->cipher);
}

@@ -117,6 +214,8 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
if (IS_ERR(inst))
goto out;

+ inst->alg.cra_flags |= CRYPTO_ALG_ASYNC;
+
inst->alg.cra_ablkcipher.givcrypt = chainiv_givcrypt_first;

inst->alg.cra_init = chainiv_init;

2007-11-26 11:55:04

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [PATCH 5/11] [CRYPTO] chainiv: Add chain IV generator

On Sun, Nov 25, 2007 at 08:58:34PM +0800, Herbert Xu ([email protected]) wrote:
> On Sun, Nov 25, 2007 at 08:31:41PM +0800, Herbert Xu wrote:
> >
> > OK, one night I suddenly had this idea that we can postpone the
> > uncommon collision case to process context. Here's the patch.
>
> Small improvement, set the may-sleep flag when postponed. So
> now it can sleep where it couldn't before, ironic huh :)

Great! I believe eventually we will even get rid of the lock around it :)

--
Evgeniy Polyakov