compressed data will be usually loaded into last pages of the extent (the last page for 4k) for in-place decompression (more specifically, in-place IO), as ilustration below, start of compressed logical extent | end of this logical extent | | ______v___________________________v________ ... | page 6 | page 7 | page 8 | page 9 | ... |__________|__________|__________|__________| . ^ . ^ . |compressed| . | data | . . . |< dstsize >|<margin>| oend iend op ip Therefore, it's possible to do decompression inplace (thus no memcpy at all) if the margin is sufficient and safe enough [1], and it can be implemented only for fixed-size output compression compared with fixed-size input compression. No memcpy for most of in-place IO (about 99% of enwik9) after decompression inplace is implemented and sequential read will be improved of course (see the following patches for test results). [1] https://github.com/lz4/lz4/commit/b17f578a919b7e6b078cede2d52be29dd48c8e8c https://github.com/lz4/lz4/commit/5997e139f53169fa3a1c1b4418d2452a90b01602 Signed-off-by: Gao Xiang <gaoxiang25@xxxxxxxxxx> --- fs/erofs/decompressor.c | 36 ++++++++++++++++++++++++++++++++---- fs/erofs/erofs_fs.h | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c index 2523f6ab552b..e882a8ff0903 100644 --- a/fs/erofs/decompressor.c +++ b/fs/erofs/decompressor.c @@ -15,6 +15,9 @@ #endif #define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1) +#ifndef LZ4_DECOMPRESS_INPLACE_MARGIN +#define LZ4_DECOMPRESS_INPLACE_MARGIN(srcsize) (((srcsize) >> 8) + 32) +#endif struct z_erofs_decompressor { /* @@ -117,7 +120,7 @@ static int lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out) { unsigned int inputmargin, inlen; u8 *src; - bool copied; + bool copied, support_0padding; int ret; if (rq->inputsize > PAGE_SIZE) @@ -125,13 +128,38 @@ static int lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out) src = kmap_atomic(*rq->in); inputmargin = 0; + support_0padding = false; + + /* decompression inplace is only safe when 0padding is enabled */ + if (EROFS_SB(rq->sb)->requirements & EROFS_REQUIREMENT_LZ4_0PADDING) { + support_0padding = true; + + while (!src[inputmargin & ~PAGE_MASK]) + if (!(++inputmargin & ~PAGE_MASK)) + break; + + if (inputmargin >= rq->inputsize) { + kunmap_atomic(src); + return -EIO; + } + } copied = false; inlen = rq->inputsize - inputmargin; if (rq->inplace_io) { - src = generic_copy_inplace_data(rq, src, inputmargin); - inputmargin = 0; - copied = true; + const uint oend = (rq->pageofs_out + + rq->outputsize) & ~PAGE_MASK; + const uint nr = PAGE_ALIGN(rq->pageofs_out + + rq->outputsize) >> PAGE_SHIFT; + + if (rq->partial_decoding || !support_0padding || + rq->out[nr - 1] != rq->in[0] || + rq->inputsize - oend < + LZ4_DECOMPRESS_INPLACE_MARGIN(inlen)) { + src = generic_copy_inplace_data(rq, src, inputmargin); + inputmargin = 0; + copied = true; + } } ret = LZ4_decompress_safe_partial(src + inputmargin, out, diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h index b307060dd220..25bda459f2e6 100644 --- a/fs/erofs/erofs_fs.h +++ b/fs/erofs/erofs_fs.h @@ -17,7 +17,7 @@ * incompatible with this kernel version. */ #define EROFS_REQUIREMENT_LZ4_0PADDING 0x00000001 -#define EROFS_ALL_REQUIREMENTS 0 +#define EROFS_ALL_REQUIREMENTS EROFS_REQUIREMENT_LZ4_0PADDING struct erofs_super_block { /* 0 */__le32 magic; /* in the little endian */ -- 2.17.1