Skip to contentSkip to conversation

erofs: support compressed fragments data

RFC PATCH Superseded Yue Hu 3 participants 6 messages Aug 22, 2022 – Aug 25, 2022 EROFS file system

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Yue Hu

author reviewer
#0 lore raw

cover erofs: support compressed fragments data

From: Yue Hu <[email protected]>

This feature can merge tail of per-file or the whole files into a special inode to achieve greater compression ratio.

mkfs v4: https://lore.kernel.org/all/[email protected]/

Yue Hu (2):
erofs: support on-disk offset for shifted decompression
erofs: add on-disk compressed fragments support

 fs/erofs/decompressor.c | 15 +++++++----
 fs/erofs/erofs_fs.h     | 26 ++++++++++++++-----
 fs/erofs/internal.h     | 16 +++++++++---
 fs/erofs/super.c        |  6 +++++
 fs/erofs/sysfs.c        |  2 ++
 fs/erofs/zdata.c        | 55 ++++++++++++++++++++++++++++++++++++++++-
 fs/erofs/zmap.c         | 40 +++++++++++++++++++++++++++---
 7 files changed, 141 insertions(+), 19 deletions(-)
Signature · 1 line
2.17.1
author reviewer
#1 lore raw

RFC PATCH 1/2 erofs: support on-disk offset for shifted decompression

From: Yue Hu <[email protected]>

Currently, there is no start offset when writing uncompressed data to disk blocks for compressed files. However, we are using in-place I/O which will decrease the number of memory copies a lot if we write it just from an offset of 'pageofs_out'. So, let's support it.

---
 fs/erofs/decompressor.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
fs/erofs/decompressor.c +10 -5
index 2d55569f96ac..dc02d95b52d7 100644
@@ -326,6 +326,7 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
 					     PAGE_SIZE - rq->pageofs_out);
 	const unsigned int lefthalf = rq->outputsize - righthalf;
 	unsigned char *src, *dst;
+	unsigned int headofs_in;
 
 	if (nrpages_out > 2) {
 		DBG_BUGON(1);
@@ -337,21 +338,25 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
 		return 0;
 	}
 
+	/* set it to pageofs_out if fragments feature is used */
+	headofs_in = 0;
+
 	src = kmap_atomic(*rq->in) + rq->pageofs_in;
 	if (rq->out[0]) {
 		dst = kmap_atomic(rq->out[0]);
-		memcpy(dst + rq->pageofs_out, src, righthalf);
+		memcpy(dst + rq->pageofs_out, src + headofs_in, righthalf);
 		kunmap_atomic(dst);
 	}
 
 	if (nrpages_out == 2) {
 		DBG_BUGON(!rq->out[1]);
-		if (rq->out[1] == *rq->in) {
-			memmove(src, src + righthalf, lefthalf);
-		} else {
+		if (rq->out[1] != *rq->in) {
 			dst = kmap_atomic(rq->out[1]);
-			memcpy(dst, src + righthalf, lefthalf);
+			memcpy(dst, src + (headofs_in ? 0 : righthalf),
+			       lefthalf);
 			kunmap_atomic(dst);
+		} else if (!headofs_in) {
+			memmove(src, src + righthalf, lefthalf);
 		}
 	}
 	kunmap_atomic(src);
Signature · 1 line
2.17.1
author reviewer
#2 lore raw

RFC PATCH 2/2 erofs: add on-disk compressed fragments support

In reply to Yue Hu

From: Yue Hu <[email protected]>

Introduce on-disk compressed fragments feature.

This approach adds a new field called `h_fragmentoff' in the per-file compression header to indicate the fragment offset of each tail pcluster or the whole file in the special packed inode.

Like ztailpacking, it will also find and record the 'headlcn' of the tail pcluster when initializing per-inode zmap for making follow-on requests more easy.

Moreover, enable the offset for shifted decompression since we are writing from 'pageofs_ofs' when compressing if this feature is used.

---
 fs/erofs/decompressor.c |  4 +--
 fs/erofs/erofs_fs.h     | 26 ++++++++++++++-----
 fs/erofs/internal.h     | 16 +++++++++---
 fs/erofs/super.c        |  6 +++++
 fs/erofs/sysfs.c        |  2 ++
 fs/erofs/zdata.c        | 55 ++++++++++++++++++++++++++++++++++++++++-
 fs/erofs/zmap.c         | 40 +++++++++++++++++++++++++++---
 7 files changed, 133 insertions(+), 16 deletions(-)
fs/erofs/decompressor.c +2 -2
index dc02d95b52d7..fe4a34ac2de7 100644
@@ -338,8 +338,8 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
 		return 0;
 	}
 
-	/* set it to pageofs_out if fragments feature is used */
-	headofs_in = 0;
+	headofs_in = !erofs_sb_has_fragments(EROFS_SB(rq->sb)) ? 0 :
+		     rq->pageofs_out;
 
 	src = kmap_atomic(*rq->in) + rq->pageofs_in;
 	if (rq->out[0]) {
fs/erofs/erofs_fs.h +20 -6
index 2b48373f690b..3306cca5f03b 100644
@@ -25,6 +25,7 @@
 #define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE	0x00000008
 #define EROFS_FEATURE_INCOMPAT_COMPR_HEAD2	0x00000008
 #define EROFS_FEATURE_INCOMPAT_ZTAILPACKING	0x00000010
+#define EROFS_FEATURE_INCOMPAT_FRAGMENTS	0x00000020
 #define EROFS_ALL_FEATURE_INCOMPAT		\
 	(EROFS_FEATURE_INCOMPAT_ZERO_PADDING | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
@@ -32,7 +33,8 @@
 	 EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \
 	 EROFS_FEATURE_INCOMPAT_DEVICE_TABLE | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 | \
-	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING)
+	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING | \
+	 EROFS_FEATURE_INCOMPAT_FRAGMENTS)
 
 #define EROFS_SB_EXTSLOT_SIZE	16
 
@@ -71,7 +73,9 @@ struct erofs_super_block {
 	} __packed u1;
 	__le16 extra_devices;	/* # of devices besides the primary device */
 	__le16 devt_slotoff;	/* startoff = devt_slotoff * devt_slotsize */
-	__u8 reserved2[38];
+	__u8 reserved[6];
+	__le64 packed_nid;	/* nid of the special packed inode */
+	__u8 reserved2[24];
 };
 
 /*
@@ -295,16 +299,25 @@ struct z_erofs_lzma_cfgs {
  * bit 1 : HEAD1 big pcluster (0 - off; 1 - on)
  * bit 2 : HEAD2 big pcluster (0 - off; 1 - on)
  * bit 3 : tailpacking inline pcluster (0 - off; 1 - on)
+ * bit 4 : fragment pcluster (0 - off; 1 - on)
  */
 #define Z_EROFS_ADVISE_COMPACTED_2B		0x0001
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_1		0x0002
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_2		0x0004
 #define Z_EROFS_ADVISE_INLINE_PCLUSTER		0x0008
+#define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER	0x0010
 
+#define Z_EROFS_FRAGMENT_INODE_BIT              7
 struct z_erofs_map_header {
-	__le16	h_reserved1;
-	/* indicates the encoded size of tailpacking data */
-	__le16  h_idata_size;
+	union {
+		/* direct addressing for fragment offset */
+		__le32  h_fragmentoff;
+		struct {
+			__le16  h_reserved1;
+			/* indicates the encoded size of tailpacking data */
+			__le16  h_idata_size;
+		};
+	};
 	__le16	h_advise;
 	/*
 	 * bit 0-3 : algorithm type of head 1 (logical cluster type 01);
@@ -313,7 +326,8 @@ struct z_erofs_map_header {
 	__u8	h_algorithmtype;
 	/*
 	 * bit 0-2 : logical cluster bits - 12, e.g. 0 for 4096;
-	 * bit 3-7 : reserved.
+	 * bit 3-6 : reserved;
+	 * bit 7   : move the whole file into packed inode or not.
 	 */
 	__u8	h_clusterbits;
 };
fs/erofs/internal.h +13 -3
index cfee49d33b95..7b9d31bab928 100644
@@ -120,6 +120,7 @@ struct erofs_sb_info {
 	struct inode *managed_cache;
 
 	struct erofs_sb_lz4_info lz4;
+	struct inode *packed_inode;
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	struct erofs_dev_context *devs;
 	struct dax_device *dax_dev;
@@ -306,6 +307,7 @@ EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
 EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE)
 EROFS_FEATURE_FUNCS(compr_head2, incompat, INCOMPAT_COMPR_HEAD2)
 EROFS_FEATURE_FUNCS(ztailpacking, incompat, INCOMPAT_ZTAILPACKING)
+EROFS_FEATURE_FUNCS(fragments, incompat, INCOMPAT_FRAGMENTS)
 EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
 
 /* atomic flag definitions */
@@ -341,8 +343,13 @@ struct erofs_inode {
 			unsigned char  z_algorithmtype[2];
 			unsigned char  z_logical_clusterbits;
 			unsigned long  z_tailextent_headlcn;
-			erofs_off_t    z_idataoff;
-			unsigned short z_idata_size;
+			union {
+				struct {
+					erofs_off_t    z_idataoff;
+					unsigned short z_idata_size;
+				};
+				erofs_off_t z_fragmentoff;
+			};
 		};
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	};
@@ -400,6 +407,7 @@ extern const struct address_space_operations z_erofs_aops;
 enum {
 	BH_Encoded = BH_PrivateStart,
 	BH_FullMapped,
+	BH_Fragment,
 };
 
 /* Has a disk mapping */
@@ -410,6 +418,8 @@ enum {
 #define EROFS_MAP_ENCODED	(1 << BH_Encoded)
 /* The length of extent is full */
 #define EROFS_MAP_FULL_MAPPED	(1 << BH_FullMapped)
+/* Located in the special packed inode */
+#define EROFS_MAP_FRAGMENT	(1 << BH_Fragment)
 
 struct erofs_map_blocks {
 	struct erofs_buf buf;
@@ -431,7 +441,7 @@ struct erofs_map_blocks {
 #define EROFS_GET_BLOCKS_FIEMAP	0x0002
 /* Used to map the whole extent if non-negligible data is requested for LZMA */
 #define EROFS_GET_BLOCKS_READMORE	0x0004
-/* Used to map tail extent for tailpacking inline pcluster */
+/* Used to map tail extent for tailpacking inline or fragment pcluster */
 #define EROFS_GET_BLOCKS_FINDTAIL	0x0008
 
 enum {
fs/erofs/super.c +6 -0
index 3173debeaa5a..fde5ece36b2c 100644
@@ -381,6 +381,8 @@ static int erofs_read_superblock(struct super_block *sb)
 #endif
 	sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact));
 	sbi->root_nid = le16_to_cpu(dsb->root_nid);
+	sbi->packed_inode = erofs_sb_has_fragments(sbi) ?
+		erofs_iget(sb, le64_to_cpu(dsb->packed_nid), false) : NULL;
 	sbi->inos = le64_to_cpu(dsb->inos);
 
 	sbi->build_time = le64_to_cpu(dsb->build_time);
@@ -411,6 +413,8 @@ static int erofs_read_superblock(struct super_block *sb)
 		erofs_info(sb, "EXPERIMENTAL compressed inline data feature in use. Use at your own risk!");
 	if (erofs_is_fscache_mode(sb))
 		erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
+	if (erofs_sb_has_fragments(sbi))
+		erofs_info(sb, "EXPERIMENTAL compressed fragments feature in use. Use at your own risk!");
 out:
 	erofs_put_metabuf(&buf);
 	return ret;
@@ -908,6 +912,8 @@ static void erofs_put_super(struct super_block *sb)
 #ifdef CONFIG_EROFS_FS_ZIP
 	iput(sbi->managed_cache);
 	sbi->managed_cache = NULL;
+	iput(sbi->packed_inode);
+	sbi->packed_inode = NULL;
 #endif
 	erofs_fscache_unregister_cookie(&sbi->s_fscache);
 }
fs/erofs/sysfs.c +2 -0
index c1383e508bbe..1b52395be82a 100644
@@ -76,6 +76,7 @@ EROFS_ATTR_FEATURE(device_table);
 EROFS_ATTR_FEATURE(compr_head2);
 EROFS_ATTR_FEATURE(sb_chksum);
 EROFS_ATTR_FEATURE(ztailpacking);
+EROFS_ATTR_FEATURE(fragments);
 
 static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(zero_padding),
@@ -86,6 +87,7 @@ static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(compr_head2),
 	ATTR_LIST(sb_chksum),
 	ATTR_LIST(ztailpacking),
+	ATTR_LIST(fragments),
 	NULL,
 };
 ATTRIBUTE_GROUPS(erofs_feat);
fs/erofs/zdata.c +54 -1

73 lines not on this page · raw message

fs/erofs/zmap.c +36 -4
index d58549ca1df9..5a1a360017ff 100644
@@ -123,6 +123,23 @@ static int z_erofs_fill_inode_lazy(struct inode *inode)
 		if (err < 0)
 			goto out_unlock;
 	}
+	if (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) {
+		vi->z_fragmentoff = le32_to_cpu(h->h_fragmentoff);
+
+		if (h->h_clusterbits >> Z_EROFS_FRAGMENT_INODE_BIT) {
+			vi->z_tailextent_headlcn = 0;
+		} else {
+			struct erofs_map_blocks map = {
+				.buf = __EROFS_BUF_INITIALIZER
+			};
+
+			err = z_erofs_do_map_blocks(inode, &map,
+						    EROFS_GET_BLOCKS_FINDTAIL);
+			erofs_put_metabuf(&map.buf);
+			if (err < 0)
+				goto out_unlock;
+		}
+	}
 	/* paired with smp_mb() at the beginning of the function */
 	smp_mb();
 	set_bit(EROFS_I_Z_INITED_BIT, &vi->flags);
@@ -598,6 +615,7 @@ static int z_erofs_do_map_blocks(struct inode *inode,
 {
 	struct erofs_inode *const vi = EROFS_I(inode);
 	bool ztailpacking = vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER;
+	bool inpacked = vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER;
 	struct z_erofs_maprecorder m = {
 		.inode = inode,
 		.map = map,
@@ -632,9 +650,10 @@ static int z_erofs_do_map_blocks(struct inode *inode,
 			/*
 			 * For ztailpacking files, in order to inline data more
 			 * effectively, special EOF lclusters are now supported
-			 * which can have three parts at most.
+			 * which can have three parts at most, also suitable to
+			 * files with fragment data.
 			 */
-			if (ztailpacking && end > inode->i_size)
+			if ((ztailpacking || inpacked) && end > inode->i_size)
 				end = inode->i_size;
 			break;
 		}
@@ -672,6 +691,9 @@ static int z_erofs_do_map_blocks(struct inode *inode,
 		map->m_flags |= EROFS_MAP_META;
 		map->m_pa = vi->z_idataoff;
 		map->m_plen = vi->z_idata_size;
+	} else if (inpacked && m.lcn == vi->z_tailextent_headlcn) {
+		map->m_flags |= EROFS_MAP_FRAGMENT;
+		DBG_BUGON(!map->m_la);
 	} else {
 		map->m_pa = blknr_to_addr(m.pblk);
 		err = z_erofs_get_extent_compressedlen(&m, initial_lcn);
@@ -709,6 +731,7 @@ int z_erofs_map_blocks_iter(struct inode *inode,
 			    struct erofs_map_blocks *map,
 			    int flags)
 {
+	struct erofs_inode *const vi = EROFS_I(inode);
 	int err = 0;
 
 	trace_z_erofs_map_blocks_iter_enter(inode, map, flags);
@@ -725,11 +748,19 @@ int z_erofs_map_blocks_iter(struct inode *inode,
 	if (err)
 		goto out;
 
+	if ((vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) &&
+	    !vi->z_tailextent_headlcn) {
+		map->m_llen = map->m_la + 1;
+		map->m_la = 0;
+		map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_FRAGMENT;
+		goto out;
+	}
+
 	err = z_erofs_do_map_blocks(inode, map, flags);
 out:
 	trace_z_erofs_map_blocks_iter_exit(inode, map, flags, err);
 
-	/* aggressively BUG_ON iff CONFIG_EROFS_FS_DEBUG is on */
+	/* aggressively BUG_ON if CONFIG_EROFS_FS_DEBUG is on */
 	DBG_BUGON(err < 0 && err != -ENOMEM);
 	return err;
 }
@@ -751,7 +782,8 @@ static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
 	iomap->length = map.m_llen;
 	if (map.m_flags & EROFS_MAP_MAPPED) {
 		iomap->type = IOMAP_MAPPED;
-		iomap->addr = map.m_pa;
+		iomap->addr = map.m_flags & EROFS_MAP_FRAGMENT ?
+			      IOMAP_NULL_ADDR : map.m_pa;
 	} else {
 		iomap->type = IOMAP_HOLE;
 		iomap->addr = IOMAP_NULL_ADDR;
Signature · 1 line
2.17.1
#3 lore raw

Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support

[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in…

Hi Yue,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on xiang-erofs/dev-test]
[also build test ERROR on linus/master v6.0-rc2 next-20220822] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yue-Hu/erofs-support-compressed-fragments-data/20220822-135454
base:   https://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git dev-test
config: i386-randconfig-a003-20220822 (https://download.01.org/0day-ci/archive/20220822/[email protected]/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/fcee3af7935e7bb4544090297b4260c00fe2f9c8
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Yue-Hu/erofs-support-compressed-fragments-data/20220822-135454 git checkout fcee3af7935e7bb4544090297b4260c00fe2f9c8
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash fs/

If you fix the issue, kindly add following tag where applicable

All errors (new ones prefixed by >>):

>> fs/erofs/super.c:384:7: error: no member named 'packed_inode' in 'struct erofs_sb_info'
           sbi->packed_inode = erofs_sb_has_fragments(sbi) ?
           ~~~  ^

1 error generated.

vim +384 fs/erofs/super.c

327
328 static int erofs_read_superblock(struct super_block *sb)
329 {
330 struct erofs_sb_info *sbi;
331 struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
332 struct erofs_super_block *dsb;
333 unsigned int blkszbits;
334 void *data;
335 int ret;
336
337 data = erofs_read_metabuf(&buf, sb, 0, EROFS_KMAP);
338 if (IS_ERR(data)) {
339 erofs_err(sb, "cannot read erofs superblock");
340 return PTR_ERR(data);
341 }
342
343 sbi = EROFS_SB(sb);
344 dsb = (struct erofs_super_block *)(data + EROFS_SUPER_OFFSET);
345
346 ret = -EINVAL;
347 if (le32_to_cpu(dsb->magic) != EROFS_SUPER_MAGIC_V1) {
348 erofs_err(sb, "cannot find valid erofs superblock");
349 goto out;
350 }
351
352 sbi->feature_compat = le32_to_cpu(dsb->feature_compat);
353 if (erofs_sb_has_sb_chksum(sbi)) {
354 ret = erofs_superblock_csum_verify(sb, data);
355 if (ret)
356 goto out;
357 }
358
359 ret = -EINVAL;
360 blkszbits = dsb->blkszbits;
361 /* 9(512 bytes) + LOG_SECTORS_PER_BLOCK == LOG_BLOCK_SIZE */
362 if (blkszbits != LOG_BLOCK_SIZE) {
363 erofs_err(sb, "blkszbits %u isn't supported on this platform",
364 blkszbits);
365 goto out;
366 }
367
368 if (!check_layout_compatibility(sb, dsb))
369 goto out;
370
371 sbi->sb_size = 128 + dsb->sb_extslots * EROFS_SB_EXTSLOT_SIZE;
372 if (sbi->sb_size > EROFS_BLKSIZ) {
373 erofs_err(sb, "invalid sb_extslots %u (more than a fs block)",
374 sbi->sb_size);
375 goto out;
376 }
377 sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks);
378 sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
379 #ifdef CONFIG_EROFS_FS_XATTR
380 sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
381 #endif
382 sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact));
383 sbi->root_nid = le16_to_cpu(dsb->root_nid);

 > 384		sbi->packed_inode = erofs_sb_has_fragments(sbi) ?

385 erofs_iget(sb, le64_to_cpu(dsb->packed_nid), false) : NULL;
386 sbi->inos = le64_to_cpu(dsb->inos);
387
388 sbi->build_time = le64_to_cpu(dsb->build_time);
389 sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
390
391 memcpy(&sb->s_uuid, dsb->uuid, sizeof(dsb->uuid));
392
393 ret = strscpy(sbi->volume_name, dsb->volume_name,
394 sizeof(dsb->volume_name));
395 if (ret < 0) { /* -E2BIG */
396 erofs_err(sb, "bad volume name without NIL terminator");
397 ret = -EFSCORRUPTED;
398 goto out;
399 }
400
401 /* parse on-disk compression configurations */
402 if (erofs_sb_has_compr_cfgs(sbi))
403 ret = erofs_load_compr_cfgs(sb, dsb);
404 else
405 ret = z_erofs_load_lz4_config(sb, dsb, NULL, 0);
406 if (ret < 0)
407 goto out;
408
409 /* handle multiple devices */
410 ret = erofs_scan_devices(sb, dsb);
411
412 if (erofs_sb_has_ztailpacking(sbi))
413 erofs_info(sb, "EXPERIMENTAL compressed inline data feature in use. Use at your own risk!");
414 if (erofs_is_fscache_mode(sb))
415 erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
416 if (erofs_sb_has_fragments(sbi))
417 erofs_info(sb, "EXPERIMENTAL compressed fragments feature in use. Use at your own risk!");
418 out:
419 erofs_put_metabuf(&buf);
420 return ret;
421 }
422

Signature · 2 lines
0-DAY CI Kernel Test Service
https://01.org/lkp
#4 lore raw

Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support

In reply to Yue Hu

Hi Yue,

Quoted 31 lines · Yue Hu

From: Yue Hu <[email protected]>

Introduce on-disk compressed fragments feature.

This approach adds a new field called `h_fragmentoff' in the per-file compression header to indicate the fragment offset of each tail pcluster or the whole file in the special packed inode.

Like ztailpacking, it will also find and record the 'headlcn' of the tail pcluster when initializing per-inode zmap for making follow-on requests more easy.

Moreover, enable the offset for shifted decompression since we are writing from 'pageofs_ofs' when compressing if this feature is used.

---
 fs/erofs/decompressor.c |  4 +--
 fs/erofs/erofs_fs.h     | 26 ++++++++++++++-----
 fs/erofs/internal.h     | 16 +++++++++---
 fs/erofs/super.c        |  6 +++++
 fs/erofs/sysfs.c        |  2 ++
 fs/erofs/zdata.c        | 55 ++++++++++++++++++++++++++++++++++++++++-
 fs/erofs/zmap.c         | 40 +++++++++++++++++++++++++++---
 7 files changed, 133 insertions(+), 16 deletions(-)
fs/erofs/decompressor.c +0 -0
index dc02d95b52d7..fe4a34ac2de7 100644

fs/erofs/decompressor.c static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq, · line 341

Quoted 6 lines
fs/erofs/decompressor.c +1 -2
@@ -338,8 +338,8 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
 		return 0;
 	}
 
-	/* set it to pageofs_out if fragments feature is used */
-	headofs_in = 0;
+	headofs_in = !erofs_sb_has_fragments(EROFS_SB(rq->sb)) ? 0 :

Same as I pointed out in the erofs-utils series.

Quoted 143 lines
fs/erofs/decompressor.c +1 -0
+		     rq->pageofs_out;
 
 	src = kmap_atomic(*rq->in) + rq->pageofs_in;
 	if (rq->out[0]) {
fs/erofs/erofs_fs.h +20 -6
index 2b48373f690b..3306cca5f03b 100644
@@ -25,6 +25,7 @@
 #define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE	0x00000008
 #define EROFS_FEATURE_INCOMPAT_COMPR_HEAD2	0x00000008
 #define EROFS_FEATURE_INCOMPAT_ZTAILPACKING	0x00000010
+#define EROFS_FEATURE_INCOMPAT_FRAGMENTS	0x00000020
 #define EROFS_ALL_FEATURE_INCOMPAT		\
 	(EROFS_FEATURE_INCOMPAT_ZERO_PADDING | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
@@ -32,7 +33,8 @@
 	 EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \
 	 EROFS_FEATURE_INCOMPAT_DEVICE_TABLE | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 | \
-	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING)
+	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING | \
+	 EROFS_FEATURE_INCOMPAT_FRAGMENTS)
 
 #define EROFS_SB_EXTSLOT_SIZE	16
 
@@ -71,7 +73,9 @@ struct erofs_super_block {
 	} __packed u1;
 	__le16 extra_devices;	/* # of devices besides the primary device */
 	__le16 devt_slotoff;	/* startoff = devt_slotoff * devt_slotsize */
-	__u8 reserved2[38];
+	__u8 reserved[6];
+	__le64 packed_nid;	/* nid of the special packed inode */
+	__u8 reserved2[24];
 };
 
 /*
@@ -295,16 +299,25 @@ struct z_erofs_lzma_cfgs {
  * bit 1 : HEAD1 big pcluster (0 - off; 1 - on)
  * bit 2 : HEAD2 big pcluster (0 - off; 1 - on)
  * bit 3 : tailpacking inline pcluster (0 - off; 1 - on)
+ * bit 4 : fragment pcluster (0 - off; 1 - on)
  */
 #define Z_EROFS_ADVISE_COMPACTED_2B		0x0001
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_1		0x0002
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_2		0x0004
 #define Z_EROFS_ADVISE_INLINE_PCLUSTER		0x0008
+#define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER	0x0010
 
+#define Z_EROFS_FRAGMENT_INODE_BIT              7
 struct z_erofs_map_header {
-	__le16	h_reserved1;
-	/* indicates the encoded size of tailpacking data */
-	__le16  h_idata_size;
+	union {
+		/* direct addressing for fragment offset */
+		__le32  h_fragmentoff;
+		struct {
+			__le16  h_reserved1;
+			/* indicates the encoded size of tailpacking data */
+			__le16  h_idata_size;
+		};
+	};
 	__le16	h_advise;
 	/*
 	 * bit 0-3 : algorithm type of head 1 (logical cluster type 01);
@@ -313,7 +326,8 @@ struct z_erofs_map_header {
 	__u8	h_algorithmtype;
 	/*
 	 * bit 0-2 : logical cluster bits - 12, e.g. 0 for 4096;
-	 * bit 3-7 : reserved.
+	 * bit 3-6 : reserved;
+	 * bit 7   : move the whole file into packed inode or not.
 	 */
 	__u8	h_clusterbits;
 };
fs/erofs/internal.h +13 -3
index cfee49d33b95..7b9d31bab928 100644
@@ -120,6 +120,7 @@ struct erofs_sb_info {
 	struct inode *managed_cache;
 
 	struct erofs_sb_lz4_info lz4;
+	struct inode *packed_inode;
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	struct erofs_dev_context *devs;
 	struct dax_device *dax_dev;
@@ -306,6 +307,7 @@ EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
 EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE)
 EROFS_FEATURE_FUNCS(compr_head2, incompat, INCOMPAT_COMPR_HEAD2)
 EROFS_FEATURE_FUNCS(ztailpacking, incompat, INCOMPAT_ZTAILPACKING)
+EROFS_FEATURE_FUNCS(fragments, incompat, INCOMPAT_FRAGMENTS)
 EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
 
 /* atomic flag definitions */
@@ -341,8 +343,13 @@ struct erofs_inode {
 			unsigned char  z_algorithmtype[2];
 			unsigned char  z_logical_clusterbits;
 			unsigned long  z_tailextent_headlcn;
-			erofs_off_t    z_idataoff;
-			unsigned short z_idata_size;
+			union {
+				struct {
+					erofs_off_t    z_idataoff;
+					unsigned short z_idata_size;
+				};
+				erofs_off_t z_fragmentoff;
+			};
 		};
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	};
@@ -400,6 +407,7 @@ extern const struct address_space_operations z_erofs_aops;
 enum {
 	BH_Encoded = BH_PrivateStart,
 	BH_FullMapped,
+	BH_Fragment,
 };
 
 /* Has a disk mapping */
@@ -410,6 +418,8 @@ enum {
 #define EROFS_MAP_ENCODED	(1 << BH_Encoded)
 /* The length of extent is full */
 #define EROFS_MAP_FULL_MAPPED	(1 << BH_FullMapped)
+/* Located in the special packed inode */
+#define EROFS_MAP_FRAGMENT	(1 << BH_Fragment)
 
 struct erofs_map_blocks {
 	struct erofs_buf buf;
@@ -431,7 +441,7 @@ struct erofs_map_blocks {
 #define EROFS_GET_BLOCKS_FIEMAP	0x0002
 /* Used to map the whole extent if non-negligible data is requested for LZMA */
 #define EROFS_GET_BLOCKS_READMORE	0x0004
-/* Used to map tail extent for tailpacking inline pcluster */
+/* Used to map tail extent for tailpacking inline or fragment pcluster */
 #define EROFS_GET_BLOCKS_FINDTAIL	0x0008
 
 enum {
fs/erofs/super.c +0 -0
index 3173debeaa5a..fde5ece36b2c 100644

fs/erofs/super.c static int erofs_read_superblock(struct super_block *sb) · line 385

Quoted 5 lines
fs/erofs/super.c +2 -0
@@ -381,6 +381,8 @@ static int erofs_read_superblock(struct super_block *sb)
 #endif
 	sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact));
 	sbi->root_nid = le16_to_cpu(dsb->root_nid);
+	sbi->packed_inode = erofs_sb_has_fragments(sbi) ?
+		erofs_iget(sb, le64_to_cpu(dsb->packed_nid), false) : NULL;

iget could fail, what will we do next in such case?

Quoted 59 lines
fs/erofs/super.c +4 -0
 	sbi->inos = le64_to_cpu(dsb->inos);
 
 	sbi->build_time = le64_to_cpu(dsb->build_time);
@@ -411,6 +413,8 @@ static int erofs_read_superblock(struct super_block *sb)
 		erofs_info(sb, "EXPERIMENTAL compressed inline data feature in use. Use at your own risk!");
 	if (erofs_is_fscache_mode(sb))
 		erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
+	if (erofs_sb_has_fragments(sbi))
+		erofs_info(sb, "EXPERIMENTAL compressed fragments feature in use. Use at your own risk!");
 out:
 	erofs_put_metabuf(&buf);
 	return ret;
@@ -908,6 +912,8 @@ static void erofs_put_super(struct super_block *sb)
 #ifdef CONFIG_EROFS_FS_ZIP
 	iput(sbi->managed_cache);
 	sbi->managed_cache = NULL;
+	iput(sbi->packed_inode);
+	sbi->packed_inode = NULL;
 #endif
 	erofs_fscache_unregister_cookie(&sbi->s_fscache);
 }
fs/erofs/sysfs.c +2 -0
index c1383e508bbe..1b52395be82a 100644
@@ -76,6 +76,7 @@ EROFS_ATTR_FEATURE(device_table);
 EROFS_ATTR_FEATURE(compr_head2);
 EROFS_ATTR_FEATURE(sb_chksum);
 EROFS_ATTR_FEATURE(ztailpacking);
+EROFS_ATTR_FEATURE(fragments);
 
 static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(zero_padding),
@@ -86,6 +87,7 @@ static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(compr_head2),
 	ATTR_LIST(sb_chksum),
 	ATTR_LIST(ztailpacking),
+	ATTR_LIST(fragments),
 	NULL,
 };
 ATTRIBUTE_GROUPS(erofs_feat);
fs/erofs/zdata.c +10 -0
index 5792ca9e0d5e..3dd3b829766f 100644
@@ -650,6 +650,33 @@ static bool should_alloc_managed_pages(struct z_erofs_decompress_frontend *fe,
 		la < fe->headoffset;
 }
 
+static int z_erofs_read_fragment_data(struct page *page, unsigned int pageofs,
+				      loff_t start, unsigned int len)
+{
+	struct inode *const inode = page->mapping->host;
+	erofs_off_t offset = EROFS_I(inode)->z_fragmentoff + start;
+	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
+	u8 *src, *dst;
+	unsigned int i, cnt;
+
+	for (i = 0; i < len; i += cnt) {

fs/erofs/zdata.c static bool should_alloc_managed_pages(struct z_erofs_decompress_frontend *fe, · line 672

Quoted 10 lines
fs/erofs/zdata.c +10 -0
+		cnt = min(EROFS_BLKSIZ - (unsigned int)erofs_blkoff(offset),
+			  len - i);
+		src = erofs_bread(&buf, EROFS_I_SB(inode)->packed_inode,
+				  erofs_blknr(offset), EROFS_KMAP);
+		if (IS_ERR(src))
+			return PTR_ERR(src);
+
+		dst = kmap_atomic(page);
+		memcpy(dst + pageofs + i, src + erofs_blkoff(offset), cnt);
+		kunmap_atomic(dst);

Use kmap_local_page instead.

Quoted 31 lines
fs/erofs/zdata.c +16 -1
+
+		offset += cnt;
+	}
+	erofs_put_metabuf(&buf);
+	return 0;
+}
+
 static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 				struct page *page, struct page **pagepool)
 {
@@ -688,7 +715,8 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 		/* didn't get a valid pcluster previously (very rare) */
 	}
 
-	if (!(map->m_flags & EROFS_MAP_MAPPED))
+	if (!(map->m_flags & EROFS_MAP_MAPPED) ||
+	    map->m_flags & EROFS_MAP_FRAGMENT)
 		goto hitted;
 
 	err = z_erofs_collector_begin(fe);
@@ -735,6 +763,31 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 		zero_user_segment(page, cur, end);
 		goto next_part;
 	}
+	if (map->m_flags & EROFS_MAP_FRAGMENT) {
+		unsigned int pageofs, skip, len;
+
+		if (!map->m_la) {
+			len = (offset + cur + 1 == inode->i_size) ? (cur + 1) :
+			      end;
+			err = z_erofs_read_fragment_data(page, 0, offset, len);

fs/erofs/zdata.c static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, · line 782

Quoted 10 lines
fs/erofs/zdata.c +10 -0
+			goto out;
+		}
+		if (map->m_la < offset) {
+			pageofs = 0;
+			skip = offset - map->m_la;
+		} else {
+			pageofs = map->m_la & ~PAGE_MASK;
+			skip = 0;
+		}
+		DBG_BUGON(map->m_llen - skip > PAGE_SIZE);
		len = min(map->m_llen - skip, end - cur)?

fs/erofs/zdata.c static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, · line 784

Quoted 2 lines
fs/erofs/zdata.c +2 -0
+		err = z_erofs_read_fragment_data(page, pageofs, skip,
+						 map->m_llen - skip);
						 len); ?

Thanks,
Gao Xiang

author reviewer
#5 lore raw

Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support

Quoted 37 lines · On Thu, 25 Aug 2022 11:23:44 +0800 Gao Xiang <[email protected]>…

Hi Yue,

Quoted 34 lines · Yue Hu

From: Yue Hu <[email protected]>

Introduce on-disk compressed fragments feature.

This approach adds a new field called `h_fragmentoff' in the per-file compression header to indicate the fragment offset of each tail pcluster or the whole file in the special packed inode.

Like ztailpacking, it will also find and record the 'headlcn' of the tail pcluster when initializing per-inode zmap for making follow-on requests more easy.

Moreover, enable the offset for shifted decompression since we are writing from 'pageofs_ofs' when compressing if this feature is used.

---
 fs/erofs/decompressor.c |  4 +--
 fs/erofs/erofs_fs.h     | 26 ++++++++++++++-----
 fs/erofs/internal.h     | 16 +++++++++---
 fs/erofs/super.c        |  6 +++++
 fs/erofs/sysfs.c        |  2 ++
 fs/erofs/zdata.c        | 55 ++++++++++++++++++++++++++++++++++++++++-
 fs/erofs/zmap.c         | 40 +++++++++++++++++++++++++++---
 7 files changed, 133 insertions(+), 16 deletions(-)
fs/erofs/decompressor.c +0 -0
index dc02d95b52d7..fe4a34ac2de7 100644
@@ -338,8 +338,8 @@ static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq,
 		return 0;
 	}
 
Quoted 5 lines

fs/erofs/decompressor.c static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq, · line 341

Quoted 3 lines
fs/erofs/decompressor.c +1 -2
-	/* set it to pageofs_out if fragments feature is used */
-	headofs_in = 0;
+	headofs_in = !erofs_sb_has_fragments(EROFS_SB(rq->sb)) ? 0 :  

Same as I pointed out in the erofs-utils series.

I will update in v2.

Quoted 138 lines
Quoted 138 lines
fs/erofs/decompressor.c +1 -0
+		     rq->pageofs_out;
 
 	src = kmap_atomic(*rq->in) + rq->pageofs_in;
 	if (rq->out[0]) {
fs/erofs/erofs_fs.h +20 -6
index 2b48373f690b..3306cca5f03b 100644
@@ -25,6 +25,7 @@
 #define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE	0x00000008
 #define EROFS_FEATURE_INCOMPAT_COMPR_HEAD2	0x00000008
 #define EROFS_FEATURE_INCOMPAT_ZTAILPACKING	0x00000010
+#define EROFS_FEATURE_INCOMPAT_FRAGMENTS	0x00000020
 #define EROFS_ALL_FEATURE_INCOMPAT		\
 	(EROFS_FEATURE_INCOMPAT_ZERO_PADDING | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
@@ -32,7 +33,8 @@
 	 EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \
 	 EROFS_FEATURE_INCOMPAT_DEVICE_TABLE | \
 	 EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 | \
-	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING)
+	 EROFS_FEATURE_INCOMPAT_ZTAILPACKING | \
+	 EROFS_FEATURE_INCOMPAT_FRAGMENTS)
 
 #define EROFS_SB_EXTSLOT_SIZE	16
 
@@ -71,7 +73,9 @@ struct erofs_super_block {
 	} __packed u1;
 	__le16 extra_devices;	/* # of devices besides the primary device */
 	__le16 devt_slotoff;	/* startoff = devt_slotoff * devt_slotsize */
-	__u8 reserved2[38];
+	__u8 reserved[6];
+	__le64 packed_nid;	/* nid of the special packed inode */
+	__u8 reserved2[24];
 };
 
 /*
@@ -295,16 +299,25 @@ struct z_erofs_lzma_cfgs {
  * bit 1 : HEAD1 big pcluster (0 - off; 1 - on)
  * bit 2 : HEAD2 big pcluster (0 - off; 1 - on)
  * bit 3 : tailpacking inline pcluster (0 - off; 1 - on)
+ * bit 4 : fragment pcluster (0 - off; 1 - on)
  */
 #define Z_EROFS_ADVISE_COMPACTED_2B		0x0001
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_1		0x0002
 #define Z_EROFS_ADVISE_BIG_PCLUSTER_2		0x0004
 #define Z_EROFS_ADVISE_INLINE_PCLUSTER		0x0008
+#define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER	0x0010
 
+#define Z_EROFS_FRAGMENT_INODE_BIT              7
 struct z_erofs_map_header {
-	__le16	h_reserved1;
-	/* indicates the encoded size of tailpacking data */
-	__le16  h_idata_size;
+	union {
+		/* direct addressing for fragment offset */
+		__le32  h_fragmentoff;
+		struct {
+			__le16  h_reserved1;
+			/* indicates the encoded size of tailpacking data */
+			__le16  h_idata_size;
+		};
+	};
 	__le16	h_advise;
 	/*
 	 * bit 0-3 : algorithm type of head 1 (logical cluster type 01);
@@ -313,7 +326,8 @@ struct z_erofs_map_header {
 	__u8	h_algorithmtype;
 	/*
 	 * bit 0-2 : logical cluster bits - 12, e.g. 0 for 4096;
-	 * bit 3-7 : reserved.
+	 * bit 3-6 : reserved;
+	 * bit 7   : move the whole file into packed inode or not.
 	 */
 	__u8	h_clusterbits;
 };
fs/erofs/internal.h +13 -3
index cfee49d33b95..7b9d31bab928 100644
@@ -120,6 +120,7 @@ struct erofs_sb_info {
 	struct inode *managed_cache;
 
 	struct erofs_sb_lz4_info lz4;
+	struct inode *packed_inode;
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	struct erofs_dev_context *devs;
 	struct dax_device *dax_dev;
@@ -306,6 +307,7 @@ EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
 EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE)
 EROFS_FEATURE_FUNCS(compr_head2, incompat, INCOMPAT_COMPR_HEAD2)
 EROFS_FEATURE_FUNCS(ztailpacking, incompat, INCOMPAT_ZTAILPACKING)
+EROFS_FEATURE_FUNCS(fragments, incompat, INCOMPAT_FRAGMENTS)
 EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
 
 /* atomic flag definitions */
@@ -341,8 +343,13 @@ struct erofs_inode {
 			unsigned char  z_algorithmtype[2];
 			unsigned char  z_logical_clusterbits;
 			unsigned long  z_tailextent_headlcn;
-			erofs_off_t    z_idataoff;
-			unsigned short z_idata_size;
+			union {
+				struct {
+					erofs_off_t    z_idataoff;
+					unsigned short z_idata_size;
+				};
+				erofs_off_t z_fragmentoff;
+			};
 		};
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	};
@@ -400,6 +407,7 @@ extern const struct address_space_operations z_erofs_aops;
 enum {
 	BH_Encoded = BH_PrivateStart,
 	BH_FullMapped,
+	BH_Fragment,
 };
 
 /* Has a disk mapping */
@@ -410,6 +418,8 @@ enum {
 #define EROFS_MAP_ENCODED	(1 << BH_Encoded)
 /* The length of extent is full */
 #define EROFS_MAP_FULL_MAPPED	(1 << BH_FullMapped)
+/* Located in the special packed inode */
+#define EROFS_MAP_FRAGMENT	(1 << BH_Fragment)
 
 struct erofs_map_blocks {
 	struct erofs_buf buf;
@@ -431,7 +441,7 @@ struct erofs_map_blocks {
 #define EROFS_GET_BLOCKS_FIEMAP	0x0002
 /* Used to map the whole extent if non-negligible data is requested for LZMA */
 #define EROFS_GET_BLOCKS_READMORE	0x0004
-/* Used to map tail extent for tailpacking inline pcluster */
+/* Used to map tail extent for tailpacking inline or fragment pcluster */
 #define EROFS_GET_BLOCKS_FINDTAIL	0x0008
 
Quoted 13 lines

fs/erofs/super.c static int erofs_read_superblock(struct super_block *sb) · line 385

Quoted 11 lines
fs/erofs/internal.h +0 -0
 enum {
fs/erofs/super.c +2 -0
index 3173debeaa5a..fde5ece36b2c 100644
@@ -381,6 +381,8 @@ static int erofs_read_superblock(struct super_block *sb)
 #endif
 	sbi->islotbits = ilog2(sizeof(struct erofs_inode_compact));
 	sbi->root_nid = le16_to_cpu(dsb->root_nid);
+	sbi->packed_inode = erofs_sb_has_fragments(sbi) ?
+		erofs_iget(sb, le64_to_cpu(dsb->packed_nid), false) : NULL;  

iget could fail, what will we do next in such case?

I will fix it in v2.

Quoted 64 lines
Quoted 64 lines
fs/erofs/super.c +4 -0
 	sbi->inos = le64_to_cpu(dsb->inos);
 
 	sbi->build_time = le64_to_cpu(dsb->build_time);
@@ -411,6 +413,8 @@ static int erofs_read_superblock(struct super_block *sb)
 		erofs_info(sb, "EXPERIMENTAL compressed inline data feature in use. Use at your own risk!");
 	if (erofs_is_fscache_mode(sb))
 		erofs_info(sb, "EXPERIMENTAL fscache-based on-demand read feature in use. Use at your own risk!");
+	if (erofs_sb_has_fragments(sbi))
+		erofs_info(sb, "EXPERIMENTAL compressed fragments feature in use. Use at your own risk!");
 out:
 	erofs_put_metabuf(&buf);
 	return ret;
@@ -908,6 +912,8 @@ static void erofs_put_super(struct super_block *sb)
 #ifdef CONFIG_EROFS_FS_ZIP
 	iput(sbi->managed_cache);
 	sbi->managed_cache = NULL;
+	iput(sbi->packed_inode);
+	sbi->packed_inode = NULL;
 #endif
 	erofs_fscache_unregister_cookie(&sbi->s_fscache);
 }
fs/erofs/sysfs.c +2 -0
index c1383e508bbe..1b52395be82a 100644
@@ -76,6 +76,7 @@ EROFS_ATTR_FEATURE(device_table);
 EROFS_ATTR_FEATURE(compr_head2);
 EROFS_ATTR_FEATURE(sb_chksum);
 EROFS_ATTR_FEATURE(ztailpacking);
+EROFS_ATTR_FEATURE(fragments);
 
 static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(zero_padding),
@@ -86,6 +87,7 @@ static struct attribute *erofs_feat_attrs[] = {
 	ATTR_LIST(compr_head2),
 	ATTR_LIST(sb_chksum),
 	ATTR_LIST(ztailpacking),
+	ATTR_LIST(fragments),
 	NULL,
 };
 ATTRIBUTE_GROUPS(erofs_feat);
fs/erofs/zdata.c +14 -0
index 5792ca9e0d5e..3dd3b829766f 100644
@@ -650,6 +650,33 @@ static bool should_alloc_managed_pages(struct z_erofs_decompress_frontend *fe,
 		la < fe->headoffset;
 }
 
+static int z_erofs_read_fragment_data(struct page *page, unsigned int pageofs,
+				      loff_t start, unsigned int len)
+{
+	struct inode *const inode = page->mapping->host;
+	erofs_off_t offset = EROFS_I(inode)->z_fragmentoff + start;
+	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
+	u8 *src, *dst;
+	unsigned int i, cnt;
+
+	for (i = 0; i < len; i += cnt) {
+		cnt = min(EROFS_BLKSIZ - (unsigned int)erofs_blkoff(offset),
+			  len - i);
+		src = erofs_bread(&buf, EROFS_I_SB(inode)->packed_inode,
+				  erofs_blknr(offset), EROFS_KMAP);
Quoted 8 lines

fs/erofs/zdata.c static bool should_alloc_managed_pages(struct z_erofs_decompress_frontend *fe, · line 672

Quoted 6 lines
fs/erofs/zdata.c +6 -0
+		if (IS_ERR(src))
+			return PTR_ERR(src);
+
+		dst = kmap_atomic(page);
+		memcpy(dst + pageofs + i, src + erofs_blkoff(offset), cnt);
+		kunmap_atomic(dst);  

Use kmap_local_page instead.

Got it.

Quoted 36 lines
Quoted 36 lines
fs/erofs/zdata.c +20 -1
+
+		offset += cnt;
+	}
+	erofs_put_metabuf(&buf);
+	return 0;
+}
+
 static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 				struct page *page, struct page **pagepool)
 {
@@ -688,7 +715,8 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 		/* didn't get a valid pcluster previously (very rare) */
 	}
 
-	if (!(map->m_flags & EROFS_MAP_MAPPED))
+	if (!(map->m_flags & EROFS_MAP_MAPPED) ||
+	    map->m_flags & EROFS_MAP_FRAGMENT)
 		goto hitted;
 
 	err = z_erofs_collector_begin(fe);
@@ -735,6 +763,31 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
 		zero_user_segment(page, cur, end);
 		goto next_part;
 	}
+	if (map->m_flags & EROFS_MAP_FRAGMENT) {
+		unsigned int pageofs, skip, len;
+
+		if (!map->m_la) {
+			len = (offset + cur + 1 == inode->i_size) ? (cur + 1) :
+			      end;
+			err = z_erofs_read_fragment_data(page, 0, offset, len);
+			goto out;
+		}
+		if (map->m_la < offset) {
+			pageofs = 0;
Quoted 8 lines

fs/erofs/zdata.c static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, · line 782

Quoted 6 lines
fs/erofs/zdata.c +6 -0
+			skip = offset - map->m_la;
+		} else {
+			pageofs = map->m_la & ~PAGE_MASK;
+			skip = 0;
+		}
+		DBG_BUGON(map->m_llen - skip > PAGE_SIZE);  
		len = min(map->m_llen - skip, end - cur)?

Nice, let me try.

Thanks.

Quoted 8 lines

fs/erofs/zdata.c static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe, · line 784

Quoted 3 lines
fs/erofs/zdata.c +2 -0
+		err = z_erofs_read_fragment_data(page, pageofs, skip,
+						 map->m_llen - skip);  
						 len); ?

Thanks,
Gao Xiang