[PATCH 1/3] erofs-utils: determine the [un]compressed data block is inline or not early

Noboru Asai asai at sijam.com
Thu Apr 18 15:52:29 AEST 2024


Introducing erofs_get_inodesize function and erofs_get_lowest_offset function,
we can determine the [un]compressed data block is inline or not before
executing z_erofs_merge_segment function. It enable the following,

* skip the redundant write for ztailpacking block.
* simplify erofs_prepare_inode_buffer function. (remove handling ENOSPC error)

Signed-off-by: Noboru Asai <asai at sijam.com>
---
 include/erofs/inode.h |   2 +
 lib/compress.c        |  11 +++--
 lib/inode.c           | 112 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 116 insertions(+), 9 deletions(-)

diff --git a/include/erofs/inode.h b/include/erofs/inode.h
index d5a732a..fb93b81 100644
--- a/include/erofs/inode.h
+++ b/include/erofs/inode.h
@@ -40,6 +40,8 @@ int __erofs_fill_inode(struct erofs_inode *inode, struct stat *st,
 struct erofs_inode *erofs_new_inode(void);
 struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path);
 struct erofs_inode *erofs_mkfs_build_special_from_fd(int fd, const char *name);
+unsigned int erofs_get_inodesize(struct erofs_inode *inode);
+unsigned int erofs_get_lowest_offset(struct erofs_inode *inode);
 
 #ifdef __cplusplus
 }
diff --git a/lib/compress.c b/lib/compress.c
index 74c5707..dfe59da 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -20,6 +20,7 @@
 #include "erofs/block_list.h"
 #include "erofs/compress_hints.h"
 #include "erofs/fragments.h"
+#include "erofs/inode.h"
 #ifdef EROFS_MT_ENABLED
 #include "erofs/workqueue.h"
 #endif
@@ -530,7 +531,8 @@ static int __z_erofs_compress_one(struct z_erofs_compress_sctx *ctx,
 			e->length = len;
 			goto frag_packing;
 		}
-		if (!may_inline && len <= blksz)
+		if ((!may_inline && len <= blksz) ||
+		    (may_inline && erofs_get_lowest_offset(inode) + len > blksz))
 			goto nocompression;
 	}
 
@@ -550,7 +552,7 @@ static int __z_erofs_compress_one(struct z_erofs_compress_sctx *ctx,
 
 	/* check if there is enough gain to keep the compressed data */
 	if (ret * h->compress_threshold / 100 >= e->length) {
-		if (may_inline && len < blksz) {
+		if (may_inline && (erofs_get_lowest_offset(inode) + len < blksz)) {
 			ret = z_erofs_fill_inline_data(inode,
 					ctx->queue + ctx->head, len, true);
 		} else {
@@ -584,7 +586,8 @@ frag_packing:
 		e->raw = false;
 		ictx->fragemitted = true;
 	/* tailpcluster should be less than 1 block */
-	} else if (may_inline && len == e->length && compressedsize < blksz) {
+	} else if (may_inline && len == e->length &&
+		   (erofs_get_lowest_offset(inode) + compressedsize < blksz)) {
 		if (ctx->clusterofs + len <= blksz) {
 			inode->eof_tailraw = malloc(len);
 			if (!inode->eof_tailraw)
@@ -621,7 +624,7 @@ frag_packing:
 					&e->length, dst, &compressedsize);
 
 		e->compressedblks = BLK_ROUND_UP(sbi, compressedsize);
-		DBG_BUGON(e->compressedblks * blksz >= e->length);
+		DBG_BUGON(!may_inline && e->compressedblks * blksz >= e->length);
 
 		padding = 0;
 		tailused = compressedsize & (blksz - 1);
diff --git a/lib/inode.c b/lib/inode.c
index 7508c74..45d2fbc 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -440,6 +440,11 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 
 	inode->datalayout = EROFS_INODE_FLAT_INLINE;
 	nblocks = inode->i_size / erofs_blksiz(sbi);
+	inode->idata_size = inode->i_size % erofs_blksiz(sbi);
+	if (erofs_get_inodesize(inode) + inode->idata_size > erofs_blksiz(sbi)) {
+		nblocks += 1;
+		inode->idata_size = 0;
+	}
 
 	ret = __allocate_inode_bh_data(inode, nblocks, DATA);
 	if (ret)
@@ -452,7 +457,7 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 		if (ret != erofs_blksiz(sbi)) {
 			if (ret < 0)
 				return -errno;
-			return -EAGAIN;
+			memset(buf + ret, 0, erofs_blksiz(sbi) - ret);
 		}
 
 		ret = blk_write(sbi, buf, inode->u.i_blkaddr + i, 1);
@@ -461,7 +466,6 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 	}
 
 	/* read the tail-end data */
-	inode->idata_size = inode->i_size % erofs_blksiz(sbi);
 	if (inode->idata_size) {
 		inode->idata = malloc(inode->idata_size);
 		if (!inode->idata)
@@ -667,6 +671,104 @@ static int erofs_prepare_tail_block(struct erofs_inode *inode)
 	return 0;
 }
 
+static unsigned int erofs_get_compacted_extent_isize(struct erofs_inode *inode)
+{
+	struct erofs_sb_info *sbi = inode->sbi;
+	const unsigned int mpos = roundup(inode->inode_isize +
+					  inode->xattr_isize, 8) +
+				  sizeof(struct z_erofs_map_header);
+	const unsigned int totalidx = BLK_ROUND_UP(sbi, inode->i_size);
+	/* # of 8-byte units so that it can be aligned with 32 bytes */
+	unsigned int compacted_4b_initial, compacted_4b_end;
+	unsigned int compacted_2b;
+	unsigned int extent_isize = 0;
+
+	if (inode->z_advise & Z_EROFS_ADVISE_COMPACTED_2B) {
+		compacted_4b_initial = (32 - mpos % 32) / 4;
+		if (compacted_4b_initial == 32 / 4)
+			compacted_4b_initial = 0;
+
+		if (compacted_4b_initial > totalidx) {
+			compacted_4b_initial = compacted_2b = 0;
+			compacted_4b_end = totalidx;
+		} else {
+			compacted_2b = rounddown(totalidx -
+						 compacted_4b_initial, 16);
+			compacted_4b_end = totalidx - compacted_4b_initial -
+					   compacted_2b;
+		}
+	} else {
+		compacted_2b = compacted_4b_initial = 0;
+		compacted_4b_end = totalidx;
+	}
+
+	extent_isize += sizeof(struct z_erofs_map_header);
+
+	/* generate compacted_4b_initial */
+	while (compacted_4b_initial) {
+		extent_isize += 4 * 2;
+		compacted_4b_initial -= 2;
+	}
+	DBG_BUGON(compacted_4b_initial);
+
+	/* generate compacted_2b */
+	while (compacted_2b) {
+		extent_isize += 2 * 16;
+		compacted_2b -= 16;
+	}
+	DBG_BUGON(compacted_2b);
+
+	/* generate compacted_4b_end */
+	while (compacted_4b_end > 1) {
+		extent_isize += 4 * 2;
+		compacted_4b_end -= 2;
+	}
+
+	/* generate final compacted_4b_end if needed */
+	if (compacted_4b_end) {
+		extent_isize += 4 * 2;
+	}
+	return extent_isize;
+}
+
+/* datalayout may change in z_erofs_compress_dedupe function,
+ * so use carefully.
+ */
+unsigned int erofs_get_inodesize(struct erofs_inode *inode)
+{
+	struct erofs_sb_info *sbi = inode->sbi;
+	unsigned int inodesize;
+
+	inodesize = inode->inode_isize + inode->xattr_isize;
+
+	if (inode->extent_isize)
+		return roundup(inodesize, 8) + inode->extent_isize;
+
+	switch (inode->datalayout) {
+	case EROFS_INODE_COMPRESSED_FULL:
+		inodesize = roundup(inodesize, 8);
+		inodesize += BLK_ROUND_UP(sbi, inode->i_size) *
+			sizeof(struct z_erofs_lcluster_index) +
+			Z_EROFS_FULL_INDEX_ALIGN(0); /* Z_EROFS_LEGACY_MAP_HEADER_SIZE */
+		break;
+	case EROFS_INODE_COMPRESSED_COMPACT:
+		inodesize = roundup(inodesize, 8);
+		inodesize += erofs_get_compacted_extent_isize(inode);
+		break;
+	default:
+	}
+
+	return inodesize;
+}
+
+unsigned int erofs_get_lowest_offset(struct erofs_inode *inode)
+{
+	struct erofs_sb_info *sbi = inode->sbi;
+	unsigned int blksz = erofs_blksiz(sbi);
+
+	return erofs_get_inodesize(inode) & (blksz - 1);
+}
+
 static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 {
 	unsigned int inodesize;
@@ -674,9 +776,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 
 	DBG_BUGON(inode->bh || inode->bh_inline);
 
-	inodesize = inode->inode_isize + inode->xattr_isize;
-	if (inode->extent_isize)
-		inodesize = roundup(inodesize, 8) + inode->extent_isize;
+	inodesize = erofs_get_inodesize(inode);
 
 	/* TODO: tailpacking inline of chunk-based format isn't finalized */
 	if (inode->datalayout == EROFS_INODE_CHUNK_BASED)
@@ -699,6 +799,8 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 	if (bh == ERR_PTR(-ENOSPC)) {
 		int ret;
 
+		BUG_ON(1);
+
 		if (is_inode_layout_compression(inode))
 			z_erofs_drop_inline_pcluster(inode);
 		else
-- 
2.44.0



More information about the Linux-erofs mailing list