The read path allocates a temporary buffer to hold decompressed data, which is than copied into the caller's bio buffer. Instead of allocating a temporary buffer to hold the decompressed data, decompress the data in the caller's bio buffer. This can be done only if the destination in the bio-buffer is contigious within the same bio-segment. Signed-off-by: Ram Pai <linuxram@xxxxxxxxxx> --- drivers/md/dm-inplace-compress.c | 143 ++++++++++++++++++++++++++----------- drivers/md/dm-inplace-compress.h | 2 + 2 files changed, 102 insertions(+), 43 deletions(-) diff --git a/drivers/md/dm-inplace-compress.c b/drivers/md/dm-inplace-compress.c index c56d9b7..f6b95e3 100644 --- a/drivers/md/dm-inplace-compress.c +++ b/drivers/md/dm-inplace-compress.c @@ -792,11 +792,34 @@ static void dm_icomp_kfree(void *addr, unsigned int size) kfree(addr); } +static void dm_icomp_release_decomp_buffer(struct dm_icomp_io_range *io) +{ + if (!io->decomp_data) + return; -static void dm_icomp_free_io_range(struct dm_icomp_io_range *io) + if (io->decomp_kmap) + kunmap(io->decomp_real_data); + else + dm_icomp_kfree(io->decomp_real_data, io->decomp_req_len); + io->decomp_data = io->decomp_real_data = NULL; + io->decomp_len = 0; + io->decomp_kmap = false; +} + +static void dm_icomp_release_comp_buffer(struct dm_icomp_io_range *io) { - dm_icomp_kfree(io->decomp_data, io->decomp_len); + if (!io->comp_data) + return; dm_icomp_kfree(io->comp_data, io->comp_len); + + io->comp_data = NULL; + io->comp_len = 0; +} + +static void dm_icomp_free_io_range(struct dm_icomp_io_range *io) +{ + dm_icomp_release_decomp_buffer(io); + dm_icomp_release_comp_buffer(io); kmem_cache_free(dm_icomp_io_range_cachep, io); } @@ -890,7 +913,9 @@ static struct dm_icomp_io_range *dm_icomp_create_io_range( io->req = req; io->decomp_data = NULL; + io->decomp_real_data = NULL; io->decomp_len = 0; + io->decomp_kmap = false; io->decomp_req_len = 0; return io; } @@ -909,15 +934,43 @@ static struct dm_icomp_io_range *dm_icomp_create_io_read_range( return io; } -static int dm_icomp_update_io_read_range(struct dm_icomp_io_range *io) +static int dm_icomp_update_io_read_range(struct dm_icomp_io_range *io, + struct bio *bio, ssize_t bio_off) { + struct bvec_iter iter; + struct bio_vec bv; + bool just_use = false; + if (io->decomp_len) return 0; + /* use a bio buffer long enough to hold the uncompressed data */ + bio_for_each_segment(bv, bio, iter) { + int avail_len; + int length = bv.bv_len; + + if (!just_use && bio_off >= length) { + bio_off -= length; + continue; + } + avail_len = just_use ? length : length-bio_off; + if (avail_len >= io->decomp_req_len) { + io->decomp_real_data = kmap(bv.bv_page); + io->decomp_data = io->decomp_real_data + bio_off; + io->decomp_len = io->decomp_req_len = avail_len; + io->decomp_kmap = true; + return 0; + } + just_use = true; + } + + /* none available. :( Allocate one */ io->decomp_data = dm_icomp_kmalloc(io->decomp_req_len, GFP_NOIO); if (!io->decomp_data) return 1; + io->decomp_real_data = io->decomp_data; io->decomp_len = io->decomp_req_len; + io->decomp_kmap = false; return 0; } @@ -978,24 +1031,34 @@ static struct dm_icomp_io_range *dm_icomp_create_io_write_range( struct dm_icomp_req *req) { struct dm_icomp_io_range *io; + struct bio *bio = req->bio; sector_t size = bio_sectors(req->bio)<<9; + int segments = bio_segments(bio); int comp_len = dm_icomp_compressor_len(req->info, size); void *addr; - addr = dm_icomp_kmalloc(size, GFP_NOIO); + if (segments == 1) { + struct bio_vec bv = bio_iovec(bio); + + addr = kmap(bv.bv_page); + } else + addr = dm_icomp_kmalloc(size, GFP_NOIO); + if (!addr) return NULL; io = dm_icomp_create_io_range(req, comp_len); if (!io) { - dm_icomp_kfree(addr, size); + (segments == 1) ? kunmap(addr) : dm_icomp_kfree(addr, size); return NULL; } - io->decomp_data = addr; + io->decomp_data = io->decomp_real_data = addr; io->decomp_len = size; - dm_icomp_bio_copy(req->bio, 0, io->decomp_data, size, true); + io->decomp_kmap = (segments == 1); + if (!io->decomp_kmap) + dm_icomp_bio_copy(req->bio, 0, io->decomp_data, size, true); return io; } @@ -1105,7 +1168,15 @@ static void dm_icomp_handle_read_decomp(struct dm_icomp_req *req) io->io_region.sector -= req->info->data_start; - if (dm_icomp_update_io_read_range(io)) { + if (io->io_region.sector >= + req->bio->bi_iter.bi_sector) + dst_off = (io->io_region.sector - + req->bio->bi_iter.bi_sector) << 9; + else + src_off = (req->bio->bi_iter.bi_sector - + io->io_region.sector) << 9; + + if (dm_icomp_update_io_read_range(io, req->bio, dst_off)) { req->result = -EIO; return; } @@ -1114,20 +1185,19 @@ static void dm_icomp_handle_read_decomp(struct dm_icomp_req *req) ret = dm_icomp_io_range_decompress(req->info, io->comp_data, io->comp_len, io->decomp_data, io->decomp_len); if (ret < 0) { + dm_icomp_release_decomp_buffer(io); + dm_icomp_release_comp_buffer(io); req->result = -EIO; return; } - if (io->io_region.sector >= req->bio->bi_iter.bi_sector) - dst_off = (io->io_region.sector - - req->bio->bi_iter.bi_sector) << 9; - else - src_off = (req->bio->bi_iter.bi_sector - - io->io_region.sector) << 9; - len = min_t(ssize_t, io->decomp_len - src_off, (bio_sectors(req->bio) << 9) - dst_off); + dm_icomp_bio_copy(req->bio, dst_off, + ((ret == 1) ? io->comp_data : io->decomp_data) + src_off, + len, false); + /* io range in all_io list is ordered for read IO */ while (bio_off != dst_off) { ssize_t size = min_t(ssize_t, PAGE_SIZE, @@ -1137,13 +1207,9 @@ static void dm_icomp_handle_read_decomp(struct dm_icomp_req *req) bio_off += size; } - if (ret == 1) - dm_icomp_bio_copy(req->bio, dst_off, - io->comp_data + src_off, len, false); - else - dm_icomp_bio_copy(req->bio, dst_off, - io->decomp_data + src_off, len, false); bio_off = dst_off + len; + dm_icomp_release_decomp_buffer(io); + dm_icomp_release_comp_buffer(io); } while (bio_off != (bio_sectors(req->bio) << 9)) { @@ -1270,29 +1336,20 @@ static int dm_icomp_handle_write_modify(struct dm_icomp_io_range *io, if (start < req->bio->bi_iter.bi_sector && start + count > bio_end_sector(req->bio)) { /* we don't split an extent */ - if (ret == 1) { - memcpy(io->decomp_data, io->comp_data, io->decomp_len); - dm_icomp_bio_copy(req->bio, 0, - io->decomp_data + - ((req->bio->bi_iter.bi_sector - start) << 9), - bio_sectors(req->bio) << 9, true); - } else { - dm_icomp_bio_copy(req->bio, 0, - io->decomp_data + - ((req->bio->bi_iter.bi_sector - start) << 9), - bio_sectors(req->bio) << 9, true); - - dm_icomp_kfree(io->comp_data, io->comp_len); - /* New compressed len might be bigger */ - io->comp_data = dm_icomp_kmalloc( - dm_icomp_compressor_len( - req->info, io->decomp_len), GFP_NOIO); - io->comp_len = io->decomp_len; - if (!io->comp_data) { - req->result = -ENOMEM; - return -EIO; + if (!io->decomp_kmap) { + if (ret == 1) { + memcpy(io->decomp_data, io->comp_data, + io->decomp_len); + dm_icomp_bio_copy(req->bio, 0, + io->decomp_data + + ((req->bio->bi_iter.bi_sector - start) << 9), + bio_sectors(req->bio) << 9, true); + } else { + dm_icomp_bio_copy(req->bio, 0, + io->decomp_data + + ((req->bio->bi_iter.bi_sector - start) << 9), + bio_sectors(req->bio) << 9, true); } - io->io_req.mem.ptr.addr = io->comp_data; } /* need compress data */ ret = 0; diff --git a/drivers/md/dm-inplace-compress.h b/drivers/md/dm-inplace-compress.h index d9cf05a..e144e96 100644 --- a/drivers/md/dm-inplace-compress.h +++ b/drivers/md/dm-inplace-compress.h @@ -115,7 +115,9 @@ struct dm_icomp_meta_io { struct dm_icomp_io_range { struct dm_io_request io_req; struct dm_io_region io_region; + int decomp_kmap; /* Is the decomp_data kmapped'? */ void *decomp_data; + void *decomp_real_data; /* holds the actual start of the buffer */ unsigned int decomp_req_len;/* originally requested length */ unsigned int decomp_len; /* actual allocated/mapped length */ void *comp_data; -- 1.7.1 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel