[PATCH 3/3] erofs-utils: merge consecutive chunks if possible

Gao Xiang hsiangkao at linux.alibaba.com
Tue Jul 25 03:06:46 AEST 2023


Since EROFS chunk size can be configured on a per-file basis,
let's generate indexes with the best chunk size.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 include/erofs/defs.h |  5 +++++
 lib/blobchunk.c      | 53 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 44af557..20f9741 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -286,6 +286,11 @@ static inline unsigned int fls_long(unsigned long x)
 	return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
 }
 
+static inline unsigned long lowbit(unsigned long n)
+{
+	return n & -n;
+}
+
 /**
  * __roundup_pow_of_two() - round up to nearest power of two
  * @n: value to round up
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index 4619057..4e4295e 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -179,13 +179,47 @@ int erofs_blob_write_chunk_indexes(struct erofs_inode *inode,
 	return dev_write(inode->sbi, inode->chunkindexes, off, inode->extent_isize);
 }
 
+int erofs_blob_mergechunks(struct erofs_inode *inode, unsigned int chunkbits,
+			   unsigned int new_chunkbits)
+{
+	struct erofs_sb_info *sbi = inode->sbi;
+	unsigned int dst, src, unit, count;
+
+	if (new_chunkbits - sbi->blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
+		new_chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + sbi->blkszbits;
+	if (chunkbits >= new_chunkbits)		/* no need to merge */
+		goto out;
+
+	if (inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
+		unit = sizeof(struct erofs_inode_chunk_index);
+	else
+		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
+
+	count = round_up(inode->i_size, 1ULL << new_chunkbits) >> new_chunkbits;
+	for (dst = src = 0; dst < count; ++dst) {
+		*((void **)inode->chunkindexes + dst) =
+			*((void **)inode->chunkindexes + src);
+		src += 1U << (new_chunkbits - chunkbits);
+	}
+
+	DBG_BUGON(count * unit >= inode->extent_isize);
+	inode->extent_isize = count * unit;
+	chunkbits = new_chunkbits;
+out:
+	inode->u.chunkformat = (chunkbits - sbi->blkszbits) |
+		(inode->u.chunkformat & ~EROFS_CHUNK_FORMAT_BLKBITS_MASK);
+	return 0;
+}
+
 int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd)
 {
 	struct erofs_sb_info *sbi = inode->sbi;
 	unsigned int chunkbits = cfg.c_chunkbits;
 	unsigned int count, unit;
+	struct erofs_blobchunk *chunk, *lastch;
 	struct erofs_inode_chunk_index *idx;
 	erofs_off_t pos, len, chunksize;
+	erofs_blk_t lb, minextblks;
 	u8 *chunkdata;
 	int ret;
 
@@ -201,10 +235,9 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd)
 		chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + sbi->blkszbits;
 	chunksize = 1ULL << chunkbits;
 	count = DIV_ROUND_UP(inode->i_size, chunksize);
-	inode->u.chunkformat |= chunkbits - sbi->blkszbits;
+
 	if (multidev)
 		inode->u.chunkformat |= EROFS_CHUNK_FORMAT_INDEXES;
-
 	if (inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
 		unit = sizeof(struct erofs_inode_chunk_index);
 	else
@@ -222,8 +255,9 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd)
 	}
 	idx = inode->chunkindexes;
 
+	lastch = NULL;
+	minextblks = BLK_ROUND_UP(sbi, inode->i_size);
 	for (pos = 0; pos < inode->i_size; pos += len) {
-		struct erofs_blobchunk *chunk;
 #ifdef SEEK_DATA
 		off_t offset = lseek(fd, pos, SEEK_DATA);
 
@@ -247,6 +281,7 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd)
 				pos += chunksize;
 			} while (pos < offset);
 			DBG_BUGON(pos != offset);
+			lastch = NULL;
 			continue;
 		}
 #endif
@@ -263,11 +298,21 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd)
 			ret = PTR_ERR(chunk);
 			goto err;
 		}
+
+		if (lastch && (lastch->device_id != chunk->device_id ||
+		    erofs_pos(sbi, lastch->blkaddr) + lastch->chunksize !=
+		    erofs_pos(sbi, chunk->blkaddr))) {
+			lb = lowbit(pos >> sbi->blkszbits);
+			if (lb && lb < minextblks)
+				minextblks = lb;
+		}
 		*(void **)idx++ = chunk;
+		lastch = chunk;
 	}
 	inode->datalayout = EROFS_INODE_CHUNK_BASED;
 	free(chunkdata);
-	return 0;
+	return erofs_blob_mergechunks(inode, chunkbits,
+				      ilog2(minextblks) + sbi->blkszbits);
 err:
 	free(inode->chunkindexes);
 	inode->chunkindexes = NULL;
-- 
2.24.4



More information about the Linux-erofs mailing list