[DM] dm-crypt: Use crypto ablkcipher interface This patch converts the uses of blkcipher to ablkcipher so that async algorithms can be used if they're present. Signed-off-by: Herbert Xu --- drivers/md/dm-crypt.c | 215 ++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 156 insertions(+), 59 deletions(-) Index: linux-2.6.23-rc6/drivers/md/dm-crypt.c =================================================================== --- linux-2.6.23-rc6.orig/drivers/md/dm-crypt.c 2007-09-25 21:37:38.000000000 +0100 +++ linux-2.6.23-rc6/drivers/md/dm-crypt.c 2007-09-25 22:12:12.000000000 +0100 @@ -6,6 +6,7 @@ * This file is released under the GPL. */ +#include #include #include #include @@ -31,6 +32,7 @@ * context holding the current state of a multi-part conversion */ struct convert_context { + struct completion restart; struct bio *bio_in; struct bio *bio_out; unsigned int offset_in; @@ -39,6 +41,7 @@ struct convert_context { unsigned int idx_out; sector_t sector; int write; + atomic_t pending; int err; }; @@ -58,6 +61,11 @@ struct dm_crypt_io { sector_t sector; }; +struct dm_crypt_request { + struct scatterlist sg_in; + struct scatterlist sg_out; +}; + struct crypt_config; struct crypt_iv_operations { @@ -78,10 +86,11 @@ struct crypt_config { sector_t start; /* - * pool for per bio private data and - * for encryption buffer pages + * pool for per bio private data, crypto requests and + * encryption requeusts/buffer pages */ mempool_t *io_pool; + mempool_t *req_pool; mempool_t *page_pool; struct bio_set *bs; @@ -99,9 +108,25 @@ struct crypt_config { sector_t iv_offset; unsigned int iv_size; + /* + * Layout of each crypto request: + * + * struct ablkcipher_request + * context + * padding + * struct dm_crypt_request + * padding + * IV + * + * The padding is added so that dm_crypt_request and the IV are + * correctly aligned. + */ + unsigned int dmreq_start; + struct ablkcipher_request *req; + char cipher[CRYPTO_MAX_ALG_NAME]; char chainmode[CRYPTO_MAX_ALG_NAME]; - struct crypto_blkcipher *tfm; + struct crypto_ablkcipher *tfm; unsigned long flags; unsigned int key_size; u8 key[0]; @@ -194,7 +219,7 @@ static int crypt_iv_essiv_ctr(struct cry return PTR_ERR(essiv_tfm); } if (crypto_cipher_blocksize(essiv_tfm) != - crypto_blkcipher_ivsize(cc->tfm)) { + crypto_ablkcipher_ivsize(cc->tfm)) { ti->error = "Block size of ESSIV cipher does " "not match IV size of block cipher"; crypto_free_cipher(essiv_tfm); @@ -231,7 +256,7 @@ static int crypt_iv_essiv_gen(struct cry static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti, const char *opts) { - unsigned int bs = crypto_blkcipher_blocksize(cc->tfm); + unsigned int bs = crypto_ablkcipher_blocksize(cc->tfm); int log = ilog2(bs); /* we need to calculate how far we must shift the sector count @@ -295,43 +320,36 @@ static struct crypt_iv_operations crypt_ .generator = crypt_iv_null_gen }; -static int -crypt_convert_scatterlist(struct crypt_config *cc, struct scatterlist *out, - struct scatterlist *in, unsigned int length, - int write, sector_t sector) -{ - u8 iv[cc->iv_size] __attribute__ ((aligned(__alignof__(u64)))); - struct blkcipher_desc desc = { - .tfm = cc->tfm, - .info = iv, - .flags = CRYPTO_TFM_REQ_MAY_SLEEP, - }; - int r; +static void dec_pending(struct dm_crypt_io *io, int error); + +static void crypt_read_done(struct dm_crypt_io *io) +{ + dec_pending(io, io->ctx.err); +} - if (cc->iv_gen_ops) { - r = cc->iv_gen_ops->generator(cc, iv, sector); - if (r < 0) - return r; +static void dm_crypt_complete(struct crypto_async_request *async_req, int err) +{ + struct convert_context *ctx = async_req->data; + struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx); + struct crypt_config *cc = io->target->private; - if (write) - r = crypto_blkcipher_encrypt_iv(&desc, out, in, length); - else - r = crypto_blkcipher_decrypt_iv(&desc, out, in, length); - } else { - if (write) - r = crypto_blkcipher_encrypt(&desc, out, in, length); - else - r = crypto_blkcipher_decrypt(&desc, out, in, length); + if (err == -EINPROGRESS) { + complete(&ctx->restart); + return; } - return r; -} + mempool_free(ablkcipher_request_cast(async_req), cc->req_pool); -static void dec_pending(struct dm_crypt_io *io, int error); + if (err) + ctx->err = err; -static void crypt_read_done(struct dm_crypt_io *io) -{ - dec_pending(io, io->ctx.err); + if (!atomic_dec_and_test(&ctx->pending)) + return; + + if (ctx->write) + crypt_write_done(io, 1); + else + crypt_read_done(io); } static void @@ -347,51 +365,111 @@ crypt_convert_init(struct crypt_config * ctx->idx_out = bio_out ? bio_out->bi_idx : 0; ctx->sector = sector + cc->iv_offset; ctx->write = write; + + init_completion(&ctx->restart); + + atomic_set(&ctx->pending, 2); + ctx->err = 0; + + if (cc->req) + ablkcipher_request_set_callback(cc->req, + CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + dm_crypt_complete, ctx); } +/* Get pointer to dm_crypt_request */ +#define GET_DMREQ(req,cc) (void *)((char *)(req) + (cc)->dmreq_start) + +/* Get init crypt vector */ +#define GET_IV(dmreq,cc) (u8 *)ALIGN((unsigned long)((dmreq) + 1), \ + crypto_ablkcipher_alignmask((cc)->tfm) + 1) /* * Encrypt / decrypt data from one bio to another one (can be the same one) */ static void crypt_convert(struct crypt_config *cc, struct convert_context *ctx) { int r = 0; + struct ablkcipher_request *req = cc->req; + struct dm_crypt_request *dmreq = NULL; + u8 *iv = NULL; + + if (req) { + dmreq = GET_DMREQ(req,cc); + iv = GET_IV(dmreq, cc); + } while(ctx->idx_in < ctx->bio_in->bi_vcnt && ctx->idx_out < ctx->bio_out->bi_vcnt) { struct bio_vec *bv_in = bio_iovec_idx(ctx->bio_in, ctx->idx_in); struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out); - struct scatterlist sg_in = { - .page = bv_in->bv_page, - .offset = bv_in->bv_offset + ctx->offset_in, - .length = 1 << SECTOR_SHIFT - }; - struct scatterlist sg_out = { - .page = bv_out->bv_page, - .offset = bv_out->bv_offset + ctx->offset_out, - .length = 1 << SECTOR_SHIFT - }; - ctx->offset_in += sg_in.length; + if (!req) { + req = mempool_alloc(cc->req_pool, GFP_NOIO); + ablkcipher_request_set_tfm(req, cc->tfm); + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + dm_crypt_complete, ctx); + dmreq = GET_DMREQ(req,cc); + iv = GET_IV(dmreq, cc); + } + + dmreq->sg_in.page = bv_in->bv_page; + dmreq->sg_in.offset = bv_in->bv_offset + ctx->offset_in; + dmreq->sg_in.length = 1 << SECTOR_SHIFT; + + dmreq->sg_out.page = bv_out->bv_page; + dmreq->sg_out.offset = bv_out->bv_offset + ctx->offset_out; + dmreq->sg_out.length = 1 << SECTOR_SHIFT; + + ctx->offset_in += 1<< SECTOR_SHIFT; if (ctx->offset_in >= bv_in->bv_len) { ctx->offset_in = 0; ctx->idx_in++; } - ctx->offset_out += sg_out.length; + ctx->offset_out += 1 << SECTOR_SHIFT; if (ctx->offset_out >= bv_out->bv_len) { ctx->offset_out = 0; ctx->idx_out++; } - r = crypt_convert_scatterlist(cc, &sg_out, &sg_in, sg_in.length, - ctx->write, ctx->sector); - if (r < 0) - break; + if (cc->iv_gen_ops) { + r = cc->iv_gen_ops->generator(cc, iv, ctx->sector++); + if (r < 0) + break; + } + + ablkcipher_request_set_crypt(req, &dmreq->sg_in, + &dmreq->sg_out, 1 << SECTOR_SHIFT, + iv); + if (ctx->write) + r = crypto_ablkcipher_encrypt(req); + else + r = crypto_ablkcipher_decrypt(req); + + switch (r) { + case -EBUSY: + wait_for_completion(&ctx->restart); + INIT_COMPLETION(ctx->restart); + /* fall through*/ + case -EINPROGRESS: + atomic_inc(&ctx->pending); + req = NULL; + /* fall through*/ + case 0: + continue; + } - ctx->sector++; + break; } ctx->err = r; + cc->req = req; + + if (atomic_sub_return(2, &ctx->pending)) + return -EINPROGRESS; } static void dm_crypt_bio_destructor(struct bio *bio) @@ -820,7 +898,7 @@ static int crypt_wipe_key(struct crypt_c static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct crypt_config *cc; - struct crypto_blkcipher *tfm; + struct crypto_ablkcipher *tfm; char *tmp; char *cipher; char *chainmode; @@ -874,7 +952,7 @@ static int crypt_ctr(struct dm_target *t goto bad1; } - tfm = crypto_alloc_blkcipher(cc->cipher, 0, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_ablkcipher(cc->cipher, 0, 0); if (IS_ERR(tfm)) { ti->error = "Error allocating crypto tfm"; goto bad1; @@ -908,7 +986,7 @@ static int crypt_ctr(struct dm_target *t cc->iv_gen_ops->ctr(cc, ti, ivopts) < 0) goto bad2; - cc->iv_size = crypto_blkcipher_ivsize(tfm); + cc->iv_size = crypto_ablkcipher_ivsize(tfm); if (cc->iv_size) /* at least a 64 bit sector number should fit in our buffer */ cc->iv_size = max(cc->iv_size, @@ -928,6 +1006,19 @@ static int crypt_ctr(struct dm_target *t goto bad3; } + cc->dmreq_start = sizeof(struct ablkcipher_request); + cc->dmreq_start += crypto_ablkcipher_reqsize(tfm); + cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment()); + cc->dmreq_start += crypto_ablkcipher_alignmask(tfm) & + ~(crypto_tfm_ctx_alignment() - 1); + + cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start + + sizeof(struct dm_crypt_request) + cc->iv_size); + if (!cc->req_pool) { + ti->error = "Cannot allocate crypt request mempool"; + goto bad_req_pool; + } + cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0); if (!cc->page_pool) { ti->error = "Cannot allocate page mempool"; @@ -940,7 +1031,7 @@ static int crypt_ctr(struct dm_target *t goto bad_bs; } - if (crypto_blkcipher_setkey(tfm, cc->key, key_size) < 0) { + if (crypto_ablkcipher_setkey(tfm, cc->key, key_size) < 0) { ti->error = "Error setting key"; goto bad5; } @@ -1001,12 +1092,14 @@ bad5: bad_bs: mempool_destroy(cc->page_pool); bad4: + mempool_destroy(cc->req_pool); +bad_req_pool: mempool_destroy(cc->io_pool); bad3: if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) cc->iv_gen_ops->dtr(cc); bad2: - crypto_free_blkcipher(tfm); + crypto_free_ablkcipher(tfm); bad1: /* Must zero key material before freeing */ memset(cc, 0, sizeof(*cc) + cc->key_size * sizeof(u8)); @@ -1021,14 +1114,18 @@ static void crypt_dtr(struct dm_target * destroy_workqueue(cc->io_queue); destroy_workqueue(cc->crypt_queue); + if (cc->req) + mempool_free(cc->req, cc->req_pool); + bioset_free(cc->bs); mempool_destroy(cc->page_pool); + mempool_destroy(cc->req_pool); mempool_destroy(cc->io_pool); kfree(cc->iv_mode); if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) cc->iv_gen_ops->dtr(cc); - crypto_free_blkcipher(cc->tfm); + crypto_free_ablkcipher(cc->tfm); dm_put_device(ti, cc->dev); /* Must zero key material before freeing */