The read path a) allocates a temporary buffer to hold the compressed data. b) reads the compressed data into the temporary buffer. c) decompresses the compressed data into the caller's bio-buffer. We know that the caller's bio-buffer will be atleast as large as the compressed data. So we could save a buffer allocation in step (a) if we use caller's bio-buffer to read in the compressed data from the disk. NOTE: this is not possible all the time, but is possible if the destination within the bio-buffer falls within the same segment. This saves us from holding the temporary buffer across read boundaries. Which is a huge advantage especially if we are operating under acute memory starvation; mostly when this block device is used as a swap device. Signed-off-by: Ram Pai <linuxram@xxxxxxxxxx> --- drivers/md/dm-inplace-compress.c | 185 ++++++++++++++++++------------------- drivers/md/dm-inplace-compress.h | 1 + 2 files changed, 91 insertions(+), 95 deletions(-) diff --git a/drivers/md/dm-inplace-compress.c b/drivers/md/dm-inplace-compress.c index f6b95e3..bc1cf70 100644 --- a/drivers/md/dm-inplace-compress.c +++ b/drivers/md/dm-inplace-compress.c @@ -810,10 +810,15 @@ static void dm_icomp_release_comp_buffer(struct dm_icomp_io_range *io) { if (!io->comp_data) return; - dm_icomp_kfree(io->comp_data, io->comp_len); + + if (io->comp_kmap) + kunmap(io->comp_data); + else + dm_icomp_kfree(io->comp_data, io->comp_len); io->comp_data = NULL; io->comp_len = 0; + io->comp_kmap = false; } static void dm_icomp_free_io_range(struct dm_icomp_io_range *io) @@ -857,6 +862,39 @@ static void dm_icomp_put_req(struct dm_icomp_req *req) kmem_cache_free(dm_icomp_req_cachep, req); } +static void dm_icomp_bio_copy(struct bio *bio, off_t bio_off, void *buf, + ssize_t len, bool to_buf) +{ + struct bio_vec bv; + struct bvec_iter iter; + off_t buf_off = 0; + ssize_t size; + void *addr; + + WARN_ON(bio_off + len > (bio_sectors(bio) << 9)); + + bio_for_each_segment(bv, bio, iter) { + int length = bv.bv_len; + + if (bio_off >= length) { + bio_off -= length; + continue; + } + addr = kmap_atomic(bv.bv_page); + size = min_t(ssize_t, len, length - bio_off); + if (to_buf) + memcpy(buf + buf_off, addr + bio_off + bv.bv_offset, + size); + else + memcpy(addr + bio_off + bv.bv_offset, buf + buf_off, + size); + kunmap_atomic(addr); + bio_off = 0; + buf_off += size; + len -= size; + } +} + static void dm_icomp_io_range_done(unsigned long error, void *context) { struct dm_icomp_io_range *io = context; @@ -886,7 +924,7 @@ static inline int dm_icomp_compressor_maxlen(struct dm_icomp_info *info, * comp_data */ static struct dm_icomp_io_range *dm_icomp_create_io_range( - struct dm_icomp_req *req, int comp_len) + struct dm_icomp_req *req) { struct dm_icomp_io_range *io; @@ -894,120 +932,67 @@ static struct dm_icomp_io_range *dm_icomp_create_io_range( if (!io) return NULL; - io->comp_data = dm_icomp_kmalloc(comp_len, GFP_NOIO); - if (!io->comp_data) { - kmem_cache_free(dm_icomp_io_range_cachep, io); - return NULL; - } - io->io_req.notify.fn = dm_icomp_io_range_done; io->io_req.notify.context = io; io->io_req.client = req->info->io_client; io->io_req.mem.type = DM_IO_KMEM; - io->io_req.mem.ptr.addr = io->comp_data; io->io_req.mem.offset = 0; io->io_region.bdev = req->info->dev->bdev; - - io->comp_len = comp_len; 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; + io->comp_data = io->decomp_data = io->decomp_real_data = NULL; + io->comp_len = io->decomp_len = io->decomp_req_len = 0; + io->comp_kmap = io->decomp_kmap = false; return io; } static struct dm_icomp_io_range *dm_icomp_create_io_read_range( struct dm_icomp_req *req, int comp_len, int decomp_len) { - struct dm_icomp_io_range *io = dm_icomp_create_io_range(req, comp_len); - - if (io) { - /* note down the requested length for decompress buffer. - * but dont allocate it yet. - */ - io->decomp_req_len = decomp_len; - } - return io; -} + struct bio *bio = req->bio; + sector_t size = bio_sectors(bio)<<9; + int segments = bio_segments(bio); + void *addr; + struct dm_icomp_io_range *io = dm_icomp_create_io_range(req); -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) + return NULL; - if (io->decomp_len) - return 0; + /* try reusing the bio buffer for compress data. */ + if (segments == 1) { + struct bio_vec bv = bio_iovec(bio); - /* 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; + WARN_ON(size < comp_len); + addr = kmap(bv.bv_page); + } else + addr = dm_icomp_kmalloc(comp_len, GFP_NOIO); - 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; + if (!addr) { + kmem_cache_free(dm_icomp_io_range_cachep, io); + return NULL; } + io->comp_data = io->io_req.mem.ptr.addr = addr; + io->comp_len = comp_len; + io->comp_kmap = (segments == 1); + /* note down the requested length for decompress buffer. + * but dont allocate it yet. + / + io->decomp_req_len = decomp_len; + return io; +} - /* none available. :( Allocate one */ +static int dm_icomp_update_io_read_range(struct dm_icomp_io_range *io) +{ 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; } -static void dm_icomp_bio_copy(struct bio *bio, off_t bio_off, void *buf, - ssize_t len, bool to_buf) -{ - struct bio_vec bv; - struct bvec_iter iter; - off_t buf_off = 0; - ssize_t size; - void *addr; - - WARN_ON(bio_off + len > (bio_sectors(bio) << 9)); - - bio_for_each_segment(bv, bio, iter) { - int length = bv.bv_len; - - if (bio_off >= length) { - bio_off -= length; - continue; - } - addr = kmap_atomic(bv.bv_page); - size = min_t(ssize_t, len, length - bio_off); - if (to_buf) - memcpy(buf + buf_off, addr + bio_off + bv.bv_offset, - size); - else - memcpy(addr + bio_off + bv.bv_offset, buf + buf_off, - size); - kunmap_atomic(addr); - bio_off = 0; - buf_off += size; - len -= size; - } -} - static int dm_icomp_mod_to_max_io_range(struct dm_icomp_info *info, struct dm_icomp_io_range *io) { @@ -1030,13 +1015,26 @@ static int dm_icomp_mod_to_max_io_range(struct dm_icomp_info *info, 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; + struct dm_icomp_io_range *io = dm_icomp_create_io_range(req); + + if (!io) + return NULL; + + addr = dm_icomp_kmalloc(comp_len, GFP_NOIO); + if (!addr) { + kmem_cache_free(dm_icomp_io_range_cachep, io); + return NULL; + } + io->comp_data = io->io_req.mem.ptr.addr = addr; + io->comp_len = comp_len; + io->comp_kmap = false; + /* try reusing the bio buffer for decomp data. */ if (segments == 1) { struct bio_vec bv = bio_iovec(bio); @@ -1044,21 +1042,18 @@ static struct dm_icomp_io_range *dm_icomp_create_io_write_range( } else addr = dm_icomp_kmalloc(size, GFP_NOIO); - if (!addr) - return NULL; - - io = dm_icomp_create_io_range(req, comp_len); - if (!io) { - (segments == 1) ? kunmap(addr) : dm_icomp_kfree(addr, size); + if (!addr) { + dm_icomp_kfree(io->comp_data, comp_len); + kmem_cache_free(dm_icomp_io_range_cachep, io); return NULL; } - io->decomp_data = io->decomp_real_data = addr; io->decomp_len = size; io->decomp_kmap = (segments == 1); if (!io->decomp_kmap) dm_icomp_bio_copy(req->bio, 0, io->decomp_data, size, true); + return io; } @@ -1176,7 +1171,7 @@ static void dm_icomp_handle_read_decomp(struct dm_icomp_req *req) 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)) { + if (dm_icomp_update_io_read_range(io)) { req->result = -EIO; return; } diff --git a/drivers/md/dm-inplace-compress.h b/drivers/md/dm-inplace-compress.h index e144e96..775ccbf 100644 --- a/drivers/md/dm-inplace-compress.h +++ b/drivers/md/dm-inplace-compress.h @@ -120,6 +120,7 @@ struct dm_icomp_io_range { 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 */ + int comp_kmap; /* Is the comp_data kmapped'? */ void *comp_data; unsigned int comp_len; /* For write, this is estimated */ struct list_head next; -- 1.7.1 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel