The previous patch had a bug when hash verification failed in the tasklet. This happened because the tasklet has already advanced the bvec_iter when it falls back to the work-queue implementation. When the work-queue job begins running, it attempts to restart verifiying at block i, but the bvec_iter is at block i+1. This causes several failures, the most noticeable is that there are not enough bytes in the bvec_iter to finish verification. Since there are not enough bytes to finish, the work queue gets stuck in an infinite loop in verity_for_io_block. To fix this, we can make a local copy of the bvec_iter struct in verity_verify_io. This requires that we restart verification from the first block when deferring to a work-queue rather than starting from the last block verified in the tasklet. This patch also fixes what appears to be a memory leak when FEC is enabled. The call to verity_fec_init_io should only be made if we are in a work-queue since a tasklet does not use FEC and is unable to free the memory. Signed-off-by: Nathan Huckleberry <nhuck@xxxxxxxxxx> --- drivers/md/dm-verity-target.c | 23 ++++++++++++++--------- drivers/md/dm-verity.h | 1 - 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 144889ba43ea..0677795454d7 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -499,17 +499,23 @@ static int verity_verify_io(struct dm_verity_io *io) bool is_zero; struct dm_verity *v = io->v; struct bvec_iter start; + /* + * Copy the iterator in case we need to restart verification in a + * work-queue. + */ + struct bvec_iter iter_copy = io->iter; struct crypto_wait wait; struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size); + unsigned int b; - for (; io->block_idx < io->n_blocks; io->block_idx++) { + for (b = 0; b < io->n_blocks; b++) { int r; - sector_t cur_block = io->block + io->block_idx; + sector_t cur_block = io->block + b; struct ahash_request *req = verity_io_hash_req(v, io); if (v->validated_blocks && likely(test_bit(cur_block, v->validated_blocks))) { - verity_bv_skip_block(v, io, &io->iter); + verity_bv_skip_block(v, io, &iter_copy); continue; } @@ -524,7 +530,7 @@ static int verity_verify_io(struct dm_verity_io *io) * If we expect a zero block, don't validate, just * return zeros. */ - r = verity_for_bv_block(v, io, &io->iter, + r = verity_for_bv_block(v, io, &iter_copy, verity_bv_zero); if (unlikely(r < 0)) return r; @@ -536,8 +542,8 @@ static int verity_verify_io(struct dm_verity_io *io) if (unlikely(r < 0)) return r; - start = io->iter; - r = verity_for_io_block(v, io, &io->iter, &wait); + start = iter_copy; + r = verity_for_io_block(v, io, &iter_copy, &wait); if (unlikely(r < 0)) return r; @@ -608,6 +614,8 @@ static void verity_work(struct work_struct *w) struct dm_verity_io *io = container_of(w, struct dm_verity_io, work); io->in_tasklet = false; + verity_fec_init_io(io); + verity_finish_io(io, errno_to_blk_status(verity_verify_io(io))); } @@ -638,7 +646,6 @@ static void verity_end_io(struct bio *bio) return; } - io->block_idx = 0; if (static_branch_unlikely(&use_tasklet_enabled) && io->v->use_tasklet) { tasklet_init(&io->tasklet, verity_tasklet, (unsigned long)io); tasklet_schedule(&io->tasklet); @@ -756,8 +763,6 @@ static int verity_map(struct dm_target *ti, struct bio *bio) bio->bi_private = io; io->iter = bio->bi_iter; - verity_fec_init_io(io); - verity_submit_prefetch(v, io); submit_bio_noacct(bio); diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index e73a7972c3e9..fb92b22a6404 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -78,7 +78,6 @@ struct dm_verity_io { sector_t block; unsigned n_blocks; - unsigned int block_idx; bool in_tasklet; struct bvec_iter iter; -- 2.37.1.455.g008518b4e5-goog -- dm-devel mailing list dm-devel@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/dm-devel