All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] erofs: support compressed fragments data
@ 2022-08-22  5:52 ` Yue Hu
  0 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:52 UTC (permalink / raw)
  To: xiang, chao; +Cc: Yue Hu, linux-erofs, linux-kernel, zhangwen

From: Yue Hu <huyue2@coolpad.com>

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/cover.1661087840.git.huyue2@coolpad.com/

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(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH 0/2] erofs: support compressed fragments data
@ 2022-08-22  5:52 ` Yue Hu
  0 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:52 UTC (permalink / raw)
  To: xiang, chao; +Cc: linux-erofs, linux-kernel, zhangwen, Yue Hu

From: Yue Hu <huyue2@coolpad.com>

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/cover.1661087840.git.huyue2@coolpad.com/

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(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH 1/2] erofs: support on-disk offset for shifted decompression
  2022-08-22  5:52 ` Yue Hu
@ 2022-08-22  5:53   ` Yue Hu
  -1 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:53 UTC (permalink / raw)
  To: xiang, chao; +Cc: Yue Hu, linux-erofs, linux-kernel, zhangwen

From: Yue Hu <huyue2@coolpad.com>

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.

Signed-off-by: Yue Hu <huyue2@coolpad.com>
---
 fs/erofs/decompressor.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index 2d55569f96ac..dc02d95b52d7 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -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);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH 1/2] erofs: support on-disk offset for shifted decompression
@ 2022-08-22  5:53   ` Yue Hu
  0 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:53 UTC (permalink / raw)
  To: xiang, chao; +Cc: linux-erofs, linux-kernel, zhangwen, Yue Hu

From: Yue Hu <huyue2@coolpad.com>

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.

Signed-off-by: Yue Hu <huyue2@coolpad.com>
---
 fs/erofs/decompressor.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index 2d55569f96ac..dc02d95b52d7 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -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);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
  2022-08-22  5:52 ` Yue Hu
@ 2022-08-22  5:53   ` Yue Hu
  -1 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:53 UTC (permalink / raw)
  To: xiang, chao; +Cc: Yue Hu, linux-erofs, linux-kernel, zhangwen

From: Yue Hu <huyue2@coolpad.com>

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.

Signed-off-by: Yue Hu <huyue2@coolpad.com>
---
 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(-)

diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index dc02d95b52d7..fe4a34ac2de7 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -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]) {
diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
index 2b48373f690b..3306cca5f03b 100644
--- a/fs/erofs/erofs_fs.h
+++ b/fs/erofs/erofs_fs.h
@@ -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;
 };
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index cfee49d33b95..7b9d31bab928 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -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 {
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 3173debeaa5a..fde5ece36b2c 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -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);
 }
diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
index c1383e508bbe..1b52395be82a 100644
--- a/fs/erofs/sysfs.c
+++ b/fs/erofs/sysfs.c
@@ -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);
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 5792ca9e0d5e..3dd3b829766f 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -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);
+		if (IS_ERR(src))
+			return PTR_ERR(src);
+
+		dst = kmap_atomic(page);
+		memcpy(dst + pageofs + i, src + erofs_blkoff(offset), cnt);
+		kunmap_atomic(dst);
+
+		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;
+			skip = offset - map->m_la;
+		} else {
+			pageofs = map->m_la & ~PAGE_MASK;
+			skip = 0;
+		}
+		DBG_BUGON(map->m_llen - skip > PAGE_SIZE);
+		err = z_erofs_read_fragment_data(page, pageofs, skip,
+						 map->m_llen - skip);
+		if (err)
+			goto out;
+		++spiltted;
+		tight = false;
+		goto next_part;
+	}
 
 	exclusive = (!cur && (!spiltted || tight));
 	if (cur)
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index d58549ca1df9..5a1a360017ff 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -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;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
@ 2022-08-22  5:53   ` Yue Hu
  0 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-22  5:53 UTC (permalink / raw)
  To: xiang, chao; +Cc: linux-erofs, linux-kernel, zhangwen, Yue Hu

From: Yue Hu <huyue2@coolpad.com>

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.

Signed-off-by: Yue Hu <huyue2@coolpad.com>
---
 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(-)

diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index dc02d95b52d7..fe4a34ac2de7 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -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]) {
diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
index 2b48373f690b..3306cca5f03b 100644
--- a/fs/erofs/erofs_fs.h
+++ b/fs/erofs/erofs_fs.h
@@ -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;
 };
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index cfee49d33b95..7b9d31bab928 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -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 {
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 3173debeaa5a..fde5ece36b2c 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -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);
 }
diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
index c1383e508bbe..1b52395be82a 100644
--- a/fs/erofs/sysfs.c
+++ b/fs/erofs/sysfs.c
@@ -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);
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index 5792ca9e0d5e..3dd3b829766f 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -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);
+		if (IS_ERR(src))
+			return PTR_ERR(src);
+
+		dst = kmap_atomic(page);
+		memcpy(dst + pageofs + i, src + erofs_blkoff(offset), cnt);
+		kunmap_atomic(dst);
+
+		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;
+			skip = offset - map->m_la;
+		} else {
+			pageofs = map->m_la & ~PAGE_MASK;
+			skip = 0;
+		}
+		DBG_BUGON(map->m_llen - skip > PAGE_SIZE);
+		err = z_erofs_read_fragment_data(page, pageofs, skip,
+						 map->m_llen - skip);
+		if (err)
+			goto out;
+		++spiltted;
+		tight = false;
+		goto next_part;
+	}
 
 	exclusive = (!cur && (!spiltted || tight));
 	if (cur)
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index d58549ca1df9..5a1a360017ff 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -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;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
  2022-08-22  5:53   ` Yue Hu
  (?)
@ 2022-08-22 11:42   ` kernel test robot
  -1 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2022-08-22 11:42 UTC (permalink / raw)
  To: Yue Hu; +Cc: llvm, kbuild-all

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/202208221956.uRQu5Di8-lkp@intel.com/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
Reported-by: kernel test robot <lkp@intel.com>

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	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
  2022-08-22  5:53   ` Yue Hu
@ 2022-08-25  3:23     ` Gao Xiang
  -1 siblings, 0 replies; 11+ messages in thread
From: Gao Xiang @ 2022-08-25  3:23 UTC (permalink / raw)
  To: Yue Hu; +Cc: linux-kernel, zhangwen, Yue Hu, linux-erofs

Hi Yue,

On Mon, Aug 22, 2022 at 01:53:01PM +0800, Yue Hu wrote:
> From: Yue Hu <huyue2@coolpad.com>
> 
> 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.
> 
> Signed-off-by: Yue Hu <huyue2@coolpad.com>
> ---
>  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(-)
> 
> diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
> index dc02d95b52d7..fe4a34ac2de7 100644
> --- a/fs/erofs/decompressor.c
> +++ b/fs/erofs/decompressor.c
> @@ -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.

> +		     rq->pageofs_out;
>  
>  	src = kmap_atomic(*rq->in) + rq->pageofs_in;
>  	if (rq->out[0]) {
> diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
> index 2b48373f690b..3306cca5f03b 100644
> --- a/fs/erofs/erofs_fs.h
> +++ b/fs/erofs/erofs_fs.h
> @@ -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;
>  };
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index cfee49d33b95..7b9d31bab928 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -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 {
> diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> index 3173debeaa5a..fde5ece36b2c 100644
> --- a/fs/erofs/super.c
> +++ b/fs/erofs/super.c
> @@ -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?

>  	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);
>  }
> diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
> index c1383e508bbe..1b52395be82a 100644
> --- a/fs/erofs/sysfs.c
> +++ b/fs/erofs/sysfs.c
> @@ -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);
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 5792ca9e0d5e..3dd3b829766f 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -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);
> +		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.

> +
> +		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;
> +			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)?

> +		err = z_erofs_read_fragment_data(page, pageofs, skip,
> +						 map->m_llen - skip);

						 len); ?

Thanks,
Gao Xiang

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
@ 2022-08-25  3:23     ` Gao Xiang
  0 siblings, 0 replies; 11+ messages in thread
From: Gao Xiang @ 2022-08-25  3:23 UTC (permalink / raw)
  To: Yue Hu; +Cc: xiang, chao, linux-erofs, linux-kernel, zhangwen, Yue Hu

Hi Yue,

On Mon, Aug 22, 2022 at 01:53:01PM +0800, Yue Hu wrote:
> From: Yue Hu <huyue2@coolpad.com>
> 
> 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.
> 
> Signed-off-by: Yue Hu <huyue2@coolpad.com>
> ---
>  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(-)
> 
> diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
> index dc02d95b52d7..fe4a34ac2de7 100644
> --- a/fs/erofs/decompressor.c
> +++ b/fs/erofs/decompressor.c
> @@ -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.

> +		     rq->pageofs_out;
>  
>  	src = kmap_atomic(*rq->in) + rq->pageofs_in;
>  	if (rq->out[0]) {
> diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
> index 2b48373f690b..3306cca5f03b 100644
> --- a/fs/erofs/erofs_fs.h
> +++ b/fs/erofs/erofs_fs.h
> @@ -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;
>  };
> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> index cfee49d33b95..7b9d31bab928 100644
> --- a/fs/erofs/internal.h
> +++ b/fs/erofs/internal.h
> @@ -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 {
> diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> index 3173debeaa5a..fde5ece36b2c 100644
> --- a/fs/erofs/super.c
> +++ b/fs/erofs/super.c
> @@ -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?

>  	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);
>  }
> diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
> index c1383e508bbe..1b52395be82a 100644
> --- a/fs/erofs/sysfs.c
> +++ b/fs/erofs/sysfs.c
> @@ -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);
> diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> index 5792ca9e0d5e..3dd3b829766f 100644
> --- a/fs/erofs/zdata.c
> +++ b/fs/erofs/zdata.c
> @@ -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);
> +		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.

> +
> +		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;
> +			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)?

> +		err = z_erofs_read_fragment_data(page, pageofs, skip,
> +						 map->m_llen - skip);

						 len); ?

Thanks,
Gao Xiang

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
  2022-08-25  3:23     ` Gao Xiang
@ 2022-08-25  8:28       ` Yue Hu
  -1 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-25  8:28 UTC (permalink / raw)
  To: Gao Xiang; +Cc: linux-kernel, zhangwen, Yue Hu, linux-erofs

On Thu, 25 Aug 2022 11:23:44 +0800
Gao Xiang <hsiangkao@linux.alibaba.com> wrote:

> Hi Yue,
> 
> On Mon, Aug 22, 2022 at 01:53:01PM +0800, Yue Hu wrote:
> > From: Yue Hu <huyue2@coolpad.com>
> > 
> > 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.
> > 
> > Signed-off-by: Yue Hu <huyue2@coolpad.com>
> > ---
> >  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(-)
> > 
> > diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
> > index dc02d95b52d7..fe4a34ac2de7 100644
> > --- a/fs/erofs/decompressor.c
> > +++ b/fs/erofs/decompressor.c
> > @@ -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.

I will update in v2.

> 
> > +		     rq->pageofs_out;
> >  
> >  	src = kmap_atomic(*rq->in) + rq->pageofs_in;
> >  	if (rq->out[0]) {
> > diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
> > index 2b48373f690b..3306cca5f03b 100644
> > --- a/fs/erofs/erofs_fs.h
> > +++ b/fs/erofs/erofs_fs.h
> > @@ -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;
> >  };
> > diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> > index cfee49d33b95..7b9d31bab928 100644
> > --- a/fs/erofs/internal.h
> > +++ b/fs/erofs/internal.h
> > @@ -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 {
> > diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> > index 3173debeaa5a..fde5ece36b2c 100644
> > --- a/fs/erofs/super.c
> > +++ b/fs/erofs/super.c
> > @@ -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.

> 
> >  	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);
> >  }
> > diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
> > index c1383e508bbe..1b52395be82a 100644
> > --- a/fs/erofs/sysfs.c
> > +++ b/fs/erofs/sysfs.c
> > @@ -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);
> > diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> > index 5792ca9e0d5e..3dd3b829766f 100644
> > --- a/fs/erofs/zdata.c
> > +++ b/fs/erofs/zdata.c
> > @@ -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);
> > +		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.

> 
> > +
> > +		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;
> > +			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.

> 
> > +		err = z_erofs_read_fragment_data(page, pageofs, skip,
> > +						 map->m_llen - skip);  
> 
> 						 len); ?
> 
> Thanks,
> Gao Xiang


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] erofs: add on-disk compressed fragments support
@ 2022-08-25  8:28       ` Yue Hu
  0 siblings, 0 replies; 11+ messages in thread
From: Yue Hu @ 2022-08-25  8:28 UTC (permalink / raw)
  To: Gao Xiang; +Cc: xiang, chao, linux-erofs, linux-kernel, zhangwen, Yue Hu

On Thu, 25 Aug 2022 11:23:44 +0800
Gao Xiang <hsiangkao@linux.alibaba.com> wrote:

> Hi Yue,
> 
> On Mon, Aug 22, 2022 at 01:53:01PM +0800, Yue Hu wrote:
> > From: Yue Hu <huyue2@coolpad.com>
> > 
> > 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.
> > 
> > Signed-off-by: Yue Hu <huyue2@coolpad.com>
> > ---
> >  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(-)
> > 
> > diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
> > index dc02d95b52d7..fe4a34ac2de7 100644
> > --- a/fs/erofs/decompressor.c
> > +++ b/fs/erofs/decompressor.c
> > @@ -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.

I will update in v2.

> 
> > +		     rq->pageofs_out;
> >  
> >  	src = kmap_atomic(*rq->in) + rq->pageofs_in;
> >  	if (rq->out[0]) {
> > diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
> > index 2b48373f690b..3306cca5f03b 100644
> > --- a/fs/erofs/erofs_fs.h
> > +++ b/fs/erofs/erofs_fs.h
> > @@ -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;
> >  };
> > diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
> > index cfee49d33b95..7b9d31bab928 100644
> > --- a/fs/erofs/internal.h
> > +++ b/fs/erofs/internal.h
> > @@ -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 {
> > diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> > index 3173debeaa5a..fde5ece36b2c 100644
> > --- a/fs/erofs/super.c
> > +++ b/fs/erofs/super.c
> > @@ -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.

> 
> >  	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);
> >  }
> > diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c
> > index c1383e508bbe..1b52395be82a 100644
> > --- a/fs/erofs/sysfs.c
> > +++ b/fs/erofs/sysfs.c
> > @@ -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);
> > diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
> > index 5792ca9e0d5e..3dd3b829766f 100644
> > --- a/fs/erofs/zdata.c
> > +++ b/fs/erofs/zdata.c
> > @@ -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);
> > +		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.

> 
> > +
> > +		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;
> > +			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.

> 
> > +		err = z_erofs_read_fragment_data(page, pageofs, skip,
> > +						 map->m_llen - skip);  
> 
> 						 len); ?
> 
> Thanks,
> Gao Xiang


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-08-25  8:27 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-22  5:52 [RFC PATCH 0/2] erofs: support compressed fragments data Yue Hu
2022-08-22  5:52 ` Yue Hu
2022-08-22  5:53 ` [RFC PATCH 1/2] erofs: support on-disk offset for shifted decompression Yue Hu
2022-08-22  5:53   ` Yue Hu
2022-08-22  5:53 ` [RFC PATCH 2/2] erofs: add on-disk compressed fragments support Yue Hu
2022-08-22  5:53   ` Yue Hu
2022-08-22 11:42   ` kernel test robot
2022-08-25  3:23   ` Gao Xiang
2022-08-25  3:23     ` Gao Xiang
2022-08-25  8:28     ` Yue Hu
2022-08-25  8:28       ` Yue Hu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.