From: Gao Xiang <hsiang...@aol.com> Store compressed block count to the compressed index so that EROFS can compress from variable-sized input to variable-sized compressed blocks and make the in-place decompression possible as well.
TODO: support storing compressed block count for compact indexes. Signed-off-by: Gao Xiang <hsiang...@aol.com> --- include/erofs/internal.h | 1 + include/erofs_fs.h | 19 ++++++++--- lib/compress.c | 70 ++++++++++++++++++++++++++-------------- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/include/erofs/internal.h b/include/erofs/internal.h index ac5b270329e2..de307e7f3d8f 100644 --- a/include/erofs/internal.h +++ b/include/erofs/internal.h @@ -104,6 +104,7 @@ static inline void erofs_sb_clear_##name(void) \ } EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING) +EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER) EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM) #define EROFS_I_EA_INITED (1 << 0) diff --git a/include/erofs_fs.h b/include/erofs_fs.h index a69f179a51a5..fa9467a2608c 100644 --- a/include/erofs_fs.h +++ b/include/erofs_fs.h @@ -20,7 +20,10 @@ * be incompatible with this kernel version. */ #define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001 -#define EROFS_ALL_FEATURE_INCOMPAT EROFS_FEATURE_INCOMPAT_LZ4_0PADDING +#define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002 +#define EROFS_ALL_FEATURE_INCOMPAT \ + (EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | \ + EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER) /* 128-byte erofs on-disk super block */ struct erofs_super_block { @@ -201,10 +204,11 @@ enum { * bit 0 : COMPACTED_2B indexes (0 - off; 1 - on) * e.g. for 4k logical cluster size, 4B if compacted 2B is off; * (4B) + 2B + (4B) if compacted 2B is on. + * bit 1 : HEAD1 big pcluster (0 - off; 1 - on) + * bit 2 : (reserved now) HEAD2 big pcluster (0 - off; 1 - on) */ -#define Z_EROFS_ADVISE_COMPACTED_2B_BIT 0 - -#define Z_EROFS_ADVISE_COMPACTED_2B (1 << Z_EROFS_ADVISE_COMPACTED_2B_BIT) +#define Z_EROFS_ADVISE_COMPACTED_2B 0x0001 +#define Z_EROFS_ADVISE_BIG_PCLUSTER_1 0x0002 struct z_erofs_map_header { __le32 h_reserved1; @@ -261,6 +265,13 @@ enum { #define Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS 2 #define Z_EROFS_VLE_DI_CLUSTER_TYPE_BIT 0 +/* + * D0_CBLKCNT will be marked _only_ for the 1st non-head lcluster to + * store the compressed block count of a compressed extent (aka. block + * count of a pcluster). + */ +#define Z_EROFS_VLE_DI_D0_CBLKCNT 0x8000 + struct z_erofs_vle_decompressed_index { __le16 di_advise; /* where to decompress in the head cluster */ diff --git a/lib/compress.c b/lib/compress.c index 86db940b6edd..f340f432c6b7 100644 --- a/lib/compress.c +++ b/lib/compress.c @@ -29,8 +29,8 @@ struct z_erofs_vle_compress_ctx { u8 queue[EROFS_CONFIG_COMPR_MAX_SZ * 2]; unsigned int head, tail; - - erofs_blk_t blkaddr; /* pointing to the next blkaddr */ + unsigned int compressedblks; + erofs_blk_t blkaddr; /* pointing to the next blkaddr */ u16 clusterofs; }; @@ -89,7 +89,13 @@ static void vle_write_indexes(struct z_erofs_vle_compress_ctx *ctx, } do { - if (d0) { + /* XXX: big pcluster feature should be per-inode */ + if (d0 == 1 && cfg.c_physical_clusterblks > 1) { + type = Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD; + di.di_u.delta[0] = cpu_to_le16(ctx->compressedblks | + Z_EROFS_VLE_DI_D0_CBLKCNT); + di.di_u.delta[1] = cpu_to_le16(d1); + } else if (d0) { type = Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD; di.di_u.delta[0] = cpu_to_le16(d0); @@ -115,9 +121,8 @@ static void vle_write_indexes(struct z_erofs_vle_compress_ctx *ctx, ctx->clusterofs = clusterofs + count; } -static int write_uncompressed_block(struct z_erofs_vle_compress_ctx *ctx, - unsigned int *len, - char *dst) +static int write_uncompressed_extent(struct z_erofs_vle_compress_ctx *ctx, + unsigned int *len, char *dst) { int ret; unsigned int count; @@ -148,17 +153,19 @@ static int vle_compress_one(struct erofs_inode *inode, struct z_erofs_vle_compress_ctx *ctx, bool final) { + const unsigned int pclusterblks = cfg.c_physical_clusterblks; + const unsigned int pclustersize = pclusterblks * EROFS_BLKSIZ; struct erofs_compress *const h = &compresshandle; unsigned int len = ctx->tail - ctx->head; unsigned int count; int ret; - static char dstbuf[EROFS_BLKSIZ * 2]; + static char dstbuf[EROFS_CONFIG_COMPR_MAX_SZ + EROFS_BLKSIZ]; char *const dst = dstbuf + EROFS_BLKSIZ; while (len) { bool raw; - if (len <= EROFS_BLKSIZ) { + if (len <= pclustersize) { if (final) goto nocompression; break; @@ -167,7 +174,7 @@ static int vle_compress_one(struct erofs_inode *inode, count = len; ret = erofs_compress_destsize(h, compressionlevel, ctx->queue + ctx->head, - &count, dst, EROFS_BLKSIZ); + &count, dst, pclustersize); if (ret <= 0) { if (ret != -EAGAIN) { erofs_err("failed to compress %s: %s", @@ -175,32 +182,36 @@ static int vle_compress_one(struct erofs_inode *inode, erofs_strerror(ret)); } nocompression: - ret = write_uncompressed_block(ctx, &len, dst); + ret = write_uncompressed_extent(ctx, &len, dst); if (ret < 0) return ret; count = ret; + ctx->compressedblks = 1; raw = true; } else { - /* write compressed data */ - erofs_dbg("Writing %u compressed data to block %u", - count, ctx->blkaddr); + const unsigned int used = ret & (EROFS_BLKSIZ - 1); + const unsigned int margin = + erofs_sb_has_lz4_0padding() && used ? + EROFS_BLKSIZ - used : 0; - if (erofs_sb_has_lz4_0padding()) - ret = blk_write(dst - (EROFS_BLKSIZ - ret), - ctx->blkaddr, 1); - else - ret = blk_write(dst, ctx->blkaddr, 1); + ctx->compressedblks = DIV_ROUND_UP(ret, EROFS_BLKSIZ); + /* write compressed data */ + erofs_dbg("Writing %u compressed data to %u of %u blocks", + count, ctx->blkaddr, ctx->compressedblks); + + ret = blk_write(dst - margin, ctx->blkaddr, + ctx->compressedblks); if (ret) return ret; raw = false; } ctx->head += count; - /* write compression indexes for this blkaddr */ + /* write compression indexes for this pcluster */ vle_write_indexes(ctx, count, raw); - ++ctx->blkaddr; + ctx->blkaddr += ctx->compressedblks; len -= count; if (!final && ctx->head >= EROFS_CONFIG_COMPR_MAX_SZ) { @@ -345,8 +356,6 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode, out = in = inode->compressmeta; - /* write out compacted header */ - memcpy(out, &mapheader, sizeof(mapheader)); out += sizeof(mapheader); in += Z_EROFS_LEGACY_MAP_HEADER_SIZE; @@ -415,6 +424,8 @@ int erofs_write_compressed_file(struct erofs_inode *inode) } memset(compressmeta, 0, Z_EROFS_LEGACY_MAP_HEADER_SIZE); + /* write out compressed header */ + memcpy(compressmeta, &mapheader, sizeof(mapheader)); blkaddr = erofs_mapbh(bh->block, true); /* start_blkaddr */ ctx.blkaddr = blkaddr; @@ -473,7 +484,8 @@ int erofs_write_compressed_file(struct erofs_inode *inode) inode->u.i_blocks = compressed_blocks; legacymetasize = ctx.metacur - compressmeta; - if (cfg.c_legacy_compress) { + /* XXX: temporarily use legacy index instead for mbpcluster */ + if (cfg.c_legacy_compress || cfg.c_physical_clusterblks > 1) { inode->extent_isize = legacymetasize; inode->datalayout = EROFS_INODE_FLAT_COMPRESSION_LEGACY; } else { @@ -531,7 +543,17 @@ int z_erofs_compress_init(void) algorithmtype[0] = ret; /* primary algorithm (head 0) */ algorithmtype[1] = 0; /* secondary algorithm (head 1) */ - mapheader.h_advise |= Z_EROFS_ADVISE_COMPACTED_2B; + mapheader.h_advise = 0; + if (!cfg.c_legacy_compress) + mapheader.h_advise |= Z_EROFS_ADVISE_COMPACTED_2B; + /* + * if big pcluster is enabled, an extra CBLKCNT lcluster index needs + * to be loaded in order to get those compressed block counts. + */ + if (cfg.c_physical_clusterblks > 1) { + erofs_sb_set_big_pcluster(); + mapheader.h_advise |= Z_EROFS_ADVISE_BIG_PCLUSTER_1; + } mapheader.h_algorithmtype = algorithmtype[1] << 4 | algorithmtype[0]; mapheader.h_clusterbits = LOG_BLOCK_SIZE - 12; -- 2.24.0