Skip to contentSkip to conversation

btrfs: introduce a new experimental feature, RAID56_VSL

PATCH In review Qu Wenruo 5 participants 9 messages Sep 3 – Sep 5

Subsystems (2)
lore
Share & tools
lore

Qu Wenruo

author
#0 lore raw

cover btrfs: introduce a new experimental feature, RAID56_VSL

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
This series.

An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
Meaning the minimal write size is 8K for data.

However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
It will have the following requirements:

* Can have up to (datasize / sectorsize) data stripes

* The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

* The full stripe length is always datasize

This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem. And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

If anyone has a better way to address the rename, I'm all ears.

Qu Wenruo (2):
btrfs: split sectorsize into datasize and sectorsize
btrfs: implement a new incompat feature, raid56_vsl

 fs/btrfs/accessors.h                   |   2 +
 fs/btrfs/bio.c                         |  16 +++-
 fs/btrfs/block-group.c                 |   2 +-
 fs/btrfs/btrfs_inode.h                 |  10 +++
 fs/btrfs/compression.c                 |  12 +--
 fs/btrfs/defrag.c                      |  14 +--
 fs/btrfs/delalloc-space.c              |  22 ++---
 fs/btrfs/direct-io.c                   |   6 +-
 fs/btrfs/disk-io.c                     |  43 +++++++---
 fs/btrfs/extent-io-tree.c              |   2 +-
 fs/btrfs/extent-tree.c                 |   4 +-
 fs/btrfs/extent_io.c                   |  62 +++++++-------
 fs/btrfs/extent_map.c                  |   2 +-
 fs/btrfs/fiemap.c                      |   2 +-
 fs/btrfs/file-item.c                   |  22 +++--
 fs/btrfs/file.c                        |  72 ++++++++--------
 fs/btrfs/fs.h                          |  27 ++++--
 fs/btrfs/inode-item.c                  |   6 +-
 fs/btrfs/inode.c                       | 113 +++++++++++++------------
 fs/btrfs/ioctl.c                       |  12 +--
 fs/btrfs/lzo.c                         |  14 +--
 fs/btrfs/reflink.c                     |  16 ++--
 fs/btrfs/relocation.c                  |  16 ++--
 fs/btrfs/send.c                        |   4 +-
 fs/btrfs/subpage.c                     |  96 +++++++++++++++------
 fs/btrfs/subpage.h                     |   2 +-
 fs/btrfs/super.c                       |   8 +-
 fs/btrfs/sysfs.c                       |   8 +-
 fs/btrfs/tests/btrfs-tests.c           |   6 +-
 fs/btrfs/tests/free-space-tree-tests.c |   2 +-
 fs/btrfs/tree-checker.c                |   2 +-
 fs/btrfs/tree-log.c                    |   6 +-
 fs/btrfs/volumes.c                     |   2 +-
 fs/btrfs/zlib.c                        |   6 +-
 fs/btrfs/zoned.c                       |   4 +-
 fs/btrfs/zstd.c                        |   8 +-
 include/uapi/linux/btrfs.h             |  22 +++++
 include/uapi/linux/btrfs_tree.h        |   3 +-
 38 files changed, 403 insertions(+), 273 deletions(-)
Signature · 1 line
2.55.0
author
#1 lore raw

PATCH 1/2 btrfs: split sectorsize into datasize and sectorsize

Currently btrfs uses sectorsize as its minimal data/metadata IO size, it's also the unit where data checksum is calculated.

However for the incoming data_size feature, we want to

The incoming new DATA_IO_SIZE feature will separate minimal data IO size from sector size.
This new change is mostly for the preparation of other incoming features.

It means all things like buffered/direct IO will all be done using btrfs_fs_info::data_size, but when data checksum is involved, everything will still respect btrfs_fs_info::sectorsize.

The minimal data IO size is guaranteed by the fs_info::datasize and datasize_bits, which forces btrfs to alloc folios no smaller than datasize for page cache, and all existing data write paths will respect datasize.

This introduces one special problem for subpage handling, now a data folio can contain less blocks than a metadata folio.

However this is not hard to address, btrfs_blocks_per_folio() can be updated to switch between different bits shift for data and metadata folios.
The way to determine if a folio belongs to a data is that, a data folio is always mapped, and the mapping owner inode is not btree inode. Otherwise the folio belongs to metadata.

---
 fs/btrfs/bio.c                         |  16 +++-
 fs/btrfs/block-group.c                 |   2 +-
 fs/btrfs/btrfs_inode.h                 |  10 +++
 fs/btrfs/compression.c                 |  12 +--
 fs/btrfs/defrag.c                      |  14 +--
 fs/btrfs/delalloc-space.c              |  22 ++---
 fs/btrfs/direct-io.c                   |   6 +-
 fs/btrfs/disk-io.c                     |  20 +++--
 fs/btrfs/extent-io-tree.c              |   2 +-
 fs/btrfs/extent-tree.c                 |   4 +-
 fs/btrfs/extent_io.c                   |  62 +++++++-------
 fs/btrfs/extent_map.c                  |   2 +-
 fs/btrfs/fiemap.c                      |   2 +-
 fs/btrfs/file-item.c                   |  22 +++--
 fs/btrfs/file.c                        |  72 ++++++++--------
 fs/btrfs/fs.h                          |  24 ++++--
 fs/btrfs/inode-item.c                  |   6 +-
 fs/btrfs/inode.c                       | 113 +++++++++++++------------
 fs/btrfs/ioctl.c                       |  12 +--
 fs/btrfs/lzo.c                         |  14 +--
 fs/btrfs/reflink.c                     |  16 ++--
 fs/btrfs/relocation.c                  |  16 ++--
 fs/btrfs/send.c                        |   4 +-
 fs/btrfs/subpage.c                     |  96 +++++++++++++++------
 fs/btrfs/subpage.h                     |   2 +-
 fs/btrfs/super.c                       |   8 +-
 fs/btrfs/sysfs.c                       |   6 +-
 fs/btrfs/tests/btrfs-tests.c           |   6 +-
 fs/btrfs/tests/free-space-tree-tests.c |   2 +-
 fs/btrfs/tree-checker.c                |   2 +-
 fs/btrfs/tree-log.c                    |   6 +-
 fs/btrfs/volumes.c                     |   2 +-
 fs/btrfs/zlib.c                        |   6 +-
 fs/btrfs/zoned.c                       |   4 +-
 fs/btrfs/zstd.c                        |   8 +-
 35 files changed, 355 insertions(+), 266 deletions(-)
fs/btrfs/bio.c +14 -2
index 6efde8c5445d..3a438a2b8247 100644
@@ -858,11 +858,23 @@ static void assert_bbio_alignment(struct btrfs_bio *bbio)
 	struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
 	struct bio_vec bvec;
 	struct bvec_iter iter;
-	const u32 blocksize = fs_info->sectorsize;
-	const u32 alignment = min(blocksize, PAGE_SIZE);
+	u32 blocksize;
+	u32 alignment;
 	const u64 logical = bbio->bio.bi_iter.bi_sector << SECTOR_SHIFT;
 	const u32 length = bbio->bio.bi_iter.bi_size;
 
+	if (btrfs_ino(bbio->inode) == BTRFS_BTREE_INODE_OBJECTID ||
+	    bbio->bio.bi_pool == &btrfs_repair_bioset) {
+		/*
+		 * For metadata or repair bios, the alignment requreiment is
+		 * sectorsize.
+		 */
+		blocksize = fs_info->sectorsize;
+	} else {
+		blocksize = fs_info->datasize;
+	}
+	alignment = min(blocksize, PAGE_SIZE);
+
 	/* The logical and length should still be aligned to blocksize. */
 	ASSERT(IS_ALIGNED(logical, blocksize) && IS_ALIGNED(length, blocksize) &&
 	       length != 0, "root=%llu inode=%llu logical=%llu length=%u",
fs/btrfs/block-group.c +1 -1
index 830460a40e86..7e84cf168710 100644
@@ -528,7 +528,7 @@ static void fragment_free_space(struct btrfs_block_group *block_group)
 	u64 start = block_group->start;
 	u64 len = block_group->length;
 	u64 chunk = block_group->flags & BTRFS_BLOCK_GROUP_METADATA ?
-		fs_info->nodesize : fs_info->sectorsize;
+		fs_info->nodesize : fs_info->datasize;
 	u64 step = chunk << 1;
 
 	while (len > chunk) {
fs/btrfs/btrfs_inode.h +10 -0
index 89e5e9c0c904..102088efe9df 100644
@@ -397,6 +397,16 @@ static inline bool is_data_inode(const struct btrfs_inode *inode)
 	return btrfs_ino(inode) != BTRFS_BTREE_INODE_OBJECTID;
 }
 
+static inline unsigned int btrfs_blocks_per_folio(const struct btrfs_fs_info *fs_info,
+						  const struct folio *folio)
+{
+	struct address_space *mapping = folio_mapping(folio);
+
+	if (mapping && mapping->host && is_data_inode(BTRFS_I(mapping->host)))
+		return folio_size(folio) >> fs_info->datasize_bits;
+	return folio_size(folio) >> fs_info->sectorsize_bits;
+}
+
 static inline void btrfs_mod_outstanding_extents(struct btrfs_inode *inode,
 						 int mod)
 {
fs/btrfs/compression.c +6 -6
index c62b5148d5ac..b6ec7af8e335 100644
@@ -318,8 +318,8 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
 	struct btrfs_inode *inode = ordered->inode;
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
-	ASSERT(IS_ALIGNED(ordered->file_offset, fs_info->sectorsize));
-	ASSERT(IS_ALIGNED(ordered->num_bytes, fs_info->sectorsize));
+	ASSERT(IS_ALIGNED(ordered->file_offset, fs_info->datasize));
+	ASSERT(IS_ALIGNED(ordered->num_bytes, fs_info->datasize));
 	/*
 	 * This flag determines if we should clear the writeback flag from the
 	 * page cache. But this function is only utilized by encoded writes, it
@@ -416,7 +416,7 @@ static noinline int add_ra_bio_folios(struct inode *inode, u64 compressed_end,
 
 			folio_put(folio);
 			sectors_missed += (folio_sz - offset) >>
-					  fs_info->sectorsize_bits;
+					  fs_info->datasize_bits;
 
 			/* Beyond threshold, no need to continue */
 			if (sectors_missed > 4)
@@ -472,7 +472,7 @@ static noinline int add_ra_bio_folios(struct inode *inode, u64 compressed_end,
 		 * to this compressed extent on disk.
 		 */
 		if (!em || cur < em->start ||
-		    (cur + fs_info->sectorsize > btrfs_extent_map_end(em)) ||
+		    (cur + fs_info->datasize > btrfs_extent_map_end(em)) ||
 		    (btrfs_extent_map_block_start(em) >> SECTOR_SHIFT) !=
 		    orig_bio->bi_iter.bi_sector) {
 			btrfs_free_extent_map(em);
@@ -549,7 +549,7 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio)
 
 	/* we need the actual starting offset of this extent in the file */
 	read_lock(&em_tree->lock);
-	em = btrfs_lookup_extent_mapping(em_tree, file_offset, fs_info->sectorsize);
+	em = btrfs_lookup_extent_mapping(em_tree, file_offset, fs_info->datasize);
 	read_unlock(&em_tree->lock);
 	if (!em) {
 		ret = -EIO;
@@ -1078,7 +1078,7 @@ int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
 {
 	struct btrfs_fs_info *fs_info = folio_to_fs_info(dest_folio);
 	struct list_head *workspace;
-	const u32 sectorsize = fs_info->sectorsize;
+	const u32 sectorsize = fs_info->datasize;
 	int ret;
 
 	/*
fs/btrfs/defrag.c +7 -7
index 6ec5dd760d42..ea8fff470387 100644
@@ -263,7 +263,7 @@ static int btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info,
 	if (ret < 0)
 		goto cleanup;
 
-	cur = max(cur + fs_info->sectorsize, range.start);
+	cur = max(cur + fs_info->datasize, range.start);
 	goto again;
 
 cleanup:
@@ -737,7 +737,7 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
 	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
 	struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
 	struct extent_map *em;
-	const u32 sectorsize = BTRFS_I(inode)->root->fs_info->sectorsize;
+	const u32 sectorsize = BTRFS_I(inode)->root->fs_info->datasize;
 
 	/*
 	 * Hopefully we have this extent in the tree already, try without the
@@ -1170,7 +1170,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
 	struct defrag_target_range *tmp;
 	LIST_HEAD(target_list);
 	struct folio AUTO_KFREE(*folios);
-	const u32 sectorsize = inode->root->fs_info->sectorsize;
+	const u32 sectorsize = inode->root->fs_info->datasize;
 	u64 cur = start;
 	const unsigned int nr_pages = ((start + len - 1) >> PAGE_SHIFT) -
 				      (start >> PAGE_SHIFT) + 1;
@@ -1266,7 +1266,7 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
 			      unsigned long max_sectors,
 			      u64 *last_scanned_ret)
 {
-	const u32 sectorsize = inode->root->fs_info->sectorsize;
+	const u32 sectorsize = inode->root->fs_info->datasize;
 	struct defrag_target_range *entry;
 	struct defrag_target_range *tmp;
 	LIST_HEAD(target_list);
@@ -1316,7 +1316,7 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
 		if (ret < 0)
 			break;
 		*sectors_defragged += range_len >>
-				      inode->root->fs_info->sectorsize_bits;
+				      inode->root->fs_info->datasize_bits;
 	}
 out:
 	list_for_each_entry_safe(entry, tmp, &target_list, list)
@@ -1400,8 +1400,8 @@ int btrfs_defrag_file(struct btrfs_inode *inode, struct file_ra_state *ra,
 	}
 
 	/* Align the range */
-	cur = round_down(range->start, fs_info->sectorsize);
-	last_byte = round_up(last_byte, fs_info->sectorsize) - 1;
+	cur = round_down(range->start, fs_info->datasize);
+	last_byte = round_up(last_byte, fs_info->datasize) - 1;
 
 	/*
 	 * Make writeback start from the beginning of the range, so that the
fs/btrfs/delalloc-space.c +11 -11
index d357ed7efd99..53735a75c09c 100644
@@ -130,7 +130,7 @@ int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes)
 	enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_DATA;
 
 	/* Make sure bytes are sectorsize aligned */
-	bytes = ALIGN(bytes, fs_info->sectorsize);
+	bytes = ALIGN(bytes, fs_info->datasize);
 
 	if (btrfs_is_free_space_inode(inode))
 		flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE;
@@ -149,9 +149,9 @@ int btrfs_check_data_free_space(struct btrfs_inode *inode,
 	int ret;
 
 	/* align the range */
-	len = round_up(start + len, fs_info->sectorsize) -
-	      round_down(start, fs_info->sectorsize);
-	start = round_down(start, fs_info->sectorsize);
+	len = round_up(start + len, fs_info->datasize) -
+	      round_down(start, fs_info->datasize);
+	start = round_down(start, fs_info->datasize);
 
 	if (noflush)
 		flush = BTRFS_RESERVE_NO_FLUSH;
@@ -186,7 +186,7 @@ void btrfs_free_reserved_data_space_noquota(struct btrfs_inode *inode, u64 len)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
-	ASSERT(IS_ALIGNED(len, fs_info->sectorsize));
+	ASSERT(IS_ALIGNED(len, fs_info->datasize));
 
 	btrfs_space_info_free_bytes_may_use(data_sinfo_for_inode(inode), len);
 }
@@ -204,9 +204,9 @@ void btrfs_free_reserved_data_space(struct btrfs_inode *inode,
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
 	/* Make sure the range is aligned to sectorsize */
-	len = round_up(start + len, fs_info->sectorsize) -
-	      round_down(start, fs_info->sectorsize);
-	start = round_down(start, fs_info->sectorsize);
+	len = round_up(start + len, fs_info->datasize) -
+	      round_down(start, fs_info->datasize);
+	start = round_down(start, fs_info->datasize);
 
 	btrfs_free_reserved_data_space_noquota(inode, len);
 	btrfs_qgroup_free_data(inode, reserved, start, len, NULL);
@@ -341,8 +341,8 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes,
 			flush = BTRFS_RESERVE_FLUSH_LIMIT;
 	}
 
-	num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
-	disk_num_bytes = ALIGN(disk_num_bytes, fs_info->sectorsize);
+	num_bytes = ALIGN(num_bytes, fs_info->datasize);
+	disk_num_bytes = ALIGN(disk_num_bytes, fs_info->datasize);
 
 	/*
 	 * We always want to do it this way, every other way is wrong and ends
@@ -409,7 +409,7 @@ void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes,
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 
-	num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
+	num_bytes = ALIGN(num_bytes, fs_info->datasize);
 	spin_lock(&inode->lock);
 	if (!(inode->flags & BTRFS_INODE_NODATASUM))
 		inode->csum_bytes -= num_bytes;
fs/btrfs/direct-io.c +3 -3
index 3075d7992713..18937303dc3a 100644
@@ -185,7 +185,7 @@ static struct extent_map *btrfs_new_extent_direct(struct btrfs_inode *inode,
 
 	alloc_hint = btrfs_get_extent_allocation_hint(inode, start, len);
 again:
-	ret = btrfs_reserve_extent(root, len, len, fs_info->sectorsize,
+	ret = btrfs_reserve_extent(root, len, len, fs_info->datasize,
 				   0, alloc_hint, &ins, true, true);
 	if (ret == -EAGAIN) {
 		ASSERT(btrfs_is_zoned(fs_info));
@@ -401,7 +401,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start,
 	 * to allocate a contiguous array for the checksums.
 	 */
 	if (!write)
-		len = min_t(u64, len, fs_info->sectorsize * BIO_MAX_VECS);
+		len = min_t(u64, len, fs_info->datasize * BIO_MAX_VECS);
 
 	lockstart = start;
 	lockend = start + len - 1;
@@ -865,7 +865,7 @@ static struct iomap_dio *btrfs_dio_write(struct kiocb *iocb, struct iov_iter *it
 static ssize_t check_direct_IO(struct btrfs_fs_info *fs_info,
 			       const struct iov_iter *iter, loff_t offset)
 {
-	const u32 blocksize_mask = fs_info->sectorsize - 1;
+	const u32 blocksize_mask = fs_info->datasize - 1;
 
 	if (offset & blocksize_mask)
 		return -EINVAL;
fs/btrfs/disk-io.c +12 -8
index dd00e8ced883..42e05e26c442 100644
@@ -2918,7 +2918,9 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
 
 	/* Usable values until the real ones are cached from the superblock */
 	fs_info->nodesize = 4096;
+	fs_info->datasize = 4096;
 	fs_info->sectorsize = 4096;
+	fs_info->datasize_bits = ilog2(4096);
 	fs_info->sectorsize_bits = ilog2(4096);
 
 	/* Default compress algorithm when user does -o compress */
@@ -3231,10 +3233,10 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
 
 	/* Runtime limitation for mixed block groups. */
 	if ((incompat & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) &&
-	    (fs_info->sectorsize != fs_info->nodesize)) {
+	    (fs_info->datasize != fs_info->nodesize)) {
 		btrfs_err(fs_info,
 "unequal nodesize/sectorsize (%u != %u) are not allowed for mixed block groups",
-			fs_info->nodesize, fs_info->sectorsize);
+			fs_info->nodesize, fs_info->datasize);
 		return -EINVAL;
 	}
 
@@ -3293,10 +3295,10 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
 	 * we're already defaulting to v2 cache, no need to bother v1 as it's
 	 * going to be deprecated anyway.
 	 */
-	if (fs_info->sectorsize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) {
+	if (fs_info->datasize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) {
 		btrfs_warn(fs_info,
 	"v1 space cache is not supported for page size %lu with sectorsize %u",
-			   PAGE_SIZE, fs_info->sectorsize);
+			   PAGE_SIZE, fs_info->datasize);
 		return -EINVAL;
 	}
 
@@ -3495,7 +3497,9 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 
 	fs_info->nodesize = nodesize;
 	fs_info->nodesize_bits = ilog2(nodesize);
+	fs_info->datasize = sectorsize;
 	fs_info->sectorsize = sectorsize;
+	fs_info->datasize_bits = ilog2(sectorsize);
 	fs_info->sectorsize_bits = ilog2(sectorsize);
 	fs_info->block_min_order = ilog2(round_up(sectorsize, PAGE_SIZE) >> PAGE_SHIFT);
 	/*
@@ -3506,14 +3510,14 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 	if (IS_ENABLED(CONFIG_HIGHMEM))
 		fs_info->block_max_order = fs_info->block_min_order;
 	else
-		fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits);
+		fs_info->block_max_order = calc_block_max_order(fs_info->datasize_bits);
 	fs_info->csums_per_leaf = BTRFS_MAX_ITEM_SIZE(fs_info) / fs_info->csum_size;
 	fs_info->fs_devices->fs_info = fs_info;
 
-	if (fs_info->sectorsize > PAGE_SIZE)
+	if (fs_info->datasize > PAGE_SIZE)
 		btrfs_warn(fs_info,
 			   "support for block size %u with page size %lu is experimental, some features may be missing",
-			   fs_info->sectorsize, PAGE_SIZE);
+			   fs_info->datasize, PAGE_SIZE);
 	/*
 	 * Handle the space caching options appropriately now that we have the
 	 * super block loaded and validated.
@@ -3543,7 +3547,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 	 * At this point our mount options are validated, if we set ->max_inline
 	 * to something non-standard make sure we truncate it to sectorsize.
 	 */
-	fs_info->max_inline = min_t(u64, fs_info->max_inline, fs_info->sectorsize);
+	fs_info->max_inline = min_t(u64, fs_info->max_inline, fs_info->datasize);
 
 	ret = btrfs_alloc_compress_wsm(fs_info);
 	if (ret)
fs/btrfs/extent-io-tree.c +1 -1
index d6df11f6088c..da0c72f0674f 100644
@@ -342,7 +342,7 @@ static void validate_extent_state(const struct extent_io_tree *tree,
 	if (tree->owner != IO_TREE_INODE_IO)
 		return;
 
-	blocksize = btrfs_extent_io_tree_to_fs_info(tree)->sectorsize;
+	blocksize = btrfs_extent_io_tree_to_fs_info(tree)->datasize;
 	ASSERT(IS_ALIGNED(state->start, blocksize) &&
 	       IS_ALIGNED(state->end + 1, blocksize),
 	       "unaligned extent state, blocksize=%u start=%llu end=%llu state=0x%x",
fs/btrfs/extent-tree.c +2 -2
index 8977e9ad0d44..e63ae4d76b58 100644
@@ -4883,6 +4883,7 @@ int btrfs_reserve_extent(struct btrfs_root *root, u64 ram_bytes,
 	struct find_free_extent_ctl ffe_ctl = {};
 	bool final_tried = num_bytes == min_alloc_size;
 	u64 flags;
+	const u32 blocksize = is_data ? fs_info->datasize : fs_info->sectorsize;
 	int ret;
 	bool for_treelog = (btrfs_root_id(root) == BTRFS_TREE_LOG_OBJECTID);
 	bool for_data_reloc = (btrfs_is_data_reloc_root(root) && is_data);
@@ -4907,8 +4908,7 @@ int btrfs_reserve_extent(struct btrfs_root *root, u64 ram_bytes,
 	} else if (ret == -ENOSPC) {
 		if (!final_tried && ins->offset) {
 			num_bytes = min(num_bytes >> 1, ins->offset);
-			num_bytes = round_down(num_bytes,
-					       fs_info->sectorsize);
+			num_bytes = round_down(num_bytes, blocksize);
 			num_bytes = max(num_bytes, min_alloc_size);
 			ram_bytes = num_bytes;
 			if (num_bytes == min_alloc_size)
fs/btrfs/extent_io.c +31 -31

203 lines not on this page · raw message

fs/btrfs/extent_map.c +1 -1
index 86d9c6f5ff4b..8eae7656bb88 100644
@@ -319,7 +319,7 @@ static void dump_extent_map(struct btrfs_fs_info *fs_info, const char *prefix,
 /* Internal sanity checks for btrfs debug builds. */
 static void validate_extent_map(struct btrfs_fs_info *fs_info, struct extent_map *em)
 {
-	const u32 blocksize = fs_info->sectorsize;
+	const u32 blocksize = fs_info->datasize;
 
 	if (!IS_ENABLED(CONFIG_BTRFS_DEBUG))
 		return;
fs/btrfs/fiemap.c +1 -1
index 7a2a97180099..988148bc35e6 100644
@@ -641,7 +641,7 @@ static int extent_fiemap(struct btrfs_inode *inode,
 	u64 prev_extent_end;
 	u64 range_start;
 	u64 range_end;
-	const u32 sectorsize = inode->root->fs_info->sectorsize;
+	const u32 sectorsize = inode->root->fs_info->datasize;
 	bool stopped = false;
 	int ret;
 
fs/btrfs/file-item.c +14 -8
index 72ebd7c9ef10..3ef1f3ed4ba1 100644
@@ -89,7 +89,7 @@ int btrfs_inode_set_file_extent_range(struct btrfs_inode *inode, u64 start,
 	if (len == 0)
 		return 0;
 
-	ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->sectorsize));
+	ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->datasize));
 
 	return btrfs_set_extent_bit(inode->file_extent_tree, start, start + len - 1,
 				    EXTENT_DIRTY, NULL);
@@ -118,7 +118,7 @@ int btrfs_inode_clear_file_extent_range(struct btrfs_inode *inode, u64 start,
 	if (len == 0)
 		return 0;
 
-	ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->sectorsize) ||
+	ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->datasize) ||
 	       len == (u64)-1);
 
 	return btrfs_clear_extent_bit(inode->file_extent_tree, start,
@@ -491,10 +491,16 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
 			count = 1;
 
 			if (btrfs_is_data_reloc_root(inode->root)) {
-				u64 file_offset = bbio->file_offset + bio_offset;
+				/*
+				 * For @datasize > @sectorsize case, we need to
+				 * keep the range to be @datasize aligned.
+				 * So expand the range to cover the full @datasize.
+				 */
+				u64 file_offset = round_down(bbio->file_offset + bio_offset,
+							     fs_info->datasize);
 
 				btrfs_set_extent_bit(&inode->io_tree, file_offset,
-						     file_offset + sectorsize - 1,
+						     file_offset + fs_info->datasize - 1,
 						     EXTENT_NODATASUM, NULL);
 			} else {
 				btrfs_warn_rl(fs_info,
@@ -680,8 +686,8 @@ int btrfs_lookup_csums_bitmap(struct btrfs_root *root, struct btrfs_path *path,
 	bool free_path = false;
 	int ret;
 
-	ASSERT(IS_ALIGNED(start, fs_info->sectorsize) &&
-	       IS_ALIGNED(end + 1, fs_info->sectorsize));
+	ASSERT(IS_ALIGNED(start, fs_info->datasize) &&
+	       IS_ALIGNED(end + 1, fs_info->datasize));
 
 	if (!path) {
 		path = btrfs_alloc_path();
@@ -1388,7 +1394,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 
 		em->disk_bytenr = EXTENT_MAP_INLINE;
 		em->start = 0;
-		em->len = fs_info->sectorsize;
+		em->len = fs_info->datasize;
 		em->offset = 0;
 		btrfs_extent_map_set_compression(em, compress_type);
 	} else {
@@ -1417,7 +1423,7 @@ u64 btrfs_file_extent_end(const struct btrfs_path *path)
 	fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
 
 	if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)
-		end = leaf->fs_info->sectorsize;
+		end = leaf->fs_info->datasize;
 	else
 		end = key.offset + btrfs_file_extent_num_bytes(leaf, fi);
 
fs/btrfs/file.c +36 -36

222 lines not on this page · raw message

fs/btrfs/fs.h +16 -8
index 3eba8438593c..a99aa3a7b4c7 100644
@@ -886,9 +886,23 @@ struct btrfs_fs_info {
 	/* Cached block sizes */
 	u32 nodesize;
 	u32 nodesize_bits;
+
+	/*
+	 * The sectorsize from super block, also the unit of data checksum
+	 * calculation.
+	 */
 	u32 sectorsize;
-	/* ilog2 of sectorsize, use to avoid 64bit division */
-	u32 sectorsize_bits;
+
+	/*
+	 * The minimal data IO size, must equal to sectorsize or power-of-2
+	 * time of sectorsize.
+	 */
+	u32 datasize;
+
+	/* ilog2 of corresponding sizes, use to avoid 64bit division */
+	u16 datasize_bits;
+	u16 sectorsize_bits;
+
 	u32 block_min_order;
 	u32 block_max_order;
 	u32 writeback_bio_size;
@@ -1078,12 +1092,6 @@ static inline u32 count_max_extents(const struct btrfs_fs_info *fs_info, u64 siz
 	return div_u64(size + fs_info->max_extent_size - 1, fs_info->max_extent_size);
 }
 
-static inline unsigned int btrfs_blocks_per_folio(const struct btrfs_fs_info *fs_info,
-						  const struct folio *folio)
-{
-	return folio_size(folio) >> fs_info->sectorsize_bits;
-}
-
 bool __attribute_const__ btrfs_supported_blocksize(u32 blocksize);
 bool btrfs_exclop_start(struct btrfs_fs_info *fs_info,
 			enum btrfs_exclusive_operation type);
fs/btrfs/inode-item.c +3 -3
index a864f8c99729..703d762ad88f 100644
@@ -565,8 +565,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
 					btrfs_file_extent_num_bytes(leaf, fi);
 				extent_num_bytes = ALIGN(new_size -
 						found_key.offset,
-						fs_info->sectorsize);
-				clear_start = ALIGN(new_size, fs_info->sectorsize);
+						fs_info->datasize);
+				clear_start = ALIGN(new_size, fs_info->datasize);
 
 				btrfs_set_file_extent_num_bytes(leaf, fi,
 							 extent_num_bytes);
@@ -612,7 +612,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
 				 * them as a full sector worth in the file
 				 * extent tree just for simplicity sake.
 				 */
-				clear_len = fs_info->sectorsize;
+				clear_len = fs_info->datasize;
 			}
 
 			control->sub_bytes += item_end + 1 - new_size;
fs/btrfs/inode.c +61 -52

381 lines not on this page · raw message

fs/btrfs/ioctl.c +6 -6
index 54960351fbd1..0359f71b16db 100644
@@ -2724,8 +2724,8 @@ static long btrfs_ioctl_fs_info(const struct btrfs_fs_info *fs_info,
 
 	memcpy(&fi_args->fsid, fs_devices->fsid, sizeof(fi_args->fsid));
 	fi_args->nodesize = fs_info->nodesize;
-	fi_args->sectorsize = fs_info->sectorsize;
-	fi_args->clone_alignment = fs_info->sectorsize;
+	fi_args->sectorsize = fs_info->datasize;
+	fi_args->clone_alignment = fs_info->datasize;
 
 	if (flags_in & BTRFS_FS_INFO_FLAG_CSUM_INFO) {
 		fi_args->csum_type = btrfs_super_csum_type(fs_info->super_copy);
@@ -4421,7 +4421,7 @@ static int btrfs_ioctl_encoded_read(struct file *file, void __user *argp,
 		bool unlocked = false;
 		u64 start, lockend, count;
 
-		start = ALIGN_DOWN(kiocb.ki_pos, fs_info->sectorsize);
+		start = ALIGN_DOWN(kiocb.ki_pos, fs_info->datasize);
 		lockend = start + BTRFS_MAX_UNCOMPRESSED - 1;
 
 		if (args.compression)
@@ -4831,7 +4831,7 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issue
 	if (issue_flags & IO_URING_F_NONBLOCK)
 		kiocb.ki_flags |= IOCB_NOWAIT;
 
-	start = ALIGN_DOWN(pos, fs_info->sectorsize);
+	start = ALIGN_DOWN(pos, fs_info->datasize);
 	lockend = start + BTRFS_MAX_UNCOMPRESSED - 1;
 
 	ret = btrfs_encoded_read(&kiocb, &data->iter, &data->args, &cached_state,
@@ -5276,8 +5276,8 @@ static int btrfs_ioctl_get_csums(struct file *file, void __user *argp)
 	if (copy_from_user(&args, argp, sizeof(args)))
 		return -EFAULT;
 
-	if (!IS_ALIGNED(args.offset, fs_info->sectorsize) ||
-	    !IS_ALIGNED(args.length, fs_info->sectorsize))
+	if (!IS_ALIGNED(args.offset, fs_info->datasize) ||
+	    !IS_ALIGNED(args.length, fs_info->datasize))
 		return -EINVAL;
 	if (args.length == 0)
 		return -EINVAL;
fs/btrfs/lzo.c +7 -7
index 2f0996692da0..16b31345df45 100644
@@ -67,11 +67,11 @@ struct workspace {
 
 static u32 workspace_buf_length(const struct btrfs_fs_info *fs_info)
 {
-	return lzo1x_worst_compress(fs_info->sectorsize);
+	return lzo1x_worst_compress(fs_info->datasize);
 }
 static u32 workspace_cbuf_length(const struct btrfs_fs_info *fs_info)
 {
-	return lzo1x_worst_compress(fs_info->sectorsize);
+	return lzo1x_worst_compress(fs_info->datasize);
 }
 
 void lzo_free_workspace(struct list_head *ws)
@@ -180,8 +180,8 @@ static int copy_compressed_data_to_bio(struct btrfs_fs_info *fs_info,
 				       struct folio **out_folio,
 				       u32 *total_out, u32 max_out)
 {
-	const u32 sectorsize = fs_info->sectorsize;
-	const u32 sectorsize_bits = fs_info->sectorsize_bits;
+	const u32 sectorsize = fs_info->datasize;
+	const u32 sectorsize_bits = fs_info->datasize_bits;
 	const u32 fsize = btrfs_min_folio_size(fs_info);
 	const u32 old_size = out_bio->bi_iter.bi_size;
 	u32 copy_start;
@@ -265,7 +265,7 @@ int lzo_compress_bio(struct list_head *ws, struct compressed_bio *cb)
 	struct bio *bio = &cb->bbio.bio;
 	const u64 start = cb->start;
 	const u32 len = cb->len;
-	const u32 sectorsize = fs_info->sectorsize;
+	const u32 sectorsize = fs_info->datasize;
 	const u32 min_folio_size = btrfs_min_folio_size(fs_info);
 	struct address_space *mapping = inode->vfs_inode.i_mapping;
 	struct folio *folio_in = NULL;
@@ -414,7 +414,7 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
 {
 	struct workspace *workspace = list_entry(ws, struct workspace, list);
 	struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info;
-	const u32 sectorsize = fs_info->sectorsize;
+	const u32 sectorsize = fs_info->datasize;
 	const u32 compressed_len = bio_get_size(&cb->bbio.bio);
 	struct folio_iter fi;
 	char *kaddr;
@@ -546,7 +546,7 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
 {
 	struct workspace *workspace = list_entry(ws, struct workspace, list);
 	struct btrfs_fs_info *fs_info = folio_to_fs_info(dest_folio);
-	const u32 sectorsize = fs_info->sectorsize;
+	const u32 sectorsize = fs_info->datasize;
 	size_t in_len;
 	size_t out_len;
 	size_t max_segment_len = workspace_buf_length(fs_info);
fs/btrfs/reflink.c +8 -8
index d2a4101912bd..3a79a82aa3b9 100644
@@ -61,7 +61,7 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
 			       const u8 comp_type)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
-	const u32 block_size = fs_info->sectorsize;
+	const u32 block_size = fs_info->datasize;
 	const u64 range_end = file_offset + block_size - 1;
 	const size_t inline_size = size - btrfs_file_extent_calc_inline_size(0);
 	char *data_start = inline_data + btrfs_file_extent_calc_inline_size(0);
@@ -175,7 +175,7 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode,
 	struct btrfs_root *root = inode->root;
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	const u64 aligned_end = ALIGN(new_key->offset + datal,
-				      fs_info->sectorsize);
+				      fs_info->datasize);
 	struct btrfs_trans_handle *trans = NULL;
 	struct btrfs_drop_extents_args drop_args = { 0 };
 	int ret;
@@ -573,10 +573,10 @@ static int btrfs_clone(struct btrfs_inode *src, struct btrfs_inode *inode,
 			 * the i_size (which implies the whole inlined data).
 			 */
 			ASSERT(key.offset == 0);
-			ASSERT(datal <= fs_info->sectorsize);
+			ASSERT(datal <= fs_info->datasize);
 			if (WARN_ON(type != BTRFS_FILE_EXTENT_INLINE) ||
 			    WARN_ON(key.offset != 0) ||
-			    WARN_ON(datal > fs_info->sectorsize)) {
+			    WARN_ON(datal > fs_info->datasize)) {
 				ret = -EUCLEAN;
 				goto out;
 			}
@@ -609,7 +609,7 @@ static int btrfs_clone(struct btrfs_inode *src, struct btrfs_inode *inode,
 		inode->last_reflink_trans = trans->transid;
 
 		last_dest_end = ALIGN(new_key.offset + datal,
-				      fs_info->sectorsize);
+				      fs_info->datasize);
 		ret = clone_finish_inode_update(trans, inode, last_dest_end,
 						destoff, olen, no_time_update);
 		if (ret)
@@ -689,7 +689,7 @@ static int btrfs_extent_same_range(struct btrfs_inode *src, u64 loff, u64 len,
 {
 	struct extent_state *cached_state = NULL;
 	struct btrfs_fs_info *fs_info = src->root->fs_info;
-	const u32 bs = fs_info->sectorsize;
+	const u32 bs = fs_info->datasize;
 	const u64 end = round_up(dst_loff + len, bs) - 1;
 	int ret;
 
@@ -761,7 +761,7 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
 	const u64 inode_isize = inode->vfs_inode.i_size;
 	int ret;
 	u64 len = olen;
-	const u32 bs = fs_info->sectorsize;
+	const u32 bs = fs_info->datasize;
 	u64 end;
 
 	/*
@@ -839,7 +839,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 {
 	struct btrfs_inode *inode_in = BTRFS_I(file_inode(file_in));
 	struct btrfs_inode *inode_out = BTRFS_I(file_inode(file_out));
-	const u32 bs = inode_out->root->fs_info->sectorsize;
+	const u32 bs = inode_out->root->fs_info->datasize;
 	u64 wb_len;
 	int ret;
 
fs/btrfs/relocation.c +8 -8
index da54db75e7a9..1fefe0470f68 100644
@@ -996,8 +996,8 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
 				end = key.offset +
 				      btrfs_file_extent_num_bytes(leaf, fi);
 				WARN_ON(!IS_ALIGNED(key.offset,
-						    fs_info->sectorsize));
-				WARN_ON(!IS_ALIGNED(end, fs_info->sectorsize));
+						    fs_info->datasize));
+				WARN_ON(!IS_ALIGNED(end, fs_info->datasize));
 				end--;
 				/* Take mmap lock to serialize with reflinks. */
 				if (!down_read_trylock(&inode->i_mmap_lock))
@@ -1447,7 +1447,7 @@ static int invalidate_extent_cache(struct btrfs_root *root,
 				start = 0;
 			else {
 				start = min_key->offset;
-				WARN_ON(!IS_ALIGNED(start, fs_info->sectorsize));
+				WARN_ON(!IS_ALIGNED(start, fs_info->datasize));
 			}
 		} else {
 			start = 0;
@@ -1462,7 +1462,7 @@ static int invalidate_extent_cache(struct btrfs_root *root,
 				if (max_key->offset == 0)
 					continue;
 				end = max_key->offset;
-				WARN_ON(!IS_ALIGNED(end, fs_info->sectorsize));
+				WARN_ON(!IS_ALIGNED(end, fs_info->datasize));
 				end--;
 			}
 		} else {
@@ -3024,7 +3024,7 @@ static int relocate_one_folio(struct reloc_control *rc,
 			u64 boundary_start = cluster->boundary[*cluster_nr] -
 						offset;
 			u64 boundary_end = boundary_start +
-					   fs_info->sectorsize - 1;
+					   fs_info->datasize - 1;
 
 			btrfs_set_extent_bit(&BTRFS_I(inode)->io_tree,
 					     boundary_start, boundary_end,
@@ -4284,7 +4284,7 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
 	spin_unlock(&sinfo->lock);
 
 	if (is_data)
-		min_size = fs_info->sectorsize;
+		min_size = fs_info->datasize;
 	else
 		min_size = fs_info->nodesize;
 
@@ -4637,7 +4637,7 @@ static int create_remap_tree_entries(struct btrfs_trans_handle *trans,
 
 				read_extent_buffer(leaf, bitmap, offset, data_size);
 
-				parse_bitmap(fs_info->sectorsize, bitmap,
+				parse_bitmap(fs_info->datasize, bitmap,
 					     data_size * BITS_PER_BYTE,
 					     found_key.objectid, space_runs,
 					     &num_space_runs);
@@ -5104,7 +5104,7 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
 	spin_unlock(&sinfo->lock);
 
 	if (is_data)
-		min_size = fs_info->sectorsize;
+		min_size = fs_info->datasize;
 	else
 		min_size = fs_info->nodesize;
 
fs/btrfs/send.c +2 -2
index 5c59b9abedcd..63ca3e436480 100644
@@ -5783,7 +5783,7 @@ static int clone_range(struct send_ctx *sctx, struct btrfs_path *dst_path,
 	 * filesystem has.
 	 */
 	if (clone_root->offset == 0 &&
-	    len == sctx->send_root->fs_info->sectorsize)
+	    len == sctx->send_root->fs_info->datasize)
 		return send_extent_data(sctx, dst_path, offset, len);
 
 	path = alloc_path_for_send();
@@ -6032,7 +6032,7 @@ static int send_write_or_clone(struct send_ctx *sctx,
 	int ret = 0;
 	u64 offset = key->offset;
 	u64 end;
-	const u32 bs = sctx->send_root->fs_info->sectorsize;
+	const u32 bs = sctx->send_root->fs_info->datasize;
 	struct btrfs_file_extent_item *ei;
 	u64 disk_byte;
 	u64 data_offset;
fs/btrfs/subpage.c +68 -28

257 lines not on this page · raw message

fs/btrfs/subpage.h +1 -1
index 9b106a73d682..10c0d4b670f2 100644
@@ -100,7 +100,7 @@ static inline bool btrfs_is_subpage(const struct btrfs_fs_info *fs_info,
 {
 	if (folio->mapping && folio->mapping->host)
 		ASSERT(is_data_inode(BTRFS_I(folio->mapping->host)));
-	return fs_info->sectorsize < folio_size(folio);
+	return fs_info->datasize < folio_size(folio);
 }
 
 int btrfs_attach_folio_state(const struct btrfs_fs_info *fs_info,
fs/btrfs/super.c +4 -4
index 464129b1b0d4..5b5e0667c18b 100644
@@ -729,10 +729,10 @@ bool btrfs_check_options(const struct btrfs_fs_info *info,
  */
 void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info)
 {
-	if (fs_info->sectorsize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) {
+	if (fs_info->datasize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) {
 		btrfs_info(fs_info,
 			   "forcing free space tree for sector size %u with page size %lu",
-			   fs_info->sectorsize, PAGE_SIZE);
+			   fs_info->datasize, PAGE_SIZE);
 		btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE);
 		btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE);
 	}
@@ -1725,7 +1725,7 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	u64 total_used = 0;
 	u64 total_free_data = 0;
 	u64 total_free_meta = 0;
-	u32 bits = fs_info->sectorsize_bits;
+	u32 bits = fs_info->datasize_bits;
 	__be32 *fsid;
 	unsigned factor = 1;
 	struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
@@ -1811,7 +1811,7 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 		buf->f_bavail = 0;
 
 	buf->f_type = BTRFS_SUPER_MAGIC;
-	buf->f_bsize = fs_info->sectorsize;
+	buf->f_bsize = fs_info->datasize;
 	buf->f_namelen = BTRFS_NAME_LEN;
 
 	/*
fs/btrfs/sysfs.c +3 -3
index dbd99f8b6698..277d2f4cc12a 100644
@@ -1022,7 +1022,7 @@ static ssize_t btrfs_sectorsize_show(struct kobject *kobj,
 {
 	struct btrfs_fs_info *fs_info = to_fs_info(kobj);
 
-	return sysfs_emit(buf, "%u\n", fs_info->sectorsize);
+	return sysfs_emit(buf, "%u\n", fs_info->datasize);
 }
 
 BTRFS_ATTR(, sectorsize, btrfs_sectorsize_show);
@@ -1082,7 +1082,7 @@ static ssize_t btrfs_clone_alignment_show(struct kobject *kobj,
 {
 	struct btrfs_fs_info *fs_info = to_fs_info(kobj);
 
-	return sysfs_emit(buf, "%u\n", fs_info->sectorsize);
+	return sysfs_emit(buf, "%u\n", fs_info->datasize);
 }
 
 BTRFS_ATTR(, clone_alignment, btrfs_clone_alignment_show);
@@ -1329,7 +1329,7 @@ static ssize_t btrfs_read_policy_store(struct kobject *kobj,
 
 	if (index == BTRFS_READ_POLICY_RR) {
 		if (value != -1) {
-			const u32 sectorsize = fs_devices->fs_info->sectorsize;
+			const u32 sectorsize = fs_devices->fs_info->datasize;
 
 			if (!IS_ALIGNED(value, sectorsize)) {
 				u64 temp_value = round_up(value, sectorsize);
fs/btrfs/tests/btrfs-tests.c +3 -3
index 6287d940323d..d56f2cebfd6f 100644
@@ -138,8 +138,8 @@ struct btrfs_fs_info *btrfs_alloc_dummy_fs_info(u32 nodesize, u32 sectorsize)
 	btrfs_init_fs_info(fs_info);
 
 	fs_info->nodesize = nodesize;
-	fs_info->sectorsize = sectorsize;
-	fs_info->sectorsize_bits = ilog2(sectorsize);
+	fs_info->datasize = sectorsize;
+	fs_info->datasize_bits = ilog2(sectorsize);
 
 	/* CRC32C csum size. */
 	fs_info->csum_size = 4;
@@ -217,7 +217,7 @@ btrfs_alloc_dummy_block_group(struct btrfs_fs_info *fs_info,
 
 	cache->start = 0;
 	cache->length = length;
-	cache->full_stripe_len = fs_info->sectorsize;
+	cache->full_stripe_len = fs_info->datasize;
 	cache->fs_info = fs_info;
 
 	INIT_LIST_HEAD(&cache->list);
fs/btrfs/tests/free-space-tree-tests.c +1 -1
index 8dee057f41fd..191444addf7d 100644
@@ -68,7 +68,7 @@ static int __check_free_space_extents(struct btrfs_trans_handle *trans,
 					i++;
 				}
 				prev_bit = bit;
-				offset += fs_info->sectorsize;
+				offset += fs_info->datasize;
 			}
 		}
 		if (prev_bit == 1) {
fs/btrfs/tree-checker.c +1 -1
index 7ca904f96621..49156a6f8ecf 100644
@@ -126,7 +126,7 @@ static u64 file_extent_end(struct extent_buffer *leaf,
 
 	if (btrfs_file_extent_type(leaf, extent) == BTRFS_FILE_EXTENT_INLINE) {
 		len = btrfs_file_extent_ram_bytes(leaf, extent);
-		end = ALIGN(key->offset + len, leaf->fs_info->sectorsize);
+		end = ALIGN(key->offset + len, leaf->fs_info->datasize);
 	} else {
 		len = btrfs_file_extent_num_bytes(leaf, extent);
 		end = key->offset + len;
fs/btrfs/tree-log.c +3 -3
index 7ba7b6098aa5..62f90ccdeef1 100644
@@ -717,7 +717,7 @@ static noinline int replay_one_extent(struct walk_control *wc)
 			nbytes = btrfs_file_extent_num_bytes(wc->log_leaf, item);
 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
 		nbytes = btrfs_file_extent_ram_bytes(wc->log_leaf, item);
-		extent_end = ALIGN(start + nbytes, fs_info->sectorsize);
+		extent_end = ALIGN(start + nbytes, fs_info->datasize);
 	} else {
 		btrfs_abort_log_replay(wc, -EUCLEAN,
 		       "unexpected extent type=%d root=%llu inode=%llu offset=%llu",
@@ -2852,7 +2852,7 @@ static int replay_one_buffer(struct extent_buffer *eb,
 					break;
 				}
 				from = ALIGN(i_size_read(&inode->vfs_inode),
-					     root->fs_info->sectorsize);
+					     root->fs_info->datasize);
 				drop_args.start = from;
 				drop_args.end = (u64)-1;
 				drop_args.drop_cache = true;
@@ -5667,7 +5667,7 @@ static int btrfs_log_holes(struct btrfs_trans_handle *trans,
 		u64 hole_len;
 
 		btrfs_release_path(path);
-		hole_len = ALIGN(i_size - prev_extent_end, fs_info->sectorsize);
+		hole_len = ALIGN(i_size - prev_extent_end, fs_info->datasize);
 		ret = btrfs_insert_hole_extent(trans, root->log_root, ino,
 					       prev_extent_end, hole_len);
 		if (ret < 0)
fs/btrfs/volumes.c +1 -1
index 949e40baff33..540f81c810ea 100644
@@ -8359,7 +8359,7 @@ int btrfs_init_writeback_bio_size(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
 	struct btrfs_device *device;
-	u32 writeback_bio_size = fs_info->sectorsize;
+	u32 writeback_bio_size = fs_info->datasize;
 
 	mutex_lock(&fs_devices->device_list_mutex);
 	/*
fs/btrfs/zlib.c +3 -3
index 486b52db583e..e1e077c64917 100644
@@ -90,8 +90,8 @@ struct list_head *zlib_alloc_workspace(struct btrfs_fs_info *fs_info, unsigned i
 		workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE;
 	}
 	if (!workspace->buf) {
-		workspace->buf = kmalloc(fs_info->sectorsize, GFP_KERNEL);
-		workspace->buf_size = fs_info->sectorsize;
+		workspace->buf = kmalloc(fs_info->datasize, GFP_KERNEL);
+		workspace->buf_size = fs_info->datasize;
 	}
 	if (!workspace->strm.workspace || !workspace->buf)
 		goto fail;
@@ -238,7 +238,7 @@ int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb)
 		}
 
 		/* We're making it bigger, give up. */
-		if (workspace->strm.total_in > fs_info->sectorsize * 2 &&
+		if (workspace->strm.total_in > fs_info->datasize * 2 &&
 		    workspace->strm.total_in < workspace->strm.total_out) {
 			ret = -E2BIG;
 			goto out;
fs/btrfs/zoned.c +2 -2
index 9d448cdd60c4..4569c818cbdf 100644
@@ -780,7 +780,7 @@ int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info)
 		min3((u64)lim->max_zone_append_sectors << SECTOR_SHIFT,
 		     (u64)lim->max_sectors << SECTOR_SHIFT,
 		     (u64)lim->max_segments << PAGE_SHIFT),
-		fs_info->sectorsize);
+		fs_info->datasize);
 	fs_info->fs_devices->chunk_alloc_policy = BTRFS_CHUNK_ALLOC_ZONED;
 
 	fs_info->max_extent_size = min_not_zero(fs_info->max_extent_size,
@@ -2722,7 +2722,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng
 
 	/* No MIXED_BG on zoned btrfs. */
 	if (block_group->flags & BTRFS_BLOCK_GROUP_DATA)
-		min_alloc_bytes = fs_info->sectorsize;
+		min_alloc_bytes = fs_info->datasize;
 	else
 		min_alloc_bytes = fs_info->nodesize;
 
fs/btrfs/zstd.c +4 -4
index cb15cbd737c4..d23ac367a0a3 100644
@@ -391,7 +391,7 @@ struct list_head *zstd_alloc_workspace(struct btrfs_fs_info *fs_info, int level)
 	workspace->req_level = level;
 	workspace->last_used = jiffies;
 	workspace->mem = kvmalloc(workspace->size, GFP_KERNEL | __GFP_NOWARN);
-	workspace->buf = kmalloc(fs_info->sectorsize, GFP_KERNEL);
+	workspace->buf = kmalloc(fs_info->datasize, GFP_KERNEL);
 	if (!workspace->mem || !workspace->buf)
 		goto fail;
 
@@ -470,7 +470,7 @@ int zstd_compress_bio(struct list_head *ws, struct compressed_bio *cb)
 		}
 
 		/* Check to see if we are making it bigger. */
-		if (tot_in + workspace->in_buf.pos > fs_info->sectorsize * 2 &&
+		if (tot_in + workspace->in_buf.pos > fs_info->datasize * 2 &&
 		    tot_in + workspace->in_buf.pos < tot_out + workspace->out_buf.pos) {
 			ret = -E2BIG;
 			goto out;
@@ -626,7 +626,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
 
 	workspace->out_buf.dst = workspace->buf;
 	workspace->out_buf.pos = 0;
-	workspace->out_buf.size = fs_info->sectorsize;
+	workspace->out_buf.size = fs_info->datasize;
 
 	while (1) {
 		size_t ret2;
@@ -711,7 +711,7 @@ int zstd_decompress(struct list_head *ws, const u8 *data_in,
 
 	workspace->out_buf.dst = workspace->buf;
 	workspace->out_buf.pos = 0;
-	workspace->out_buf.size = fs_info->sectorsize;
+	workspace->out_buf.size = fs_info->datasize;
 
 	/*
 	 * Since both input and output buffers should not exceed one sector,
Signature · 1 line
2.55.0
author
#2 lore raw

PATCH 2/2 btrfs: implement a new incompat feature, raid56_vsl

In reply to Qu Wenruo

The new feature is a new, and hopefully simpler solution to raid56 write hole.

The VSL part stands for Variable Stripe Length, the whole feature is in fact two features combined:

- Data size feature
This forces all data reads and writes to follow @datasize from the super block.
And @datasize is power of 2 times @sectorsize, and must be larger than @sectorsize.

Meanwhile data checksum is still calculated based on @sectorsize.

This @datasize can only be specified at mkfs time, there is no plan to provide offline conversion to this feature.

- RAID56_VSL chunks
The new RAID56_VSL chunks will get rid of the fixed 64K stripe length, but uses a stripe length and device numbers so that the full stripe length is always matching the above @datasize.

This will allow every data write to be full stripe aligned, thus completely resolve the write hole problem, at least for COW writes.

The cost is that the number of devices for each RAID56_VSL chunk must be power of 2 + P/Q device(s).

This means at mkfs time one has to determine the maximum number of devices the btrfs will support.
Please note that, the btrfs can still have more than (datasize/sectorsize + 1/2) devices, it's the RAID56_VSL will not spread the data beyond (datasize/sectorsize) for each chunk.

So for a datasize 64K and sectorsize 4K, the maximum device number is 17 (RAID5) or 18 (RAID6), which should be reasonable enough for most use cases.

For now, this patch only implements the data size part for testing.

However if the new feature is enabled, some features will be disabled/changed:

- LZO compression
Since our data write path is using datasize, LZO compression will also require datasize as the minimal block size.
But an older kernel will still treat the LZO payload using sectorsize, causing data read errors, as LZO payload is data size dependent.

- Mixed block group support
Since data size is larger than sector size, thus it will not match nodesize, thus causing mount failure.

- Reported blocksize through super block/stat/statx
The reported block size will be datasize, not sectorsize anymore, since our data IO size is datasize.

This means for most cases, the fs acts if it has a larger block size.

---
 fs/btrfs/accessors.h            |  2 ++
 fs/btrfs/disk-io.c              | 27 ++++++++++++++++++++-------
 fs/btrfs/fs.h                   |  3 ++-
 fs/btrfs/sysfs.c                |  2 ++
 include/uapi/linux/btrfs.h      | 22 ++++++++++++++++++++++
 include/uapi/linux/btrfs_tree.h |  3 ++-
 6 files changed, 50 insertions(+), 9 deletions(-)
fs/btrfs/accessors.h +2 -0
index 8938357fcb40..85edd861c093 100644
@@ -889,6 +889,8 @@ BTRFS_SETGET_STACK_FUNCS(super_remap_root_generation, struct btrfs_super_block,
 			 remap_root_generation, 64);
 BTRFS_SETGET_STACK_FUNCS(super_remap_root_level, struct btrfs_super_block,
 			 remap_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_data_size, struct btrfs_super_block,
+			 data_size, 32);
 
 /* struct btrfs_file_extent_item */
 BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item,
fs/btrfs/disk-io.c +20 -7
index 42e05e26c442..aaba6e64504b 100644
@@ -2528,6 +2528,16 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info,
 		ret = -EINVAL;
 	}
 
+	if (btrfs_fs_incompat(fs_info, RAID56_VSL)) {
+		const u32 data_size = btrfs_super_data_size(sb);
+
+		if (unlikely(!is_power_of_2(data_size) || data_size <= sectorsize)) {
+			btrfs_err(fs_info,
+		"invalid data size, has %u expect power of 2 values larger than %u",
+				  data_size, sectorsize);
+			ret = -EINVAL;
+		}
+	}
 	if (btrfs_fs_incompat(fs_info, REMAP_TREE)) {
 		/*
 		 * Reduce test matrix for remap tree by requiring block-group-tree
@@ -3371,11 +3381,11 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info)
 	invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
 }
 
-static u32 calc_block_max_order(u32 sectorsize_bits)
+static u32 calc_block_max_order(u32 datasize_bits)
 {
 	u32 max_size;
 
-	max_size = min(BTRFS_MAX_BLOCKS_PER_FOLIO << sectorsize_bits,
+	max_size = min(BTRFS_MAX_BLOCKS_PER_FOLIO << datasize_bits,
 		       BTRFS_MAX_FOLIO_SIZE);
 	return ilog2(round_up(max_size, PAGE_SIZE) >> PAGE_SHIFT);
 }
@@ -3497,11 +3507,14 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 
 	fs_info->nodesize = nodesize;
 	fs_info->nodesize_bits = ilog2(nodesize);
-	fs_info->datasize = sectorsize;
 	fs_info->sectorsize = sectorsize;
-	fs_info->datasize_bits = ilog2(sectorsize);
 	fs_info->sectorsize_bits = ilog2(sectorsize);
-	fs_info->block_min_order = ilog2(round_up(sectorsize, PAGE_SIZE) >> PAGE_SHIFT);
+	if (btrfs_fs_incompat(fs_info, RAID56_VSL))
+		fs_info->datasize = btrfs_super_data_size(disk_super);
+	else
+		fs_info->datasize = sectorsize;
+	fs_info->datasize_bits = ilog2(fs_info->datasize);
+	fs_info->block_min_order = ilog2(round_up(fs_info->datasize, PAGE_SIZE) >> PAGE_SHIFT);
 	/*
 	 * For HIGHMEM, a large folio cannot be mapped in one go, breaking a lot
 	 * of basic assumptions for btrfs IOs.
@@ -3560,8 +3573,8 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 	sb->s_bdi->ra_pages = max(sb->s_bdi->ra_pages, SZ_4M / PAGE_SIZE);
 
 	/* Update the values for the current filesystem. */
-	sb->s_blocksize = sectorsize;
-	sb->s_blocksize_bits = blksize_bits(sectorsize);
+	sb->s_blocksize = fs_info->datasize;
+	sb->s_blocksize_bits = blksize_bits(fs_info->datasize);
 	/*
 	 * When temp_fsid is active, fs_devices->fsid is assigned a random UUID
 	 * at mount. This inconsistent UUID causes issues for layered filesystems
fs/btrfs/fs.h +2 -1
index a99aa3a7b4c7..a3ac2b85d270 100644
@@ -334,7 +334,8 @@ enum {
 	(BTRFS_FEATURE_INCOMPAT_SUPP_STABLE |	\
 	 BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE | \
 	 BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \
-	 BTRFS_FEATURE_INCOMPAT_REMAP_TREE)
+	 BTRFS_FEATURE_INCOMPAT_REMAP_TREE |	\
+	 BTRFS_FEATURE_INCOMPAT_RAID56_VSL)
 
 #else
 
fs/btrfs/sysfs.c +2 -0
index 277d2f4cc12a..ac833c35ca2f 100644
@@ -188,6 +188,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2);
 BTRFS_FEAT_ATTR_INCOMPAT(raid_stripe_tree, RAID_STRIPE_TREE);
 /* Remove once support for remap tree is feature complete. */
 BTRFS_FEAT_ATTR_INCOMPAT(remap_tree, REMAP_TREE);
+BTRFS_FEAT_ATTR_INCOMPAT(raid56_vsl, RAID56_VSL);
 #endif
 #ifdef CONFIG_FS_VERITY
 BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY);
@@ -221,6 +222,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = {
 	BTRFS_FEAT_ATTR_PTR(extent_tree_v2),
 	BTRFS_FEAT_ATTR_PTR(raid_stripe_tree),
 	BTRFS_FEAT_ATTR_PTR(remap_tree),
+	BTRFS_FEAT_ATTR_PTR(raid56_vsl),
 #endif
 #ifdef CONFIG_FS_VERITY
 	BTRFS_FEAT_ATTR_PTR(verity),
include/uapi/linux/btrfs.h +22 -0
index 0a13baf3d8d1..99e51a73dc06 100644
@@ -338,6 +338,28 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA	(1ULL << 16)
 #define BTRFS_FEATURE_INCOMPAT_REMAP_TREE	(1ULL << 17)
 
+/*
+ * This feature includes two parts:
+ * - Force data read/writes to be aligned to data size
+ *   And the data size is power of 2 times of sectorsize.
+ *   This feature must be determined at mkfs time, and will limit the number
+ *   of data stripes for RAID56 VSL chunks.
+ *
+ *   This can be implemented as a compat RO feature, but doesn't make much
+ *   sense as an independent feature.
+ *
+ * - Variable stripe length RAID56 profiles
+ *   This forces the full stripe length to match the above data size.
+ *   This will allow all data writes to be full stripe aligned, thus
+ *   no more write holes.
+ *
+ *   The cost is that data stripes will be limited by (datasize / sectorsize),
+ *   and larger IO sizes.
+ *
+ * For now, only the data size part is implemented.
+ */
+#define BTRFS_FEATURE_INCOMPAT_RAID56_VSL	(1ULL << 18)
+
 struct btrfs_ioctl_feature_flags {
 	__u64 compat_flags;
 	__u64 compat_ro_flags;
include/uapi/linux/btrfs_tree.h +2 -1
index fa4984c80c9d..75e76ac2104a 100644
@@ -724,9 +724,10 @@ struct btrfs_super_block {
 	__le64 remap_root;
 	__le64 remap_root_generation;
 	__u8 remap_root_level;
+	__u32 data_size;
 
 	/* Future expansion */
-	__u8 reserved[199];
+	__u8 reserved[195];
 	__u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
 	struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS];
 
Signature · 1 line
2.55.0
#3 lore raw
In reply to Qu Wenruo
Quoted 8 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

Why not make mixed incompatible with it then?

Quoted 41 lines

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
This series.

An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
Meaning the minimal write size is 8K for data.

However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
It will have the following requirements:

* Can have up to (datasize / sectorsize) data stripes

* The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

* The full stripe length is always datasize

This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem. And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

Quoted 5 lines

But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

But this is still all !zoned RAID56, isn't it? Or did I miss something?

#4 lore raw
Quoted 11 lines · Johannes Thumshirn
Quoted 8 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

Why not make mixed incompatible with it then?

It's already rejected by kernel.

And for progs (and I noticed I forgot to send the patch) it will reject mkfs.

Quoted 42 lines
Quoted 42 lines

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
   This series.

   An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
   Meaning the minimal write size is 8K for data.

   However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

   The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

   But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
   It will have the following requirements:

   * Can have up to (datasize / sectorsize) data stripes

   * The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

   * The full stripe length is always datasize

   This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

   This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem. And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

Quoted 8 lines
Quoted 5 lines

   But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

But this is still all !zoned RAID56, isn't it? Or did I miss something?

Yes, all for non-zoned RAID56.

#5 lore raw
In reply to Qu Wenruo
Quoted 44 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
This series.

An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
Meaning the minimal write size is 8K for data.

However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
It will have the following requirements:

* Can have up to (datasize / sectorsize) data stripes

* The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

* The full stripe length is always datasize

This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

Quoted 7 lines

This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem.

And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

If I understood correctly, the above sentences should be read as:

And will require users to determine the datasize at mkfs
time, without any way to change to another datasize. And implicitly the *minimum* number of disks which will be >= than datasize / sectorsize + nr_parity where nr_parity is 1 for raid5 and 2 for raid6...

So it is more a minim number of disks requirements than a maximum device count.

Some consideration about the possible wasting of space where the extent is less than the datasize.

I did some simulation on my filesystem about the disk usage. The most interesting part is that (at least on my filesystem), about 70% the files has a size < 4k, and thus it is inlined.
The other ones with size >64k consume 90% of space, but those quite often are way bigger than 64k so the wasting of space are less than I initially thought.

However most of the files are the files of my root filesystem (without my home), which are "near immutable". In fact these are not update in place but mostly rewritten from scratch by my package manager.

We need to understand what happens to the files to my home (i.e. files which are likely to be rewrote in place).

As mitigation we could add a RAID1C2/RAID1C1 for extent smaller than a specific threshold (i.e. where the space consumed by RAID1cX arrangement is smaller than a RAID56_VSL).

About the arrangement of the sector in the chunk, I would suggest the following one:

Current RAID5 layout (for simplicity I left the parity on D3):

D  D  D
1  2  3
1  5  P
2  6  P
3  7  P
4  8  P
9  13 P

10 14 P
[...]

RAID56_VSL layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3
1  2  P    |
3  4  P    |  1st extent
5  6  P    |
7  8  P    |
9  10 P    |  2nd extent
11 12 P    |
13 14 P    |

My proposal layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3
1  4  P   |
2  5  P   |  First extent
3  6  P   |
7  11 P   |
8  12 P   |  2nd extent
9  13 P   |
10 14 P   |

Because an extent consumes the entire rows, we can spread the sector vertically and when we fill a column, we will move to the next one.

Quoted 52 lines

But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

If anyone has a better way to address the rename, I'm all ears.

Qu Wenruo (2):
btrfs: split sectorsize into datasize and sectorsize
btrfs: implement a new incompat feature, raid56_vsl

  fs/btrfs/accessors.h                   |   2 +
  fs/btrfs/bio.c                         |  16 +++-
  fs/btrfs/block-group.c                 |   2 +-
  fs/btrfs/btrfs_inode.h                 |  10 +++
  fs/btrfs/compression.c                 |  12 +--
  fs/btrfs/defrag.c                      |  14 +--
  fs/btrfs/delalloc-space.c              |  22 ++---
  fs/btrfs/direct-io.c                   |   6 +-
  fs/btrfs/disk-io.c                     |  43 +++++++---
  fs/btrfs/extent-io-tree.c              |   2 +-
  fs/btrfs/extent-tree.c                 |   4 +-
  fs/btrfs/extent_io.c                   |  62 +++++++-------
  fs/btrfs/extent_map.c                  |   2 +-
  fs/btrfs/fiemap.c                      |   2 +-
  fs/btrfs/file-item.c                   |  22 +++--
  fs/btrfs/file.c                        |  72 ++++++++--------
  fs/btrfs/fs.h                          |  27 ++++--
  fs/btrfs/inode-item.c                  |   6 +-
  fs/btrfs/inode.c                       | 113 +++++++++++++------------
  fs/btrfs/ioctl.c                       |  12 +--
  fs/btrfs/lzo.c                         |  14 +--
  fs/btrfs/reflink.c                     |  16 ++--
  fs/btrfs/relocation.c                  |  16 ++--
  fs/btrfs/send.c                        |   4 +-
  fs/btrfs/subpage.c                     |  96 +++++++++++++++------
  fs/btrfs/subpage.h                     |   2 +-
  fs/btrfs/super.c                       |   8 +-
  fs/btrfs/sysfs.c                       |   8 +-
  fs/btrfs/tests/btrfs-tests.c           |   6 +-
  fs/btrfs/tests/free-space-tree-tests.c |   2 +-
  fs/btrfs/tree-checker.c                |   2 +-
  fs/btrfs/tree-log.c                    |   6 +-
  fs/btrfs/volumes.c                     |   2 +-
  fs/btrfs/zlib.c                        |   6 +-
  fs/btrfs/zoned.c                       |   4 +-
  fs/btrfs/zstd.c                        |   8 +-
  include/uapi/linux/btrfs.h             |  22 +++++
  include/uapi/linux/btrfs_tree.h        |   3 +-
  38 files changed, 403 insertions(+), 273 deletions(-)
Signature · 2 lines
gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5
author
#6 lore raw
Quoted 53 lines · Goffredo Baroncelli
Quoted 44 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
   This series.

   An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
   Meaning the minimal write size is 8K for data.

   However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

   The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

   But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
   It will have the following requirements:

   * Can have up to (datasize / sectorsize) data stripes

   * The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

   * The full stripe length is always datasize

   This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

Quoted 7 lines

   This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem.

   And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

Quoted 8 lines

If I understood correctly, the above sentences should be read as:

    And will require users to determine the datasize at mkfs time, without any way to change to another datasize. And implicitly
the
    *minimum* number of disks which will be >= than datasize /
sectorsize + nr_parity
    where nr_parity is 1 for raid5 and 2 for raid6...

Nope, one can always go as low as 1 data stripe no matter the datasize.

So it's maximum, and you're wrong.

Quoted 6 lines

So it is more a minim number of disks requirements than a maximum device count.

Some consideration about the possible wasting of space where the extent is less than the datasize.

Impossible, the minimal extent size will be data size.

It looks like you didn't even understand that such fs works exactly like it has a larger block size.

Quoted 10 lines

I did some simulation on my filesystem about the disk usage. The most interesting part is that (at least on my filesystem), about 70% the files has a size < 4k, and thus it is inlined.
The other ones with size >64k consume 90% of space, but those quite often are way bigger than 64k so the wasting of space are less than I initially thought.

However most of the files are the files of my root filesystem (without my home), which are "near immutable". In fact these are not update in place but mostly rewritten from scratch by my package manager.

Quoted 6 lines

We need to understand what happens to the files to my home (i.e. files which are likely to be rewrote in place).

As mitigation we could add a RAID1C2/RAID1C1 for extent smaller than a specific threshold (i.e. where the space consumed by RAID1cX arrangement is smaller than a RAID56_VSL).

About the arrangement of the sector in the chunk, I would suggest the following one:

There is no change in the data layout in the RAID56_VSL, and I do not think there should be any change.

Quoted 110 lines

Current RAID5 layout (for simplicity I left the parity on D3):

D  D  D
1  2  3

1  5  P
2  6  P
3  7  P
4  8  P
9  13 P
10 14 P
[...]

RAID56_VSL layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  2  P    |
3  4  P    |  1st extent
5  6  P    |

7  8  P    |
9  10 P    |  2nd extent
11 12 P    |
13 14 P    |

My proposal layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  4  P   |
2  5  P   |  First extent
3  6  P   |
7  11 P   |
8  12 P   |  2nd extent
9  13 P   |

10 14 P   |

Because an extent consumes the entire rows, we can spread the sector vertically and when we fill a column, we will move to the next one.

Quoted 52 lines

   But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

If anyone has a better way to address the rename, I'm all ears.

Qu Wenruo (2):
   btrfs: split sectorsize into datasize and sectorsize
   btrfs: implement a new incompat feature, raid56_vsl

  fs/btrfs/accessors.h                   |   2 + fs/btrfs/bio.c                         |  16 +++-

  fs/btrfs/block-group.c                 |   2 +-
  fs/btrfs/btrfs_inode.h                 |  10 +++
  fs/btrfs/compression.c                 |  12 +--

  fs/btrfs/defrag.c                      |  14 +-- fs/btrfs/delalloc-space.c              |  22 ++--- fs/btrfs/direct-io.c                   |   6 +- fs/btrfs/disk-io.c                     |  43 +++++++--- fs/btrfs/extent-io-tree.c              |   2 +- fs/btrfs/extent-tree.c                 |   4 +- fs/btrfs/extent_io.c                   |  62 +++++++------- fs/btrfs/extent_map.c                  |   2 +- fs/btrfs/fiemap.c                      |   2 +- fs/btrfs/file-item.c                   |  22 +++-- fs/btrfs/file.c                        |  72 ++++++++-------- fs/btrfs/fs.h                          |  27 ++++-- fs/btrfs/inode-item.c                  |   6 +- fs/btrfs/inode.c                       | 113 +++++++++++++------------ fs/btrfs/ioctl.c                       |  12 +-- fs/btrfs/lzo.c                         |  14 +-- fs/btrfs/reflink.c                     |  16 ++-- fs/btrfs/relocation.c                  |  16 ++-- fs/btrfs/send.c                        |   4 +- fs/btrfs/subpage.c                     |  96 +++++++++++++++------ fs/btrfs/subpage.h                     |   2 +- fs/btrfs/super.c                       |   8 +- fs/btrfs/sysfs.c                       |   8 +- fs/btrfs/tests/btrfs-tests.c           |   6 +- fs/btrfs/tests/free-space-tree-tests.c |   2 +- fs/btrfs/tree-checker.c                |   2 +- fs/btrfs/tree-log.c                    |   6 +- fs/btrfs/volumes.c                     |   2 +- fs/btrfs/zlib.c                        |   6 +- fs/btrfs/zoned.c                       |   4 +- fs/btrfs/zstd.c                        |   8 +- include/uapi/linux/btrfs.h             |  22 +++++ include/uapi/linux/btrfs_tree.h        |   3 +-

  38 files changed, 403 insertions(+), 273 deletions(-)
#7 lore raw
Quoted 66 lines · Qu Wenruo
Quoted 53 lines · Goffredo Baroncelli
Quoted 44 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
   This series.

   An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
   Meaning the minimal write size is 8K for data.

   However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

   The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

   But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
   It will have the following requirements:

   * Can have up to (datasize / sectorsize) data stripes

   * The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

   * The full stripe length is always datasize

   This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

Quoted 7 lines

   This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem.

   And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

Quoted 6 lines

If I understood correctly, the above sentences should be read as:

     And will require users to determine the datasize at mkfs
     time, without any way to change to another datasize. And implicitly the *minimum* number of disks which will be >= than datasize / sectorsize + nr_parity where nr_parity is 1 for raid5 and 2 for raid6...

Nope, one can always go as low as 1 data stripe no matter the datasize.

So it's maximum, and you're wrong.

Quoted 3 lines · Before you
Quoted 3 lines
Quoted 3 lines

* The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

So I understood that the number of the devices is a limit of the chunk. I.e. I can have a datasize of 64k (=16 devices), but the filesystem can also have (e.g.) 20 devices.

Each time a datasize is allocated, the most empty 16+nr_parity disks are picked.

Instead from

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

it seems that the limit is per filesystem. This get me confused.

I want only to understand your design.

Quoted 1 line

Nope, one can always go as low as 1 data stripe no matter the datasize.

Does this mean that when the user sets datasize = 64k (16 disks), BTRFS allows
to create chunk of 16+nr_parity disks, 8+nr_parity, 4+nr_parity, 2+nr_parity, 1+nr_parity ?

Quoted 11 lines
Quoted 6 lines

So it is more a minim number of disks requirements than a maximum device count.

Some consideration about the possible wasting of space where the extent is less than the datasize.

Impossible, the minimal extent size will be data size.

It looks like you didn't even understand that such fs works exactly like it has a larger block size.

If I have a file of (e.g.) 48K, and the datasize is (e.g.) 64k, I wasted 16K. This is what I told .

Quoted 130 lines
Quoted 10 lines

I did some simulation on my filesystem about the disk usage. The most interesting part is that (at least on my filesystem), about 70% the files has a size < 4k, and thus it is inlined.
The other ones with size >64k consume 90% of space, but those quite often are way bigger than 64k so the wasting of space are less than I initially thought.

However most of the files are the files of my root filesystem (without my home), which are "near immutable". In fact these are not update in place but mostly rewritten from scratch by my package manager.

Quoted 6 lines

We need to understand what happens to the files to my home (i.e. files which are likely to be rewrote in place).

As mitigation we could add a RAID1C2/RAID1C1 for extent smaller than a specific threshold (i.e. where the space consumed by RAID1cX arrangement is smaller than a RAID56_VSL).

About the arrangement of the sector in the chunk, I would suggest the following one:

There is no change in the data layout in the RAID56_VSL, and I do not think there should be any change.

Quoted 110 lines

Current RAID5 layout (for simplicity I left the parity on D3):

D  D  D
1  2  3

1  5  P
2  6  P
3  7  P
4  8  P
9  13 P
10 14 P
[...]

RAID56_VSL layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  2  P    |
3  4  P    |  1st extent
5  6  P    |

7  8  P    |
9  10 P    |  2nd extent
11 12 P    |
13 14 P    |

My proposal layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  4  P   |
2  5  P   |  First extent
3  6  P   |
7  11 P   |
8  12 P   |  2nd extent
9  13 P   |

10 14 P   |

Because an extent consumes the entire rows, we can spread the sector vertically and when we fill a column, we will move to the next one.

Quoted 52 lines

   But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

If anyone has a better way to address the rename, I'm all ears.

Qu Wenruo (2):
   btrfs: split sectorsize into datasize and sectorsize
   btrfs: implement a new incompat feature, raid56_vsl

  fs/btrfs/accessors.h                   |   2 + fs/btrfs/bio.c                         |  16 +++-

  fs/btrfs/block-group.c                 |   2 +-
  fs/btrfs/btrfs_inode.h                 |  10 +++
  fs/btrfs/compression.c                 |  12 +--

  fs/btrfs/defrag.c                      |  14 +-- fs/btrfs/delalloc-space.c              |  22 ++--- fs/btrfs/direct-io.c                   |   6 +- fs/btrfs/disk-io.c                     |  43 +++++++--- fs/btrfs/extent-io-tree.c              |   2 +- fs/btrfs/extent-tree.c                 |   4 +- fs/btrfs/extent_io.c                   |  62 +++++++------- fs/btrfs/extent_map.c                  |   2 +- fs/btrfs/fiemap.c                      |   2 +- fs/btrfs/file-item.c                   |  22 +++-- fs/btrfs/file.c                        |  72 ++++++++-------- fs/btrfs/fs.h                          |  27 ++++-- fs/btrfs/inode-item.c                  |   6 +- fs/btrfs/inode.c                       | 113 +++++++++++++------------ fs/btrfs/ioctl.c                       |  12 +-- fs/btrfs/lzo.c                         |  14 +-- fs/btrfs/reflink.c                     |  16 ++-- fs/btrfs/relocation.c                  |  16 ++-- fs/btrfs/send.c                        |   4 +- fs/btrfs/subpage.c                     |  96 +++++++++++++++------ fs/btrfs/subpage.h                     |   2 +- fs/btrfs/super.c                       |   8 +- fs/btrfs/sysfs.c                       |   8 +- fs/btrfs/tests/btrfs-tests.c           |   6 +- fs/btrfs/tests/free-space-tree-tests.c |   2 +- fs/btrfs/tree-checker.c                |   2 +- fs/btrfs/tree-log.c                    |   6 +- fs/btrfs/volumes.c                     |   2 +- fs/btrfs/zlib.c                        |   6 +- fs/btrfs/zoned.c                       |   4 +- fs/btrfs/zstd.c                        |   8 +- include/uapi/linux/btrfs.h             |  22 +++++ include/uapi/linux/btrfs_tree.h        |   3 +-

  38 files changed, 403 insertions(+), 273 deletions(-)
Signature · 2 lines
gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D  17B2 0EDA 9B37 8B82 E0B5
author
#8 lore raw
Quoted 86 lines · Goffredo Baroncelli
Quoted 66 lines · Qu Wenruo
Quoted 53 lines · Goffredo Baroncelli
Quoted 44 lines · Qu Wenruo

The new feature stands for RAID56 Variable Stripe Length, however the VSL part is not implemented yet, thus the whole feature is still hidden behind experimental, and there are definitely works left to properly split the 1st patch.

But for now, this series can pass most fstests cases.
The failing ones are all related to mixed block groups, which can not be created nor mounted with this new feature.

The roadmap for the full RAID56 VSL implementation is split into two parts:

- Introduce a new datasize member
   This series.

   An fs with data_size 8K and sectorsize 4K will act like a fs with sectorsize 8K.
   Meaning the minimal write size is 8K for data.

   However the checksum is still calculated based sectorsize, meanwhile we can still recover each corrupted 4K sector inside a 8K data block.

   The idea and implementation is not that complex, we're just reusing the existing bs > ps support to handle it (on 4K page sized systems).

   But the challenge is the details where some part of the code still requires sectorsize (checksum related), meanwhile every other location goes datasize for data.

- Introduce a new RAID56_VSL chunk type
   It will have the following requirements:

   * Can have up to (datasize / sectorsize) data stripes

   * The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

   * The full stripe length is always datasize

   This allows every data write to be full stripe aligned. And for read repair/scrub, we can still locate and recover a single sector inside a RAID56 stripe.

Quoted 7 lines

   This is less flex than the traditional RAID56, which has no limit on the number of data stripes, but has the write-hole problem.

   And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

Quoted 6 lines

If I understood correctly, the above sentences should be read as:

     And will require users to determine the datasize at mkfs
     time, without any way to change to another datasize. And implicitly the *minimum* number of disks which will be >= than datasize / sectorsize + nr_parity where nr_parity is 1 for raid5 and 2 for raid6...

Nope, one can always go as low as 1 data stripe no matter the datasize.

So it's maximum, and you're wrong.

Quoted 3 lines · Before you
Quoted 3 lines
Quoted 3 lines

   * The number of data stripes are always power of 2 This is only for every RAID56_VSL chunk, users can still have whatever number of devices in the fs.

So I understood that the number of the devices is a limit of the chunk. I.e. I can have a datasize of 64k (=16 devices), but the filesystem can also have (e.g.) 20 devices.

Each time a datasize is allocated, the most empty 16+nr_parity disks are picked.

Instead from

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

   And will require users to determine the maximum device numbers at mkfs time, without any way to change to another datasize.

it seems that the limit is per filesystem. This get me confused.

Quoted 7 lines

I want only to understand your design.

Quoted 1 line

Nope, one can always go as low as 1 data stripe no matter the datasize.

Does this mean that when the user sets  datasize = 64k (16 disks), BTRFS allows
 to create chunk of 16+nr_parity disks, 8+nr_parity, 4+nr_parity,
2+nr_parity, 1+nr_parity ?

Yes.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

So it is more a minim number of disks requirements than a maximum device count.

Some consideration about the possible wasting of space where the extent is less than the datasize.

Quoted 7 lines
Quoted 4 lines

Impossible, the minimal extent size will be data size.

It looks like you didn't even understand that such fs works exactly like it has a larger block size.

If I have a file of (e.g.) 48K, and the datasize is (e.g.) 64k, I wasted 16K. This is what I told .

That's exactly when you write 3K and the sectorsize is 4K.

This is the common sense for all block filesystems.

Quoted 135 lines
Quoted 133 lines
Quoted 12 lines

I did some simulation on my filesystem about the disk usage. The most interesting part is that (at least on my filesystem), about 70% the files has a size < 4k, and thus it is inlined.
The other ones with size >64k consume 90% of space, but those quite often are way bigger than 64k so the wasting of space are less than I initially thought.

However most of the files are the files of my root filesystem (without my home), which are "near immutable". In fact these are not update in place but mostly rewritten from scratch by my package manager.

Quoted 6 lines

We need to understand what happens to the files to my home (i.e. files which are likely to be rewrote in place).

As mitigation we could add a RAID1C2/RAID1C1 for extent smaller than a specific threshold (i.e. where the space consumed by RAID1cX arrangement is smaller than a RAID56_VSL).

About the arrangement of the sector in the chunk, I would suggest the following one:

There is no change in the data layout in the RAID56_VSL, and I do not think there should be any change.

Quoted 111 lines

Current RAID5 layout (for simplicity I left the parity on D3):

D  D  D
1  2  3

1  5  P
2  6  P
3  7  P
4  8  P
9  13 P
10 14 P
[...]

RAID56_VSL layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  2  P    |
3  4  P    |  1st extent
5  6  P    |

7  8  P    |
9  10 P    |  2nd extent
11 12 P    |
13 14 P    |

My proposal layout (2 extents with length of 6 sectors and 8 sectors, parity still in D3)

D  D  D
1  2  3

1  4  P   |
2  5  P   |  First extent
3  6  P   |
7  11 P   |
8  12 P   |  2nd extent
9  13 P   |

10 14 P   |

Because an extent consumes the entire rows, we can spread the sector vertically and when we fill a column, we will move to the next one.

Quoted 53 lines

   But the second part is pretty easy to implement.

As the digest shows, the biggest problem is the first patch, which is touching over 200 sectorsize users, and is definitely the source of all bugs I hit and fixed so far.

If anyone has a better way to address the rename, I'm all ears.

Qu Wenruo (2):
   btrfs: split sectorsize into datasize and sectorsize
   btrfs: implement a new incompat feature, raid56_vsl

  fs/btrfs/accessors.h                   |   2 + fs/btrfs/bio.c                         |  16 +++-

  fs/btrfs/block-group.c                 |   2 +-
  fs/btrfs/btrfs_inode.h                 |  10 +++
  fs/btrfs/compression.c                 |  12 +--

  fs/btrfs/defrag.c                      |  14 +-- fs/btrfs/delalloc-space.c              |  22 ++--- fs/btrfs/direct-io.c                   |   6 +- fs/btrfs/disk-io.c                     |  43 +++++++--- fs/btrfs/extent-io-tree.c              |   2 +- fs/btrfs/extent-tree.c                 |   4 +- fs/btrfs/extent_io.c                   |  62 +++++++------- fs/btrfs/extent_map.c                  |   2 +- fs/btrfs/fiemap.c                      |   2 +- fs/btrfs/file-item.c                   |  22 +++-- fs/btrfs/file.c                        |  72 ++++++++-------- fs/btrfs/fs.h                          |  27 ++++-- fs/btrfs/inode-item.c                  |   6 +- fs/btrfs/inode.c                       | 113 ++++++++++++

+------------
  fs/btrfs/ioctl.c                       |  12 +--
  fs/btrfs/lzo.c                         |  14 +--
  fs/btrfs/reflink.c                     |  16 ++--
  fs/btrfs/relocation.c                  |  16 ++--
  fs/btrfs/send.c                        |   4 +-
  fs/btrfs/subpage.c                     |  96 +++++++++++++++------
  fs/btrfs/subpage.h                     |   2 +-
  fs/btrfs/super.c                       |   8 +-
  fs/btrfs/sysfs.c                       |   8 +-
  fs/btrfs/tests/btrfs-tests.c           |   6 +-
  fs/btrfs/tests/free-space-tree-tests.c |   2 +-
  fs/btrfs/tree-checker.c                |   2 +-
  fs/btrfs/tree-log.c                    |   6 +-
  fs/btrfs/volumes.c                     |   2 +-
  fs/btrfs/zlib.c                        |   6 +-
  fs/btrfs/zoned.c                       |   4 +-
  fs/btrfs/zstd.c                        |   8 +-
  include/uapi/linux/btrfs.h             |  22 +++++
  include/uapi/linux/btrfs_tree.h        |   3 +-
  38 files changed, 403 insertions(+), 273 deletions(-)