From: Milan Broz Moving all work from map dm call to workqueue. Read is processed in two steps - generating read request and after completion in endio second req. for decryption. Writes are processed in one step - encryption and generating request. Previous implementation of map function in dm-crypt waits for earlies items to complete to solve insufficient memory situations. /* out of memory -> run queues */ if (remaining) blk_congestion_wait(bio_data_dir(clone), HZ/100); This will not work after reduce stack changes in generic_make_request() which will not process IOs immediately. (md-dm-reduce-stack-usage-with-stacked-block-devices.patch) Signed-off-by: Milan Broz Index: linux-2.6.17/drivers/md/dm-crypt.c =================================================================== --- linux-2.6.17.orig/drivers/md/dm-crypt.c 2006-08-09 21:43:14.000000000 +0100 +++ linux-2.6.17/drivers/md/dm-crypt.c 2006-08-09 21:48:01.000000000 +0100 @@ -34,6 +34,7 @@ struct crypt_io { struct work_struct work; atomic_t pending; int error; + int post_process; }; /* @@ -437,13 +438,6 @@ static void dec_pending(struct crypt_io mempool_free(io, cc->io_pool); } -/* - * kcryptd: - * - * Needed because it would be very unwise to do decryption in an - * interrupt context, so bios returning from read requests get - * queued here. - */ static struct workqueue_struct *_kcryptd_workqueue; static void kcryptd_do_work(void *data); @@ -466,24 +460,27 @@ static int crypt_endio(struct bio *bio, if (!read_io) crypt_free_buffer_pages(cc, bio, done); + if (unlikely(error)) + goto out; + + /* keep going - not finished yet */ if (unlikely(bio->bi_size)) return 1; - bio_put(bio); - - /* - * successful reads are decrypted by the worker thread - */ if (!read_io) goto out; - if (unlikely(!bio_flagged(bio, BIO_UPTODATE))) + if (unlikely(!bio_flagged(bio, BIO_UPTODATE))) { + error = -EIO; goto out; + } + io->post_process = 1; kcryptd_queue_io(io); return 0; - out: + bio_put(bio); + dec_pending(io, error); return error; } @@ -498,60 +495,91 @@ static void clone_init(struct crypt_io * clone->bi_rw = io->bio->bi_rw; } -static struct bio *clone_read(struct crypt_io *io, - sector_t sector) +static void process_read(struct crypt_io *io) { struct crypt_config *cc = io->target->private; struct bio *bio = io->bio; struct bio *clone; + atomic_inc(&io->pending); + /* * The block layer might modify the bvec array, so always * copy the required bvecs because we need the original * one in order to decrypt the whole bio data *afterwards*. */ clone = bio_alloc(GFP_NOIO, bio_segments(bio)); - if (unlikely(!clone)) - return NULL; + if (unlikely(!clone)) { + dec_pending(io, -ENOMEM); + return; + } clone_init(io, clone); clone->bi_idx = 0; clone->bi_vcnt = bio_segments(bio); clone->bi_size = bio->bi_size; + clone->bi_sector = cc->start + bio->bi_sector - io->target->begin; memcpy(clone->bi_io_vec, bio_iovec(bio), - sizeof(struct bio_vec) * clone->bi_vcnt); - clone->bi_sector = cc->start + sector; + sizeof(struct bio_vec) * clone->bi_vcnt); - return clone; + bio_get(clone); + generic_make_request(clone); } -static struct bio *clone_write(struct crypt_io *io, - sector_t sector, - unsigned *bvec_idx, - struct convert_context *ctx) +static void process_write(struct crypt_io *io) { struct crypt_config *cc = io->target->private; struct bio *bio = io->bio; struct bio *clone; + struct convert_context ctx; + unsigned remaining = bio->bi_size; + sector_t sector = bio->bi_sector - io->target->begin; + unsigned bvec_idx = 0; - clone = crypt_alloc_buffer(cc, bio->bi_size, - io->first_clone, bvec_idx); - if (!clone) - return NULL; + crypt_convert_init(cc, &ctx, NULL, bio, sector, 1); - ctx->bio_out = clone; + /* + * The allocated buffers can be smaller than the whole bio, + * so repeat the whole process until all the data can be handled. + */ + do { + atomic_inc(&io->pending); - if (unlikely(crypt_convert(cc, ctx) < 0)) { - crypt_free_buffer_pages(cc, clone, - clone->bi_size); - bio_put(clone); - return NULL; - } + clone = crypt_alloc_buffer(cc, bio->bi_size, + io->first_clone, &bvec_idx); + if (unlikely(!clone)) { + dec_pending(io, -ENOMEM); + return; + } - clone_init(io, clone); - clone->bi_sector = cc->start + sector; + ctx.bio_out = clone; + + if (unlikely(crypt_convert(cc, &ctx) < 0)) { + crypt_free_buffer_pages(cc, clone, clone->bi_size); + bio_put(clone); + dec_pending(io, -EIO); + return; + } + + clone_init(io, clone); + clone->bi_sector = cc->start + sector; + + if (!io->first_clone) { + /* + * hold a reference to the first clone, because it + * holds the bio_vec array and that can't be freed + * before all other clones are released + */ + bio_get(clone); + io->first_clone = clone; + } + + remaining -= clone->bi_size; + sector += bio_sectors(clone); + generic_make_request(clone); - return clone; + /* out of memory -> wait */ + } while (remaining && blk_congestion_wait(bio_data_dir(clone), HZ/100)); } static void process_read_endio(struct crypt_io *io) @@ -569,7 +597,12 @@ static void kcryptd_do_work(void *data) { struct crypt_io *io = data; - process_read_endio(io); + if (io->post_process) + process_read_endio(io); + else if (bio_data_dir(io->bio) == READ) + process_read(io); + else + process_write(io); } /* @@ -839,68 +872,19 @@ static int crypt_map(struct dm_target *t { struct crypt_config *cc = ti->private; struct crypt_io *io; - struct convert_context ctx; - struct bio *clone; - unsigned int remaining = bio->bi_size; - sector_t sector = bio->bi_sector - ti->begin; - unsigned int bvec_idx = 0; io = mempool_alloc(cc->io_pool, GFP_NOIO); + if (unlikely(!io)) + return -ENOMEM; + io->target = ti; io->bio = bio; io->first_clone = NULL; - io->error = 0; - atomic_set(&io->pending, 1); /* hold a reference */ - - if (bio_data_dir(bio) == WRITE) - crypt_convert_init(cc, &ctx, NULL, bio, sector, 1); - - /* - * The allocated buffers can be smaller than the whole bio, - * so repeat the whole process until all the data can be handled. - */ - while (remaining) { - if (bio_data_dir(bio) == WRITE) - clone = crypt_clone_write(io, sector, &bvec_idx, &ctx); - else - clone = crypt_clone_read(io, sector); - if (!clone) - goto cleanup; - - if (!io->first_clone) { - /* - * hold a reference to the first clone, because it - * holds the bio_vec array and that can't be freed - * before all other clones are released - */ - bio_get(clone); - io->first_clone = clone; - } - atomic_inc(&io->pending); - - remaining -= clone->bi_size; - sector += bio_sectors(clone); - - generic_make_request(clone); - - /* out of memory -> run queues */ - if (remaining) - blk_congestion_wait(bio_data_dir(clone), HZ/100); - } + io->error = io->post_process = 0; + atomic_set(&io->pending, 0); /* io created in workqueue */ + kcryptd_queue_io(io); - /* drop reference, clones could have returned before we reach this */ - dec_pending(io, 0); return 0; - -cleanup: - if (io->first_clone) { - dec_pending(io, -ENOMEM); - return 0; - } - - /* if no bio has been dispatched yet, we can directly return the error */ - mempool_free(io, cc->io_pool); - return -ENOMEM; } static int crypt_status(struct dm_target *ti, status_type_t type, @@ -1009,7 +993,7 @@ error: static struct target_type crypt_target = { .name = "crypt", - .version= {1, 2, 0}, + .version= {1, 3, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr,