Linux Btrfs filesystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/9] btrfs: prepare for variable stripe_len
@ 2026-10-21  3:39 Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 1/9] btrfs: reduce the width of num_stripes and sub_stripes Qu Wenruo
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

[CHANGELOG]
v2:
- Use IS_ALINGED() when possible
  Mostly affecting zoned.c

- Refactor is_same_chunk_map()
  Not only reduce the length of the parameter list, but also make it
  easier to expand, with extra unlikely() calls.

- Refactor alloc_scrub_bbio()
  Since the @private is always a scrub_stripe pointer, use scrub_stripe
  as the type instead, so we can grab stripe_len without introducing a
  new parameter.

All are suggested by Johannes.


This is a preparation patchset, to support a stripe_len that is not
64KiB.

This is for the future variable stripe length RAID56 feature, where
RAID56_VSL profile is allowed for data chunks, and the stripe_len can be
as small as sectorsize, meanwhile data IO are kept to be aligned to
data_size, which is power of 2, and larger than sector size.

Finally, RAID56_VSL will always have a number of devices that nr_data ==
data_size / sector_size.

So that's why we want a variable stripe_len.

This series does all the preparation, including supporting stripe_len <
PAGE_SIZE.

For now, tree-checker will still reject stripe_len != 64K, but that
limit will be removed when RAID56_VSL feature is introduced and can be
properly tested.


Qu Wenruo (9):
  btrfs: reduce the width of num_stripes and sub_stripes
  btrfs: load the on-disk btrfs_chunk::stripe_len
  btrfs: add btrfs_io_context::stripe_len and load it
  btrfs: change scrub to use stripe_len
  btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset()
  btrfs: convert volumes.c and block-group.c to use map->stripe_len
  btrfs: convert raid56.c to use rbio->bioc->stripe_len
  btrfs: convert zoned.c to use map->stripe_len
  btrfs: introduce extra limits on btrfs_chunk::stripe_len

 fs/btrfs/block-group.c  |   9 +--
 fs/btrfs/raid56.c       |  57 ++++++++++++------
 fs/btrfs/raid56.h       |   7 +--
 fs/btrfs/scrub.c        | 129 ++++++++++++++++++++++++++--------------
 fs/btrfs/tree-checker.c |  13 +++-
 fs/btrfs/volumes.c      |  95 +++++++++++++++++++----------
 fs/btrfs/volumes.h      |  16 +++--
 fs/btrfs/zoned.c        |  56 +++++++++--------
 8 files changed, 246 insertions(+), 136 deletions(-)

-- 
2.56.0


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

* [PATCH v2 1/9] btrfs: reduce the width of num_stripes and sub_stripes
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 2/9] btrfs: load the on-disk btrfs_chunk::stripe_len Qu Wenruo
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Johannes Thumshirn

The on-disk btrfs_chunk structure uses u16 for both num_stripes and
sub_stripes, so there is no need to use "int" to represent them in
btrfs_chunk_map.

Follow the on-disk format to use u16 for those members, including
verified_stripes too.

And since verified_stripes is u16, it no longer completely fill the
hole between @refs and @start.
Instead use @num_stripes and @sub_stripes to fill the hole, and leave a 6
bytes hole between @verified_stripes and @stripes.

This doesn't save any space for btrfs_chunk_map, but leaves enough space
for future expansion.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/volumes.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 67464025e449..d97ca875c596 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -623,9 +623,9 @@ extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES];
 
 struct btrfs_chunk_map {
 	struct rb_node rb_node;
-	/* For mount time dev extent verification. */
-	int verified_stripes;
 	refcount_t refs;
+	u16 num_stripes;
+	u16 sub_stripes;
 	u64 start;
 	u64 chunk_len;
 	u64 dev_extent_size;
@@ -638,8 +638,8 @@ struct btrfs_chunk_map {
 	 */
 	u64 type;
 	u64 on_disk_type;
-	int num_stripes;
-	int sub_stripes;
+	/* For mount time dev extent verification. */
+	u16 verified_stripes;
 	struct btrfs_io_stripe stripes[];
 };
 
-- 
2.56.0


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

* [PATCH v2 2/9] btrfs: load the on-disk btrfs_chunk::stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 1/9] btrfs: reduce the width of num_stripes and sub_stripes Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 3/9] btrfs: add btrfs_io_context::stripe_len and load it Qu Wenruo
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Johannes Thumshirn

Currently btrfs doesn't load btrfs_chunk::stripe_len into
btrfs_chunk_map, as tree-checker will reject any chunk whose stripe_len
is not 64K.

But for the future we will want stripe length other than 64K.

As a preparation for the variable stripe length in the future, load
btrfs_chunk::stripe_len into btrfs_chunk_map::stripe_len and
stripe_shift.

For now stripe_len is still 64K, no matter if it's loaded from the
on-disk metadata or newly allocated chunk.
As tree-checker still rejects any chunk whose stripe_len is not 64K.

The two new members fit into the existing hole of btrfs_chunk_map, so
the size of btrfs_chunk_map stay the same.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/volumes.c | 12 +++++++++++-
 fs/btrfs/volumes.h |  2 ++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 6574939e1295..a30b212ef0f1 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6166,6 +6166,12 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp)
 	refcount_set(&map->refs, 1);
 	RB_CLEAR_NODE(&map->rb_node);
 
+	/*
+	 * Initialize the default stripe len, selftests will utilize them
+	 * without an on-disk chunk item.
+	 */
+	map->stripe_len = BTRFS_STRIPE_LEN;
+	map->stripe_shift = ilog2(map->stripe_len);
 	return map;
 }
 
@@ -6203,6 +6209,8 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans,
 	map->on_disk_type = type;
 	map->sub_stripes = ctl->sub_stripes;
 	map->num_stripes = ctl->num_stripes;
+	map->stripe_len = BTRFS_STRIPE_LEN;
+	map->stripe_shift = ilog2(BTRFS_STRIPE_LEN);
 	set_real_chunk_type(map);
 
 	for (int i = 0; i < ctl->ndevs; i++) {
@@ -6381,7 +6389,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans,
 
 	btrfs_set_stack_chunk_length(chunk, bg->length);
 	btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID);
-	btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN);
+	btrfs_set_stack_chunk_stripe_len(chunk, map->stripe_len);
 	btrfs_set_stack_chunk_type(chunk, map->on_disk_type);
 	btrfs_set_stack_chunk_num_stripes(chunk, map->num_stripes);
 	btrfs_set_stack_chunk_io_align(chunk, BTRFS_STRIPE_LEN);
@@ -7781,6 +7789,8 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
 	map->chunk_len = length;
 	map->num_stripes = num_stripes;
 	map->on_disk_type = type;
+	map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+	map->stripe_shift = ilog2(map->stripe_len);
 	/*
 	 * We can't use the sub_stripes value, as for profiles other than
 	 * RAID10, they may have 0 as sub_stripes for filesystems created by
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index d97ca875c596..9dcc14cd61e2 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -640,6 +640,8 @@ struct btrfs_chunk_map {
 	u64 on_disk_type;
 	/* For mount time dev extent verification. */
 	u16 verified_stripes;
+	u8 stripe_shift;
+	u32 stripe_len;
 	struct btrfs_io_stripe stripes[];
 };
 
-- 
2.56.0


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

* [PATCH v2 3/9] btrfs: add btrfs_io_context::stripe_len and load it
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 1/9] btrfs: reduce the width of num_stripes and sub_stripes Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 2/9] btrfs: load the on-disk btrfs_chunk::stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 4/9] btrfs: change scrub to use stripe_len Qu Wenruo
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Johannes Thumshirn

The new member will be utilized by various path that no direct
btrfs_chunk_map is available, but a stripe length is still required.

The new member is located inside a hole between @refs and @fs_info, so
no size increase for btrfs_io_context.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/volumes.c | 1 +
 fs/btrfs/volumes.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a30b212ef0f1..ff7189ac1c49 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -7411,6 +7411,7 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
 		ret = -ENOMEM;
 		goto out;
 	}
+	bioc->stripe_len = map->stripe_len;
 	bioc->map_type = map->type;
 	bioc->use_rst = io_geom.use_rst;
 
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 9dcc14cd61e2..9356cec00a90 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -515,6 +515,7 @@ struct btrfs_discard_stripe {
  */
 struct btrfs_io_context {
 	refcount_t refs;
+	u32 stripe_len;
 	struct btrfs_fs_info *fs_info;
 	/* Taken from struct btrfs_chunk_map::type. */
 	u64 map_type;
-- 
2.56.0


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

* [PATCH v2 4/9] btrfs: change scrub to use stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (2 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 3/9] btrfs: add btrfs_io_context::stripe_len and load it Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  5:58   ` Johannes Thumshirn
  2026-10-21  3:39 ` [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset() Qu Wenruo
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

Several points to note:

- scrub_ctx::stripes[] stripe_len updates
  We always pre-initialize scrub_ctx::stripes[] at the very beginning of
  scrub, without holding any btrfs_chunk_map.

  So init_scrub_stripe() still uses BTRFS_STRIPE_LEN, and all allocated
  memory are based on that stripe length.

  But before scrubbing each chunk, update_scrub_stripe_len() will be
  called to update the scrub_stripe::stripe_len.

- Stripe_len should not exceed BTRFS_STRIPE_LEN
  Since scrub_stripe's heap memory are all based on BTRFS_STRIPE_LEN,
  any stripe_len larger than BTRFS_STRIPE_LEN will cause
  access-beyond-boundary.

  Thankfully there is no plan to support such stripe_len >
  BTRFS_STRIPE_LEN any time soon.

- alloc_scrub_bbio() changes
  Previously we ensure BTRFS_STRIPE_LEN >= PAGE_SIZE, so
  BTRFS_STRIPE_LEN >> PAGE_SHIFT can never be zero.

  But now stripe_len can be smaller than PAGE_SIZE. E.g. stripe_len 4K,
  page size 64K.

  So we have to use "round_up(stripe_len, PAGE_SIZE) >> PAGE_SHIFT"
  instead.

  Finally alloc_scrub_bbio() is always called with a scrub_stripe
  pointer as the @private parameter, change the type from (void *) to
  (struct scrub_stripe *), so that we can grab the stripe_len without
  a new parameter.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/scrub.c | 99 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 66 insertions(+), 33 deletions(-)

diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index bf91a6130e46..c3387e109779 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -124,7 +124,7 @@ enum {
 };
 
 /*
- * Represent one contiguous range with a length of BTRFS_STRIPE_LEN.
+ * Represent one contiguous range with a length of stripe_len.
  */
 struct scrub_stripe {
 	struct scrub_ctx *sctx;
@@ -139,7 +139,7 @@ struct scrub_stripe {
 
 	u16 mirror_num;
 
-	/* Should be BTRFS_STRIPE_LEN / sectorsize. */
+	/* Should be stripe_len / sectorsize. */
 	u16 nr_sectors;
 
 	/*
@@ -150,6 +150,8 @@ struct scrub_stripe {
 	u16 nr_meta_extents;
 
 	atomic_t pending_io;
+	/* From btrfs_chunk_map::stripe_len. Should not exceed BTRFS_STRIPE_LEN. */
+	u32 stripe_len;
 	wait_queue_head_t io_wait;
 	wait_queue_head_t repair_wait;
 
@@ -347,7 +349,13 @@ static int init_scrub_stripe(struct btrfs_fs_info *fs_info,
 {
 	memset(stripe, 0, sizeof(*stripe));
 
+	/*
+	 * Use the max stripe_len for now.
+	 *
+	 * This will be updated for every chunk using its stripe_len.
+	 */
 	stripe->nr_sectors = BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits;
+	stripe->stripe_len = BTRFS_STRIPE_LEN;
 	stripe->state = 0;
 
 	init_waitqueue_head(&stripe->io_wait);
@@ -901,7 +909,7 @@ static void scrub_bio_add_sector(struct btrfs_bio *bbio, struct scrub_stripe *st
 	const u32 offset = sector_nr << fs_info->sectorsize_bits;
 	int ret;
 
-	ASSERT(offset + fs_info->sectorsize <= BTRFS_STRIPE_LEN);
+	ASSERT(offset + fs_info->sectorsize <= stripe->stripe_len);
 
 	if (is_vmalloc_addr(stripe->buffer)) {
 		ret = bio_add_vmalloc(&bbio->bio, stripe->buffer + offset, fs_info->sectorsize);
@@ -914,9 +922,9 @@ static void scrub_bio_add_sector(struct btrfs_bio *bbio, struct scrub_stripe *st
 }
 
 static struct btrfs_bio *alloc_scrub_bbio(struct btrfs_fs_info *fs_info,
-					  blk_opf_t opf,
-					  u64 logical,
-					  btrfs_bio_end_io_t end_io, void *private)
+					  blk_opf_t opf, u64 logical,
+					  btrfs_bio_end_io_t end_io,
+					  struct scrub_stripe *stripe)
 {
 	struct btrfs_bio *bbio;
 
@@ -925,9 +933,10 @@ static struct btrfs_bio *alloc_scrub_bbio(struct btrfs_fs_info *fs_info,
 	 * different physical addresses, we have to ensure the bbio is large
 	 * enough to contain the full stripe.
 	 */
-	bbio = btrfs_bio_alloc(BTRFS_STRIPE_LEN >> PAGE_SHIFT, opf,
+	bbio = btrfs_bio_alloc(round_up(stripe->stripe_len, PAGE_SIZE) >>
+			       PAGE_SHIFT, opf,
 			       BTRFS_I(fs_info->btree_inode),
-			       logical, end_io, private);
+			       logical, end_io, stripe);
 	bbio->is_scrub = true;
 	bbio->bio.bi_iter.bi_sector = logical >> SECTOR_SHIFT;
 	return bbio;
@@ -959,7 +968,8 @@ static void scrub_stripe_submit_repair_read(struct scrub_stripe *stripe,
 
 		if (!bbio)
 			bbio = alloc_scrub_bbio(fs_info, REQ_OP_READ,
-						stripe->logical + (i << fs_info->sectorsize_bits),
+						stripe->logical +
+						(i << fs_info->sectorsize_bits),
 						scrub_repair_read_endio, stripe);
 
 		scrub_bio_add_sector(bbio, stripe, i);
@@ -1176,7 +1186,7 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work)
 		const unsigned long old_error_bitmap = scrub_bitmap_read_error(stripe);
 
 		scrub_stripe_submit_repair_read(stripe, mirror,
-						BTRFS_STRIPE_LEN, false);
+						stripe->stripe_len, false);
 		wait_scrub_stripe_io(stripe);
 		scrub_verify_one_stripe(stripe, old_error_bitmap);
 		if (scrub_bitmap_empty_error(stripe))
@@ -1325,7 +1335,8 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str
 		}
 		if (!bbio)
 			bbio = alloc_scrub_bbio(fs_info, REQ_OP_WRITE,
-					stripe->logical + (sector_nr << fs_info->sectorsize_bits),
+					stripe->logical +
+					(sector_nr << fs_info->sectorsize_bits),
 					scrub_write_endio, stripe);
 		scrub_bio_add_sector(bbio, stripe, sector_nr);
 	}
@@ -1416,7 +1427,7 @@ static int get_raid56_logic_offset(u64 physical, int num,
 
 		*offset = last_offset + btrfs_stripe_nr_to_offset(i);
 
-		stripe_nr = (u32)(*offset >> BTRFS_STRIPE_LEN_SHIFT) / data_stripes;
+		stripe_nr = (u32)(*offset >> map->stripe_shift) / data_stripes;
 
 		/* Work out the disk rotation on this stripe-set */
 		rot = stripe_nr % map->num_stripes;
@@ -1591,7 +1602,7 @@ static void fill_one_extent_info(struct btrfs_fs_info *fs_info,
 				 u64 extent_flags, u64 extent_gen)
 {
 	for (u64 cur_logical = max(stripe->logical, extent_start);
-	     cur_logical < min(stripe->logical + BTRFS_STRIPE_LEN,
+	     cur_logical < min(stripe->logical + stripe->stripe_len,
 			       extent_start + extent_len);
 	     cur_logical += fs_info->sectorsize) {
 		const int nr_sector = (cur_logical - stripe->logical) >>
@@ -1670,15 +1681,15 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg,
 	 * Round down to stripe boundary.
 	 *
 	 * The extra calculation against bg->start is to handle block groups
-	 * whose logical bytenr is not BTRFS_STRIPE_LEN aligned.
+	 * whose logical bytenr is not stripe_len aligned.
 	 */
-	stripe->logical = round_down(cur_logical - bg->start, BTRFS_STRIPE_LEN) +
+	stripe->logical = round_down(cur_logical - bg->start, stripe->stripe_len) +
 			  bg->start;
 	stripe->physical = physical + stripe->logical - logical_start;
 	stripe->dev = dev;
 	stripe->bg = bg;
 	stripe->mirror_num = mirror_num;
-	stripe_end = stripe->logical + BTRFS_STRIPE_LEN - 1;
+	stripe_end = stripe->logical + stripe->stripe_len - 1;
 
 	/* Fill the first extent info into stripe->sectors[] array. */
 	fill_one_extent_info(fs_info, stripe, extent_start, extent_len,
@@ -1715,10 +1726,15 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg,
 		ASSERT(stripe->csums);
 
 		/*
-		 * Our csum bitmap should be large enough, as BTRFS_STRIPE_LEN
-		 * should contain at most 16 sectors.
+		 * Our csum bitmap should be large enough, as stripe_len is no
+		 * larger than BTRFS_STRIPE_LEN, and BTRFS_STRIPE_LEN contains
+		 * at most 16 sectors (4K sector size).
+		 *
+		 * So (stripe_len >> sectorsize_bits) should be at most 16,
+		 * not reaching BITS_PER_LONG.
 		 */
-		ASSERT(BITS_PER_LONG >= BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
+		ASSERT(BITS_PER_LONG >= (stripe->stripe_len >>
+					 fs_info->sectorsize_bits));
 
 		ret = btrfs_lookup_csums_bitmap(csum_root, csum_path,
 						stripe->logical, stripe_end,
@@ -1756,7 +1772,7 @@ static u32 stripe_length(const struct scrub_stripe *stripe)
 {
 	ASSERT(stripe->bg);
 
-	return min(BTRFS_STRIPE_LEN,
+	return min(stripe->stripe_len,
 		   stripe->bg->start + stripe->bg->length - stripe->logical);
 }
 
@@ -1766,7 +1782,7 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe)
 	struct btrfs_bio *bbio = NULL;
 	unsigned int nr_sectors = stripe_length(stripe) >> fs_info->sectorsize_bits;
 	const unsigned long has_extent = scrub_bitmap_read_has_extent(stripe);
-	u64 stripe_len = BTRFS_STRIPE_LEN;
+	u64 stripe_len = stripe->stripe_len;
 	int mirror = stripe->mirror_num;
 	int i;
 
@@ -2102,7 +2118,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx,
 	/* For RAID56 write there must be an @bioc allocated. */
 	ASSERT(bioc);
 	rbio = raid56_parity_alloc_scrub_rbio(&bio, bioc, scrub_dev, extent_bitmap,
-				BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
+				map->stripe_len >> fs_info->sectorsize_bits);
 	btrfs_put_bioc(bioc);
 	if (!rbio) {
 		ret = -ENOMEM;
@@ -2113,7 +2129,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx,
 		struct scrub_stripe *stripe = &sctx->raid56_data_stripes[i];
 
 		raid56_parity_cache_data_folios(rbio, stripe->buffer,
-				full_stripe_start + (i << BTRFS_STRIPE_LEN_SHIFT));
+				full_stripe_start + (i << map->stripe_shift));
 	}
 	raid56_parity_submit_scrub_rbio(rbio);
 	wait_for_completion_io(&io_done);
@@ -2172,7 +2188,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 
 		stripe = &sctx->raid56_data_stripes[i];
 		rot = div_u64(full_stripe_start - bg->start,
-			      data_stripes) >> BTRFS_STRIPE_LEN_SHIFT;
+			      data_stripes) >> map->stripe_shift;
 		stripe_index = (i + rot) % map->num_stripes;
 		physical = map->stripes[stripe_index].physical +
 			   btrfs_stripe_nr_to_offset(rot);
@@ -2182,7 +2198,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 		ret = scrub_find_fill_first_stripe(bg, &extent_path, &csum_path,
 				map->stripes[stripe_index].dev, physical, 1,
 				full_stripe_start + btrfs_stripe_nr_to_offset(i),
-				BTRFS_STRIPE_LEN, stripe);
+				map->stripe_len, stripe);
 		if (ret < 0)
 			return ret;
 		/*
@@ -2270,6 +2286,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 static int scrub_simple_mirror(struct scrub_ctx *sctx,
 			       struct btrfs_block_group *bg,
 			       u64 logical_start, u64 logical_length,
+			       u32 stripe_len,
 			       struct btrfs_device *device,
 			       u64 physical, int mirror_num)
 {
@@ -2317,7 +2334,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
 
 		/* queue_scrub_stripe() returned 0, @found_logical must be updated. */
 		ASSERT(found_logical != U64_MAX);
-		cur_logical = found_logical + BTRFS_STRIPE_LEN;
+		cur_logical = found_logical + stripe_len;
 
 		/* Don't hold CPU for too long time */
 		cond_resched();
@@ -2372,6 +2389,7 @@ static int scrub_simple_stripe(struct scrub_ctx *sctx,
 	const u64 orig_logical = simple_stripe_get_logical(map, bg, stripe_index);
 	const u64 orig_physical = map->stripes[stripe_index].physical;
 	const u64 end = btrfs_block_group_end(bg);
+	const u32 stripe_len = map->stripe_len;
 	const int mirror_num = simple_stripe_mirror_num(map, stripe_index);
 	u64 cur_logical = orig_logical;
 	u64 cur_physical = orig_physical;
@@ -2384,18 +2402,27 @@ static int scrub_simple_stripe(struct scrub_ctx *sctx,
 		 * this stripe.
 		 */
 		ret = scrub_simple_mirror(sctx, bg, cur_logical,
-					  BTRFS_STRIPE_LEN, device, cur_physical,
+					  stripe_len, stripe_len,
+					  device, cur_physical,
 					  mirror_num);
 		if (ret)
 			return ret;
 		/* Skip to next stripe which belongs to the target device */
 		cur_logical += logical_increment;
 		/* For physical offset, we just go to next stripe */
-		cur_physical += BTRFS_STRIPE_LEN;
+		cur_physical += stripe_len;
 	}
 	return ret;
 }
 
+static void update_scrub_stripe_len(const struct btrfs_fs_info *fs_info,
+				    const struct btrfs_chunk_map *map,
+				    struct scrub_stripe *stripe)
+{
+	stripe->stripe_len = map->stripe_len;
+	stripe->nr_sectors = map->stripe_len >> fs_info->sectorsize_bits;
+}
+
 static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 					   struct btrfs_block_group *bg,
 					   struct btrfs_chunk_map *map,
@@ -2405,6 +2432,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 	struct btrfs_fs_info *fs_info = sctx->fs_info;
 	const u64 profile = map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK;
 	const u64 chunk_logical = bg->start;
+	const u32 stripe_len = map->stripe_len;
 	int ret;
 	int ret2;
 	u64 physical = map->stripes[stripe_index].physical;
@@ -2447,6 +2475,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 				goto out;
 			sctx->raid56_data_stripes[i].bg = bg;
 			sctx->raid56_data_stripes[i].sctx = sctx;
+			update_scrub_stripe_len(fs_info, map, &sctx->raid56_data_stripes[i]);
 		}
 	}
 	/*
@@ -2467,7 +2496,8 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 		 * @stripe_index.
 		 */
 		ret = scrub_simple_mirror(sctx, bg, bg->start, bg->length,
-				scrub_dev, map->stripes[stripe_index].physical,
+				stripe_len, scrub_dev,
+				map->stripes[stripe_index].physical,
 				stripe_index + 1);
 		offset = 0;
 		goto out;
@@ -2505,7 +2535,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 			ret = scrub_raid56_parity_stripe(sctx, scrub_dev, bg,
 							 map, stripe_logical);
 			spin_lock(&sctx->stat_lock);
-			sctx->stat.last_physical = min(physical + BTRFS_STRIPE_LEN,
+			sctx->stat.last_physical = min(physical + stripe_len,
 						       physical_end);
 			spin_unlock(&sctx->stat_lock);
 			if (ret)
@@ -2521,13 +2551,13 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 		 * We can reuse scrub_simple_mirror() here, as the repair part
 		 * is still based on @mirror_num.
 		 */
-		ret = scrub_simple_mirror(sctx, bg, logical, BTRFS_STRIPE_LEN,
-					  scrub_dev, physical, 1);
+		ret = scrub_simple_mirror(sctx, bg, logical, stripe_len,
+					  stripe_len, scrub_dev, physical, 1);
 		if (ret < 0)
 			goto out;
 next:
 		logical += increment;
-		physical += BTRFS_STRIPE_LEN;
+		physical += stripe_len;
 		spin_lock(&sctx->stat_lock);
 		sctx->stat.last_physical = physical;
 		spin_unlock(&sctx->stat_lock);
@@ -2587,6 +2617,9 @@ static noinline_for_stack int scrub_chunk(struct scrub_ctx *sctx,
 	if (map->chunk_len < dev_extent_len)
 		goto out;
 
+	for (i = 0; i < SCRUB_TOTAL_STRIPES; i++)
+		update_scrub_stripe_len(fs_info, map, &sctx->stripes[i]);
+
 	for (i = 0; i < map->num_stripes; ++i) {
 		if (map->stripes[i].dev->bdev == scrub_dev->bdev &&
 		    map->stripes[i].physical == dev_offset) {
-- 
2.56.0


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

* [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset()
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (3 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 4/9] btrfs: change scrub to use stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  6:00   ` Johannes Thumshirn
  2026-10-21  3:39 ` [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len Qu Wenruo
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

That helper is currently using a fixed BTRFS_STRIPE_LEN, change it to
accept a 'u32 stripe_len'.

Also add an ASSERT() to make sure the @stripe_len passed in is power of
2, and no larger than BTRFS_STRIPE_LEN.

There is a special case where we still pass BTRFS_STRIPE_LEN into
btrfs_stripe_nr_to_offset(), that's for chunk allocation to determine
the minimal device extent size.

In that case, since our future planned map->stripe_len is always power
of 2 and no larger than BTRFS_STRIPE_LEN, we are totally fine to use the
old BTRFS_STRIPE_LEN.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/block-group.c |  3 ++-
 fs/btrfs/scrub.c       | 32 +++++++++++++++++++-------------
 fs/btrfs/volumes.c     | 40 +++++++++++++++++++++++++++-------------
 fs/btrfs/volumes.h     |  5 +++--
 fs/btrfs/zoned.c       |  7 +++++--
 5 files changed, 56 insertions(+), 31 deletions(-)

diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index f2b55ffbdaef..c0d735cee4a8 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -2302,7 +2302,8 @@ int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start,
 
 	/* For RAID5/6 adjust to a full IO stripe length */
 	if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
-		io_stripe_size = btrfs_stripe_nr_to_offset(nr_data_stripes(map));
+		io_stripe_size = btrfs_stripe_nr_to_offset(nr_data_stripes(map),
+							   map->stripe_len);
 
 	buf = kzalloc_objs(u64, map->num_stripes, GFP_NOFS);
 	if (!buf) {
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index c3387e109779..98362c91bce4 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -1425,7 +1425,8 @@ static int get_raid56_logic_offset(u64 physical, int num,
 		u32 stripe_index;
 		u32 rot;
 
-		*offset = last_offset + btrfs_stripe_nr_to_offset(i);
+		*offset = last_offset + btrfs_stripe_nr_to_offset(i,
+							map->stripe_len);
 
 		stripe_nr = (u32)(*offset >> map->stripe_shift) / data_stripes;
 
@@ -1439,7 +1440,7 @@ static int get_raid56_logic_offset(u64 physical, int num,
 		if (stripe_index < num)
 			j++;
 	}
-	*offset = last_offset + btrfs_stripe_nr_to_offset(j);
+	*offset = last_offset + btrfs_stripe_nr_to_offset(j, map->stripe_len);
 	return 1;
 }
 
@@ -1924,7 +1925,8 @@ static void submit_initial_group_read(struct scrub_ctx *sctx,
 	ASSERT(first_slot + nr_stripes <= SCRUB_TOTAL_STRIPES);
 
 	scrub_throttle_dev_io(sctx, sctx->stripes[0].dev,
-			      btrfs_stripe_nr_to_offset(nr_stripes));
+			      btrfs_stripe_nr_to_offset(nr_stripes,
+						sctx->stripes[0].stripe_len));
 	blk_start_plug(&plug);
 	for (int i = 0; i < nr_stripes; i++) {
 		struct scrub_stripe *stripe = &sctx->stripes[first_slot + i];
@@ -2102,7 +2104,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx,
 	struct btrfs_raid_bio *rbio;
 	struct bio bio;
 	const int data_stripes = nr_data_stripes(map);
-	u64 length = btrfs_stripe_nr_to_offset(data_stripes);
+	u64 length = btrfs_stripe_nr_to_offset(data_stripes, map->stripe_len);
 	int ret;
 
 	bio_init(&bio, NULL, NULL, 0, REQ_OP_READ);
@@ -2151,6 +2153,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 	BTRFS_PATH_AUTO_RELEASE(csum_path);
 	struct scrub_stripe *stripe;
 	bool all_empty = true;
+	const u32 stripe_len = map->stripe_len;
 	const int data_stripes = nr_data_stripes(map);
 	unsigned long extent_bitmap = 0;
 	int ret;
@@ -2191,14 +2194,15 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 			      data_stripes) >> map->stripe_shift;
 		stripe_index = (i + rot) % map->num_stripes;
 		physical = map->stripes[stripe_index].physical +
-			   btrfs_stripe_nr_to_offset(rot);
+			   btrfs_stripe_nr_to_offset(rot, stripe_len);
 
 		scrub_reset_stripe(stripe);
 		set_bit(SCRUB_STRIPE_FLAG_NO_REPORT, &stripe->state);
 		ret = scrub_find_fill_first_stripe(bg, &extent_path, &csum_path,
 				map->stripes[stripe_index].dev, physical, 1,
-				full_stripe_start + btrfs_stripe_nr_to_offset(i),
-				map->stripe_len, stripe);
+				full_stripe_start +
+				btrfs_stripe_nr_to_offset(i, stripe_len),
+				stripe_len, stripe);
 		if (ret < 0)
 			return ret;
 		/*
@@ -2207,7 +2211,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
 		 */
 		if (ret > 0) {
 			stripe->logical = full_stripe_start +
-					  btrfs_stripe_nr_to_offset(i);
+					  btrfs_stripe_nr_to_offset(i, stripe_len);
 			stripe->dev = map->stripes[stripe_index].dev;
 			stripe->mirror_num = 1;
 			set_bit(SCRUB_STRIPE_FLAG_INITIALIZED, &stripe->state);
@@ -2348,7 +2352,8 @@ static u64 simple_stripe_full_stripe_len(const struct btrfs_chunk_map *map)
 	ASSERT(map->type & (BTRFS_BLOCK_GROUP_RAID0 |
 			    BTRFS_BLOCK_GROUP_RAID10));
 
-	return btrfs_stripe_nr_to_offset(map->num_stripes / map->sub_stripes);
+	return btrfs_stripe_nr_to_offset(map->num_stripes / map->sub_stripes,
+					 map->stripe_len);
 }
 
 /* Get the logical bytenr for the stripe */
@@ -2364,8 +2369,8 @@ static u64 simple_stripe_get_logical(struct btrfs_chunk_map *map,
 	 * (stripe_index / sub_stripes) gives how many data stripes we need to
 	 * skip.
 	 */
-	return btrfs_stripe_nr_to_offset(stripe_index / map->sub_stripes) +
-	       bg->start;
+	return btrfs_stripe_nr_to_offset(stripe_index / map->sub_stripes,
+					 map->stripe_len) + bg->start;
 }
 
 /* Get the mirror number for the stripe */
@@ -2504,7 +2509,8 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 	}
 	if (profile & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID10)) {
 		ret = scrub_simple_stripe(sctx, bg, map, scrub_dev, stripe_index);
-		offset = btrfs_stripe_nr_to_offset(stripe_index / map->sub_stripes);
+		offset = btrfs_stripe_nr_to_offset(stripe_index / map->sub_stripes,
+						   stripe_len);
 		goto out;
 	}
 
@@ -2519,7 +2525,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
 
 	/* Initialize @offset in case we need to go to out: label */
 	get_raid56_logic_offset(physical, stripe_index, map, &offset, NULL);
-	increment = btrfs_stripe_nr_to_offset(nr_data_stripes(map));
+	increment = btrfs_stripe_nr_to_offset(nr_data_stripes(map), stripe_len);
 
 	/*
 	 * Due to the rotation, for RAID56 it's better to iterate each stripe
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index ff7189ac1c49..6a3ae8a081fe 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -5823,7 +5823,8 @@ static void init_alloc_chunk_ctl_policy_regular(
 	/* We don't want a chunk larger than 10% of writable space */
 	ctl->max_chunk_size = min(mult_perc(fs_devices->total_rw_bytes, 10),
 				  ctl->max_chunk_size);
-	ctl->dev_extent_min = btrfs_stripe_nr_to_offset(ctl->dev_stripes);
+	ctl->dev_extent_min = btrfs_stripe_nr_to_offset(ctl->dev_stripes,
+							BTRFS_STRIPE_LEN);
 }
 
 static void init_alloc_chunk_ctl_policy_zoned(
@@ -6587,7 +6588,8 @@ unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info,
 
 	if (!WARN_ON(IS_ERR(map))) {
 		if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
-			len = btrfs_stripe_nr_to_offset(nr_data_stripes(map));
+			len = btrfs_stripe_nr_to_offset(nr_data_stripes(map),
+							map->stripe_len);
 		btrfs_free_chunk_map(map);
 	}
 	return len;
@@ -6784,6 +6786,7 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 	u64 stripe_end_offset;
 	u64 stripe_offset;
 	u32 stripe_index;
+	u32 stripe_len;
 	u32 factor = 0;
 	u32 sub_stripes = 0;
 	u32 stripes_per_dev = 0;
@@ -6820,6 +6823,7 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 		goto out_free_map;
 	}
 
+	stripe_len = map->stripe_len;
 	offset = logical - map->start;
 	length = min_t(u64, map->start + map->chunk_len - logical, length);
 	*length_ret = length;
@@ -6831,12 +6835,13 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 	stripe_nr = offset >> BTRFS_STRIPE_LEN_SHIFT;
 
 	/* stripe_offset is the offset of this block in its stripe */
-	stripe_offset = offset - btrfs_stripe_nr_to_offset(stripe_nr);
+	stripe_offset = offset - btrfs_stripe_nr_to_offset(stripe_nr,
+							   stripe_len);
 
 	stripe_nr_end = round_up(offset + length, BTRFS_STRIPE_LEN) >>
 			BTRFS_STRIPE_LEN_SHIFT;
 	stripe_cnt = stripe_nr_end - stripe_nr;
-	stripe_end_offset = btrfs_stripe_nr_to_offset(stripe_nr_end) -
+	stripe_end_offset = btrfs_stripe_nr_to_offset(stripe_nr_end, stripe_len) -
 			    (offset + length);
 	/*
 	 * after this, stripe_nr is the number of stripes on this
@@ -6879,12 +6884,14 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 	for (i = 0; i < *num_stripes; i++) {
 		stripes[i].physical =
 			map->stripes[stripe_index].physical +
-			stripe_offset + btrfs_stripe_nr_to_offset(stripe_nr);
+			stripe_offset + btrfs_stripe_nr_to_offset(stripe_nr,
+								  stripe_len);
 		stripes[i].dev = map->stripes[stripe_index].dev;
 
 		if (map->type & (BTRFS_BLOCK_GROUP_RAID0 |
 				 BTRFS_BLOCK_GROUP_RAID10)) {
-			stripes[i].length = btrfs_stripe_nr_to_offset(stripes_per_dev);
+			stripes[i].length = btrfs_stripe_nr_to_offset(stripes_per_dev,
+								      stripe_len);
 
 			if (i / sub_stripes < remaining_stripes)
 				stripes[i].length += BTRFS_STRIPE_LEN;
@@ -7022,6 +7029,8 @@ static void handle_ops_on_dev_replace(struct btrfs_io_context *bioc,
 static u64 btrfs_max_io_len(struct btrfs_chunk_map *map, u64 offset,
 			    struct btrfs_io_geometry *io_geom)
 {
+	const u32 stripe_len = map->stripe_len;
+
 	/*
 	 * Stripe_nr is the stripe where this block falls.  stripe_offset is
 	 * the offset of this block in its stripe.
@@ -7033,7 +7042,8 @@ static u64 btrfs_max_io_len(struct btrfs_chunk_map *map, u64 offset,
 
 	if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
 		unsigned long full_stripe_len =
-			btrfs_stripe_nr_to_offset(nr_data_stripes(map));
+			btrfs_stripe_nr_to_offset(nr_data_stripes(map),
+						  stripe_len);
 
 		/*
 		 * For full stripe start, we use previously calculated
@@ -7045,7 +7055,8 @@ static u64 btrfs_max_io_len(struct btrfs_chunk_map *map, u64 offset,
 		 * not ensured to be power of 2.
 		 */
 		io_geom->raid56_full_stripe_start = btrfs_stripe_nr_to_offset(
-			rounddown(io_geom->stripe_nr, nr_data_stripes(map)));
+			rounddown(io_geom->stripe_nr, nr_data_stripes(map)),
+			stripe_len);
 
 		ASSERT(io_geom->raid56_full_stripe_start + full_stripe_len > offset,
 		       "raid56_full_stripe_start=%llu full_stripe_len=%lu offset=%llu",
@@ -7084,7 +7095,8 @@ static int set_io_stripe(struct btrfs_fs_info *fs_info, u64 logical,
 
 	dst->physical = map->stripes[io_geom->stripe_index].physical +
 			io_geom->stripe_offset +
-			btrfs_stripe_nr_to_offset(io_geom->stripe_nr);
+			btrfs_stripe_nr_to_offset(io_geom->stripe_nr,
+						  map->stripe_len);
 	return 0;
 }
 
@@ -7207,7 +7219,7 @@ static void map_blocks_raid56_write(struct btrfs_chunk_map *map,
 	/* Return the length to the full stripe end. */
 	*length = min(logical + *length,
 		      io_geom->raid56_full_stripe_start + map->start +
-		      btrfs_stripe_nr_to_offset(data_stripes)) -
+		      btrfs_stripe_nr_to_offset(data_stripes, map->stripe_len)) -
 		logical;
 	io_geom->stripe_index = 0;
 	io_geom->stripe_offset = 0;
@@ -7434,7 +7446,8 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
 		 */
 		bioc->full_stripe_logical = map->start +
 			btrfs_stripe_nr_to_offset(io_geom.stripe_nr *
-						  nr_data_stripes(map));
+						  nr_data_stripes(map),
+						  map->stripe_len);
 		for (int i = 0; i < io_geom.num_stripes; i++) {
 			struct btrfs_io_stripe *dst = &bioc->stripes[i];
 			u32 stripe_index;
@@ -7444,7 +7457,8 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
 			dst->physical =
 				map->stripes[stripe_index].physical +
 				io_geom.stripe_offset +
-				btrfs_stripe_nr_to_offset(io_geom.stripe_nr);
+				btrfs_stripe_nr_to_offset(io_geom.stripe_nr,
+							  map->stripe_len);
 		}
 	} else {
 		/*
@@ -9070,7 +9084,7 @@ static void map_raid56_repair_block(struct btrfs_io_context *bioc,
 
 	for (i = 0; i < data_stripes; i++) {
 		u64 stripe_start = bioc->full_stripe_logical +
-				   btrfs_stripe_nr_to_offset(i);
+				   btrfs_stripe_nr_to_offset(i, bioc->stripe_len);
 
 		if (logical >= stripe_start &&
 		    logical < stripe_start + BTRFS_STRIPE_LEN)
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 9356cec00a90..75b1179776c8 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -722,9 +722,10 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
  * @stripe_nr is u32, with left shift it can overflow u32 for chunks larger
  * than 4G.  This does the proper type cast to avoid overflow.
  */
-static inline u64 btrfs_stripe_nr_to_offset(u32 stripe_nr)
+static inline u64 btrfs_stripe_nr_to_offset(u32 stripe_nr, u32 stripe_len)
 {
-	return (u64)stripe_nr << BTRFS_STRIPE_LEN_SHIFT;
+	ASSERT(is_power_of_2(stripe_len) && stripe_len <= BTRFS_STRIPE_LEN);
+	return (u64)stripe_nr << ilog2(stripe_len);
 }
 
 void btrfs_get_bioc(struct btrfs_io_context *bioc);
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 1cda88332284..37084c8d8d75 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -1630,7 +1630,9 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 
 		if (zone_info[i].alloc_offset == WP_CONVENTIONAL) {
 			has_conventional = true;
-			zone_info[i].alloc_offset = btrfs_stripe_nr_to_offset(stripe_nr);
+			zone_info[i].alloc_offset =
+				btrfs_stripe_nr_to_offset(stripe_nr,
+							  map->stripe_len);
 
 			if (stripe_index > i)
 				zone_info[i].alloc_offset += BTRFS_STRIPE_LEN;
@@ -1772,7 +1774,8 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 
 		if (raid0_allocs[idx] == WP_CONVENTIONAL) {
 			has_conventional = true;
-			raid0_allocs[idx] = btrfs_stripe_nr_to_offset(stripe_nr);
+			raid0_allocs[idx] = btrfs_stripe_nr_to_offset(stripe_nr,
+								map->stripe_len);
 
 			if (stripe_index > idx)
 				raid0_allocs[idx] += BTRFS_STRIPE_LEN;
-- 
2.56.0


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

* [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (4 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset() Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  6:01   ` Johannes Thumshirn
  2026-10-21  3:39 ` [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len Qu Wenruo
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

There are still some BTRFS_STRIPE_LEN usage remaining, but they are all
for chunk allocation.

There is one more refactor for is_same_chunk_map(), as there are more
members to compare, the parameter list can go longer and longer.

Instead of passing a new @stripe_len member, pass extent_buffer and
btrfs_chunk pointers, and grab the needed member inside that function.

And doing "if (unlikely()) return false" for each member check.

Although this make is_same_chunk_map() more complex, considering it's
only called for system chunks, it should not bring too much overhead.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/block-group.c |  6 ++---
 fs/btrfs/volumes.c     | 52 ++++++++++++++++++++++++------------------
 2 files changed, 33 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index c0d735cee4a8..092cdbedf39f 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -2297,7 +2297,7 @@ int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start,
 		return -EIO;
 
 	data_stripe_length = map->dev_extent_size;
-	io_stripe_size = BTRFS_STRIPE_LEN;
+	io_stripe_size = map->stripe_len;
 	chunk_start = map->start;
 
 	/* For RAID5/6 adjust to a full IO stripe length */
@@ -2322,9 +2322,9 @@ int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start,
 			continue;
 
 		stripe_nr = (physical - map->stripes[i].physical) >>
-			    BTRFS_STRIPE_LEN_SHIFT;
+			    map->stripe_shift;
 		offset = (physical - map->stripes[i].physical) &
-			 BTRFS_STRIPE_LEN_MASK;
+			 (map->stripe_len - 1);
 
 		if (map->type & (BTRFS_BLOCK_GROUP_RAID0 |
 				 BTRFS_BLOCK_GROUP_RAID10))
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 6a3ae8a081fe..ce78a9441c64 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6168,8 +6168,8 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp)
 	RB_CLEAR_NODE(&map->rb_node);
 
 	/*
-	 * Initialize the default stripe len, selftests will utilize them
-	 * without an on-disk chunk item.
+	 * Initialize the default stripe len, selftests and some other call
+	 * sites relies on the sane default value.
 	 */
 	map->stripe_len = BTRFS_STRIPE_LEN;
 	map->stripe_shift = ilog2(map->stripe_len);
@@ -6210,8 +6210,6 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans,
 	map->on_disk_type = type;
 	map->sub_stripes = ctl->sub_stripes;
 	map->num_stripes = ctl->num_stripes;
-	map->stripe_len = BTRFS_STRIPE_LEN;
-	map->stripe_shift = ilog2(BTRFS_STRIPE_LEN);
 	set_real_chunk_type(map);
 
 	for (int i = 0; i < ctl->ndevs; i++) {
@@ -6393,8 +6391,8 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans,
 	btrfs_set_stack_chunk_stripe_len(chunk, map->stripe_len);
 	btrfs_set_stack_chunk_type(chunk, map->on_disk_type);
 	btrfs_set_stack_chunk_num_stripes(chunk, map->num_stripes);
-	btrfs_set_stack_chunk_io_align(chunk, BTRFS_STRIPE_LEN);
-	btrfs_set_stack_chunk_io_width(chunk, BTRFS_STRIPE_LEN);
+	btrfs_set_stack_chunk_io_align(chunk, map->stripe_len);
+	btrfs_set_stack_chunk_io_width(chunk, map->stripe_len);
 	btrfs_set_stack_chunk_sector_size(chunk, fs_info->sectorsize);
 	btrfs_set_stack_chunk_sub_stripes(chunk, map->sub_stripes);
 
@@ -6832,14 +6830,14 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 	 * stripe_nr counts the total number of stripes we have to stride
 	 * to get to this block
 	 */
-	stripe_nr = offset >> BTRFS_STRIPE_LEN_SHIFT;
+	stripe_nr = offset >> map->stripe_shift;
 
 	/* stripe_offset is the offset of this block in its stripe */
 	stripe_offset = offset - btrfs_stripe_nr_to_offset(stripe_nr,
 							   stripe_len);
 
-	stripe_nr_end = round_up(offset + length, BTRFS_STRIPE_LEN) >>
-			BTRFS_STRIPE_LEN_SHIFT;
+	stripe_nr_end = round_up(offset + length, stripe_len) >>
+			map->stripe_shift;
 	stripe_cnt = stripe_nr_end - stripe_nr;
 	stripe_end_offset = btrfs_stripe_nr_to_offset(stripe_nr_end, stripe_len) -
 			    (offset + length);
@@ -6894,7 +6892,7 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
 								      stripe_len);
 
 			if (i / sub_stripes < remaining_stripes)
-				stripes[i].length += BTRFS_STRIPE_LEN;
+				stripes[i].length += stripe_len;
 
 			/*
 			 * Special for the first stripe and
@@ -7035,8 +7033,8 @@ static u64 btrfs_max_io_len(struct btrfs_chunk_map *map, u64 offset,
 	 * Stripe_nr is the stripe where this block falls.  stripe_offset is
 	 * the offset of this block in its stripe.
 	 */
-	io_geom->stripe_offset = offset & BTRFS_STRIPE_LEN_MASK;
-	io_geom->stripe_nr = offset >> BTRFS_STRIPE_LEN_SHIFT;
+	io_geom->stripe_offset = offset & (stripe_len - 1);
+	io_geom->stripe_nr = offset >> map->stripe_shift;
 	ASSERT(io_geom->stripe_offset < U32_MAX,
 	       "stripe_offset=%llu", io_geom->stripe_offset);
 
@@ -7077,7 +7075,7 @@ static u64 btrfs_max_io_len(struct btrfs_chunk_map *map, u64 offset,
 	 * a single disk).
 	 */
 	if (map->type & BTRFS_BLOCK_GROUP_STRIPE_MASK)
-		return BTRFS_STRIPE_LEN - io_geom->stripe_offset;
+		return stripe_len - io_geom->stripe_offset;
 	return U64_MAX;
 }
 
@@ -7741,11 +7739,20 @@ static struct btrfs_device *handle_missing_device(struct btrfs_fs_info *fs_info,
 	return dev;
 }
 
-static bool is_same_chunk_map(const struct btrfs_chunk_map *map, u64 logical,
-			      u64 length, u64 type, u16 num_stripes)
+static bool is_same_chunk_map(const struct btrfs_chunk_map *map,
+			      const struct extent_buffer *leaf,
+			      const struct btrfs_chunk *chunk,
+			      u64 logical)
 {
-	if (map->start != logical || map->chunk_len != length ||
-	    map->on_disk_type != type || map->num_stripes != num_stripes)
+	if (unlikely(map->start != logical))
+		return false;
+	if (unlikely(map->chunk_len != btrfs_chunk_length(leaf, chunk)))
+		return false;
+	if (unlikely(map->on_disk_type != btrfs_chunk_type(leaf, chunk)))
+		return false;
+	if (unlikely(map->stripe_len != btrfs_chunk_stripe_len(leaf, chunk)))
+		return false;
+	if (unlikely(map->num_stripes != btrfs_chunk_num_stripes(leaf, chunk)))
 		return false;
 	return true;
 }
@@ -7760,6 +7767,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
 	u64 length;
 	u64 devid;
 	u64 type;
+	u32 stripe_len;
 	u8 uuid[BTRFS_UUID_SIZE];
 	int index;
 	int num_stripes;
@@ -7770,6 +7778,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
 	length = btrfs_chunk_length(leaf, chunk);
 	type = btrfs_chunk_type(leaf, chunk);
 	index = btrfs_bg_flags_to_raid_index(type);
+	stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
 	num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
 
 #if BITS_PER_LONG == 32
@@ -7783,8 +7792,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
 
 	/* Already mapped? Check if the existing one matches. */
 	if (map) {
-		if (unlikely(!is_same_chunk_map(map, logical, length,
-						type, num_stripes))) {
+		if (unlikely(!is_same_chunk_map(map, leaf, chunk, logical))) {
 			btrfs_err(fs_info,
 		"existing chunk %llu doesn't match the new one at %llu",
 				  map->start, logical);
@@ -7804,7 +7812,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
 	map->chunk_len = length;
 	map->num_stripes = num_stripes;
 	map->on_disk_type = type;
-	map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+	map->stripe_len = stripe_len;
 	map->stripe_shift = ilog2(map->stripe_len);
 	/*
 	 * We can't use the sub_stripes value, as for profiles other than
@@ -9087,14 +9095,14 @@ static void map_raid56_repair_block(struct btrfs_io_context *bioc,
 				   btrfs_stripe_nr_to_offset(i, bioc->stripe_len);
 
 		if (logical >= stripe_start &&
-		    logical < stripe_start + BTRFS_STRIPE_LEN)
+		    logical < stripe_start + bioc->stripe_len)
 			break;
 	}
 	ASSERT(i < data_stripes, "i=%d data_stripes=%d", i, data_stripes);
 	smap->dev = bioc->stripes[i].dev;
 	smap->physical = bioc->stripes[i].physical +
 			((logical - bioc->full_stripe_logical) &
-			 BTRFS_STRIPE_LEN_MASK);
+			 (bioc->stripe_len - 1));
 }
 
 /*
-- 
2.56.0


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

* [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (5 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  6:46   ` Johannes Thumshirn
  2026-10-21  3:39 ` [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len Qu Wenruo
  2026-10-21  3:39 ` [PATCH v2 9/9] btrfs: introduce extra limits on btrfs_chunk::stripe_len Qu Wenruo
  8 siblings, 1 reply; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

So that raid56 can support stripe length other than BTRFS_STRIPE_LEN.

There are several extra things to note:

- btrfs_raid_bio::stripe_npages is removed
  With stripe_len < BTRFS_STRIPE_LEN, there is no guarantee that a page
  won't cross multiple stripes.

  E.g. with 4K stripe_len and sectorsize, 2 data stripes, with 64K page
  size, one page will cover the full stripe.

  So stripe_npages no longer makes sense, just remove it.

- alloc_rbio_data_pages()/alloc_rbio_parity_pages() fallback
  For the same above reason, a page can contain both data and parity
  blocks.
  In that case we can no longer only allocate pages for data/parity.

  Add a fallback that if stripe_len is smaller than page size, always
  allocate all the pages.

  This is not the best solution, but should be good enough for now until
  the variable stripe_len is properly tested.

- steal_rbio() fallback
  If stripe_len is smaller than PAGE_SIZE, it's hard to only steal the
  data pages.

  Again skip the stealing if stripe_len is smaller than PAGE_SIZE.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/raid56.c | 57 +++++++++++++++++++++++++++++++----------------
 fs/btrfs/raid56.h |  7 +-----
 2 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 5ec31299f1c3..080be467c6fc 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -395,8 +395,9 @@ static bool is_data_stripe_page(struct btrfs_raid_bio *rbio, int page_nr)
 	const int sector_nr = page_nr_to_sector_nr(rbio, page_nr);
 
 	/*
-	 * We have ensured PAGE_SIZE is aligned with sectorsize, thus
-	 * we won't have a page which is half data half parity.
+	 * The caller has ensured stripe_len >= PAGE_SIZE, and PAGE_SIZE is
+	 * aligned with sectorsize, thus we won't have a page which is half
+	 * data half parity.
 	 *
 	 * Thus if the first sector of the page belongs to data stripes, then
 	 * the full page belongs to data stripes.
@@ -417,6 +418,9 @@ static void steal_rbio(struct btrfs_raid_bio *src, struct btrfs_raid_bio *dest)
 
 	if (!test_bit(RBIO_CACHE_READY_BIT, &src->flags))
 		return;
+	/* Data and P/Q can be mixed into a page, skip stealing. */
+	if (src->bioc->stripe_len < PAGE_SIZE)
+		return;
 
 	for (i = 0; i < dest->nr_pages; i++) {
 		struct page *p = src->stripe_pages[i];
@@ -624,12 +628,13 @@ static void cache_rbio(struct btrfs_raid_bio *rbio)
 static int rbio_is_full(struct btrfs_raid_bio *rbio)
 {
 	unsigned long size = rbio->bio_list_bytes;
+	const u32 stripe_len = rbio->bioc->stripe_len;
 	int ret = 1;
 
 	spin_lock(&rbio->bio_list_lock);
-	if (size != rbio->nr_data * BTRFS_STRIPE_LEN)
+	if (size != rbio->nr_data * stripe_len)
 		ret = 0;
-	BUG_ON(size > rbio->nr_data * BTRFS_STRIPE_LEN);
+	BUG_ON(size > rbio->nr_data * stripe_len);
 	spin_unlock(&rbio->bio_list_lock);
 
 	return ret;
@@ -1041,11 +1046,12 @@ static phys_addr_t sector_paddr_in_rbio(struct btrfs_raid_bio *rbio,
 static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
 					 struct btrfs_io_context *bioc)
 {
+	const u32 stripe_len = bioc->stripe_len;
 	const unsigned int real_stripes = bioc->num_stripes - bioc->replace_nr_stripes;
-	const unsigned int stripe_npages = BTRFS_STRIPE_LEN >> PAGE_SHIFT;
-	const unsigned int num_pages = stripe_npages * real_stripes;
+	const unsigned int num_pages = round_up(stripe_len * real_stripes, PAGE_SIZE) >>
+				       PAGE_SHIFT;
 	const unsigned int stripe_nsectors =
-		BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits;
+		stripe_len >> fs_info->sectorsize_bits;
 	const unsigned int num_sectors = stripe_nsectors * real_stripes;
 	const unsigned int step = min(fs_info->sectorsize, PAGE_SIZE);
 	const unsigned int sector_nsteps = fs_info->sectorsize / step;
@@ -1105,7 +1111,6 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
 	rbio->nr_pages = num_pages;
 	rbio->nr_sectors = num_sectors;
 	rbio->real_stripes = real_stripes;
-	rbio->stripe_npages = stripe_npages;
 	rbio->stripe_nsectors = stripe_nsectors;
 	rbio->sector_nsteps = sector_nsteps;
 	refcount_set(&rbio->refs, 1);
@@ -1134,9 +1139,17 @@ static int alloc_rbio_pages(struct btrfs_raid_bio *rbio)
 /* only allocate pages for p/q stripes */
 static int alloc_rbio_parity_pages(struct btrfs_raid_bio *rbio)
 {
-	const int data_pages = rbio->nr_data * rbio->stripe_npages;
+	int data_pages;
 	int ret;
 
+	/*
+	 * Page size larger than stripe size, we can not only allocate
+	 * pages for P/Q stripes.
+	 */
+	if (rbio->bioc->stripe_len < PAGE_SIZE)
+		return alloc_rbio_pages(rbio);
+
+	data_pages = rbio->nr_data * (rbio->bioc->stripe_len >> PAGE_SHIFT);
 	ret = btrfs_alloc_page_array(rbio->nr_pages - data_pages,
 				     rbio->stripe_pages + data_pages, GFP_NOFS);
 	if (ret < 0)
@@ -1274,7 +1287,7 @@ static int rbio_add_io_paddrs(struct btrfs_raid_bio *rbio, struct bio_list *bio_
 
 	/* put a new bio on the list */
 	bio = bio_alloc(stripe->dev->bdev,
-			max(BTRFS_STRIPE_LEN >> PAGE_SHIFT, 1),
+			max(rbio->bioc->stripe_len >> PAGE_SHIFT, 1),
 			op, GFP_NOFS);
 	bio->bi_iter.bi_sector = disk_start >> SECTOR_SHIFT;
 	bio->bi_private = rbio;
@@ -1715,9 +1728,13 @@ static void submit_read_wait_bio_list(struct btrfs_raid_bio *rbio,
 
 static int alloc_rbio_data_pages(struct btrfs_raid_bio *rbio)
 {
-	const int data_pages = rbio->nr_data * rbio->stripe_npages;
+	int data_pages;
 	int ret;
 
+	if (rbio->bioc->stripe_len < PAGE_SIZE)
+		return alloc_rbio_pages(rbio);
+
+	data_pages = rbio->nr_data * (rbio->bioc->stripe_len >> PAGE_SHIFT);
 	ret = btrfs_alloc_page_array(data_pages, rbio->stripe_pages, GFP_NOFS);
 	if (ret < 0)
 		return ret;
@@ -1804,7 +1821,7 @@ static void rbio_add_bio(struct btrfs_raid_bio *rbio, struct bio *orig_bio)
 
 	ASSERT_RBIO_LOGICAL(orig_logical >= full_stripe_start &&
 			    orig_logical + orig_len <= full_stripe_start +
-			    rbio->nr_data * BTRFS_STRIPE_LEN,
+			    rbio->nr_data * rbio->bioc->stripe_len,
 			    rbio, orig_logical);
 
 	bio_list_add(&rbio->bio_list, orig_bio);
@@ -3012,6 +3029,7 @@ void raid56_parity_cache_data_folios(struct btrfs_raid_bio *rbio,
 	struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
 	const u64 offset_in_full_stripe = data_logical -
 					  rbio->bioc->full_stripe_logical;
+	const u32 stripe_len = rbio->bioc->stripe_len;
 	int ret;
 
 	/*
@@ -3027,19 +3045,20 @@ void raid56_parity_cache_data_folios(struct btrfs_raid_bio *rbio,
 		return;
 
 	/* data_logical must be at stripe boundary and inside the full stripe. */
-	ASSERT(IS_ALIGNED(offset_in_full_stripe, BTRFS_STRIPE_LEN));
-	ASSERT(offset_in_full_stripe < (rbio->nr_data << BTRFS_STRIPE_LEN_SHIFT));
+	ASSERT(IS_ALIGNED(offset_in_full_stripe, stripe_len));
+	ASSERT(offset_in_full_stripe < (rbio->nr_data << ilog2(stripe_len)));
 
 	for (unsigned int cur_off = offset_in_full_stripe;
-	     cur_off < offset_in_full_stripe + BTRFS_STRIPE_LEN;
+	     cur_off < offset_in_full_stripe + stripe_len;
 	     cur_off += PAGE_SIZE) {
 		const unsigned int pindex = cur_off >> PAGE_SHIFT;
+		const u32 cur_len = min(stripe_len, PAGE_SIZE);
 
-		ASSERT(cur_off - offset_in_full_stripe + PAGE_SIZE <= BTRFS_STRIPE_LEN);
-		memcpy_to_page(rbio->stripe_pages[pindex], 0,
-			       vaddr + cur_off - offset_in_full_stripe, PAGE_SIZE);
+		ASSERT(cur_off - offset_in_full_stripe + cur_len <= stripe_len);
+		memcpy_to_page(rbio->stripe_pages[pindex], offset_in_page(cur_off),
+			       vaddr + cur_off - offset_in_full_stripe, cur_len);
 	}
 	bitmap_set(rbio->stripe_uptodate_bitmap,
 		   offset_in_full_stripe >> fs_info->sectorsize_bits,
-		   BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
+		   stripe_len >> fs_info->sectorsize_bits);
 }
diff --git a/fs/btrfs/raid56.h b/fs/btrfs/raid56.h
index 8542648199f1..8700910b8dc9 100644
--- a/fs/btrfs/raid56.h
+++ b/fs/btrfs/raid56.h
@@ -60,8 +60,6 @@ enum btrfs_rbio_ops {
  * stripe_pages[] array stores all the pages covering the full stripe, including
  * data and P/Q pages.
  * stripe_pages[0] is the first page of the first data stripe.
- * stripe_pages[BTRFS_STRIPE_LEN / PAGE_SIZE] is the first page of the second
- * data stripe.
  *
  * Some pointers inside stripe_pages[] can be NULL, e.g. for a full stripe write
  * (the bio covers all data stripes) there is no need to allocate pages for
@@ -81,7 +79,7 @@ enum btrfs_rbio_ops {
  *
  * - sector_nr
  *   Starts from 0 (representing the first sector of the stripe), ends
- *   at BTRFS_STRIPE_LEN / sectorsize - 1.
+ *   at stripe_len / sectorsize - 1.
  *
  * - step_nr
  *   A step is min(sector_size, PAGE_SIZE).
@@ -153,9 +151,6 @@ struct btrfs_raid_bio {
 	/* Number of all stripes (including P/Q) */
 	u8 real_stripes;
 
-	/* How many pages there are for each stripe */
-	u8 stripe_npages;
-
 	/* How many sectors there are for each stripe */
 	u8 stripe_nsectors;
 
-- 
2.56.0


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

* [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (6 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  2026-10-21  6:47   ` Johannes Thumshirn
  2026-10-21  3:39 ` [PATCH v2 9/9] btrfs: introduce extra limits on btrfs_chunk::stripe_len Qu Wenruo
  8 siblings, 1 reply; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs

There are still several call sites using BTRFS_STRIPE_LEN, but that's
when doing the basic zoned checks.

For other call sites with a btrfs_chunk_map, use map->stripe_len instead
of the hardcoded BTRFS_STRIPE_LEN.

And since we're here, replace the following pattern

	if (val & BTRFS_STRIPE_LEN_MASK)

with
	if (!IS_ALIGNED(val, stripe_len))

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/zoned.c | 55 ++++++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 37084c8d8d75..6b6228aaa5fa 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -1576,6 +1576,8 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 					u64 last_alloc)
 {
 	struct btrfs_fs_info *fs_info = bg->fs_info;
+	const u32 stripe_len = map->stripe_len;
+	const u8 stripe_shift = map->stripe_shift;
 	u64 stripe_nr = 0, stripe_offset = 0;
 	u64 prev_offset = 0;
 	u32 stripe_index = 0;
@@ -1599,18 +1601,18 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 		    zone_info[i].alloc_offset == WP_CONVENTIONAL)
 			continue;
 
-		stripe_nr = zone_info[i].alloc_offset >> BTRFS_STRIPE_LEN_SHIFT;
-		stripe_offset = zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK;
+		stripe_nr = zone_info[i].alloc_offset >> stripe_shift;
+		stripe_offset = zone_info[i].alloc_offset & (stripe_len - 1);
 		if (stripe_offset == 0 && stripe_nr > 0) {
 			stripe_nr--;
-			stripe_offset = BTRFS_STRIPE_LEN;
+			stripe_offset = stripe_len;
 		}
-		alloc = ((stripe_nr * map->num_stripes + i) << BTRFS_STRIPE_LEN_SHIFT) +
+		alloc = ((stripe_nr * map->num_stripes + i) << stripe_shift) +
 			stripe_offset;
 		last_alloc = max(last_alloc, alloc);
 
 		/* Partially written stripe found. It should be last. */
-		if (zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK)
+		if (!IS_ALIGNED(zone_info[i].alloc_offset, stripe_len))
 			break;
 	}
 	stripe_nr = 0;
@@ -1619,8 +1621,8 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 	if (last_alloc) {
 		u32 factor = map->num_stripes;
 
-		stripe_nr = last_alloc >> BTRFS_STRIPE_LEN_SHIFT;
-		stripe_offset = last_alloc & BTRFS_STRIPE_LEN_MASK;
+		stripe_nr = last_alloc >> stripe_shift;
+		stripe_offset = last_alloc & (stripe_len - 1);
 		stripe_nr = div_u64_rem(stripe_nr, factor, &stripe_index);
 	}
 
@@ -1631,11 +1633,10 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 		if (zone_info[i].alloc_offset == WP_CONVENTIONAL) {
 			has_conventional = true;
 			zone_info[i].alloc_offset =
-				btrfs_stripe_nr_to_offset(stripe_nr,
-							  map->stripe_len);
+				btrfs_stripe_nr_to_offset(stripe_nr, stripe_len);
 
 			if (stripe_index > i)
-				zone_info[i].alloc_offset += BTRFS_STRIPE_LEN;
+				zone_info[i].alloc_offset += stripe_len;
 			else if (stripe_index == i)
 				zone_info[i].alloc_offset += stripe_offset;
 		}
@@ -1650,7 +1651,8 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 			}
 
 			if (unlikely(has_partial &&
-				     (zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK))) {
+				     !IS_ALIGNED(zone_info[i].alloc_offset,
+						 stripe_len))) {
 				btrfs_err(fs_info,
 				"zoned: multiple partial written stripe found in block group %llu",
 					  bg->start);
@@ -1659,7 +1661,7 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 		}
 		prev_offset = zone_info[i].alloc_offset;
 
-		if ((zone_info[i].alloc_offset & BTRFS_STRIPE_LEN_MASK) != 0)
+		if (!IS_ALIGNED(zone_info[i].alloc_offset, stripe_len))
 			has_partial = true;
 
 		if (test_bit(0, active) != test_bit(i, active)) {
@@ -1675,7 +1677,7 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 
 	/* Check if all devices stay in the same stripe row. */
 	if (unlikely(zone_info[0].alloc_offset -
-		     zone_info[map->num_stripes - 1].alloc_offset > BTRFS_STRIPE_LEN)) {
+		     zone_info[map->num_stripes - 1].alloc_offset > stripe_len)) {
 		btrfs_err(fs_info, "zoned: stripe gap too large in block group %llu", bg->start);
 		return -EIO;
 	}
@@ -1699,6 +1701,8 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 	u64 AUTO_KFREE(raid0_allocs);
 	u64 stripe_nr = 0, stripe_offset = 0;
 	u32 stripe_index = 0;
+	const u32 stripe_len = map->stripe_len;
+	const u8 stripe_shift = map->stripe_shift;
 	bool has_partial = false, has_conventional = false;
 	u64 prev_offset = 0;
 
@@ -1746,16 +1750,16 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 			return -EIO;
 		}
 
-		stripe_nr = alloc >> BTRFS_STRIPE_LEN_SHIFT;
-		stripe_offset = alloc & BTRFS_STRIPE_LEN_MASK;
+		stripe_nr = alloc >> stripe_shift;
+		stripe_offset = alloc & (stripe_len - 1);
 		if (stripe_offset == 0 && stripe_nr > 0) {
 			stripe_nr--;
-			stripe_offset = BTRFS_STRIPE_LEN;
+			stripe_offset = stripe_len;
 		}
 
 		alloc = ((stripe_nr * (map->num_stripes / map->sub_stripes) +
-			  (i / map->sub_stripes)) <<
-			 BTRFS_STRIPE_LEN_SHIFT) + stripe_offset;
+			  (i / map->sub_stripes)) << stripe_shift) +
+			stripe_offset;
 		last_alloc = max(last_alloc, alloc);
 	}
 	stripe_nr = 0;
@@ -1764,8 +1768,8 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 	if (last_alloc) {
 		u32 factor = map->num_stripes / map->sub_stripes;
 
-		stripe_nr = last_alloc >> BTRFS_STRIPE_LEN_SHIFT;
-		stripe_offset = last_alloc & BTRFS_STRIPE_LEN_MASK;
+		stripe_nr = last_alloc >> stripe_shift;
+		stripe_offset = last_alloc & (stripe_len - 1);
 		stripe_nr = div_u64_rem(stripe_nr, factor, &stripe_index);
 	}
 
@@ -1775,10 +1779,10 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 		if (raid0_allocs[idx] == WP_CONVENTIONAL) {
 			has_conventional = true;
 			raid0_allocs[idx] = btrfs_stripe_nr_to_offset(stripe_nr,
-								map->stripe_len);
+								      stripe_len);
 
 			if (stripe_index > idx)
-				raid0_allocs[idx] += BTRFS_STRIPE_LEN;
+				raid0_allocs[idx] += stripe_len;
 			else if (stripe_index == idx)
 				raid0_allocs[idx] += stripe_offset;
 		}
@@ -1794,7 +1798,8 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 				}
 
 				if (unlikely(has_partial &&
-					     (raid0_allocs[idx] & BTRFS_STRIPE_LEN_MASK))) {
+					     !IS_ALIGNED(raid0_allocs[idx],
+							 stripe_len))) {
 					btrfs_err(fs_info,
 					"zoned: multiple partial written stripe found in block group %llu",
 						  bg->start);
@@ -1803,7 +1808,7 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 			}
 			prev_offset = raid0_allocs[idx];
 
-			if ((raid0_allocs[idx] & BTRFS_STRIPE_LEN_MASK) != 0)
+			if (!IS_ALIGNED(raid0_allocs[idx], stripe_len))
 				has_partial = true;
 		}
 
@@ -1826,7 +1831,7 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
 
 	/* Check if all devices stay in the same stripe row. */
 	if (unlikely(zone_info[0].alloc_offset -
-		     zone_info[map->num_stripes - 1].alloc_offset > BTRFS_STRIPE_LEN)) {
+		     zone_info[map->num_stripes - 1].alloc_offset > stripe_len)) {
 		btrfs_err(fs_info, "zoned: stripe gap too large in block group %llu",
 			  bg->start);
 		return -EIO;
-- 
2.56.0


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

* [PATCH v2 9/9] btrfs: introduce extra limits on btrfs_chunk::stripe_len
  2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
                   ` (7 preceding siblings ...)
  2026-10-21  3:39 ` [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len Qu Wenruo
@ 2026-10-21  3:39 ` Qu Wenruo
  8 siblings, 0 replies; 15+ messages in thread
From: Qu Wenruo @ 2026-10-21  3:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Johannes Thumshirn

The new limits are:

- Stripe_len must be no smaller than sectorsize
  As sectorsize is still the minimal unit.

- Stripe_len must be no larger than BTRFS_STRIPE_LEN
  We still have a lot of things requiring on BTRFS_STRIPE_LEN alignment,
  e.g. dev extent and chunk allocation.
  A stripe_len larger than BTRFS_STRIPE_LEN will cause problems for the
  existing chunk allocator.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/tree-checker.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 503b6be90dda..61ea8f42c9f2 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1044,7 +1044,18 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info,
 			  logical, length);
 		return -EUCLEAN;
 	}
-	if (unlikely(!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN)) {
+
+	/*
+	 * For now the "stripe_len != BTRFS_STRIPE_LEN" will cover all cases,
+	 * but the power_of_2 and stripe_len >= sectorsize and
+	 * stripe_len <= BTRFS_STRIPE_LEN are the requirements for future
+	 * stripe_len support.
+	 *
+	 * In the future, "stripe_len != BTRFS_STRIPE_LEN" will be removed.
+	 */
+	if (unlikely(stripe_len != BTRFS_STRIPE_LEN ||
+		     !is_power_of_2(stripe_len) || stripe_len < sectorsize ||
+		     stripe_len > BTRFS_STRIPE_LEN)) {
 		chunk_err(fs_info, leaf, chunk, logical,
 			  "invalid chunk stripe length: %llu",
 			  stripe_len);
-- 
2.56.0


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

* Re: [PATCH v2 4/9] btrfs: change scrub to use stripe_len
  2026-10-21  3:39 ` [PATCH v2 4/9] btrfs: change scrub to use stripe_len Qu Wenruo
@ 2026-10-21  5:58   ` Johannes Thumshirn
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Thumshirn @ 2026-10-21  5:58 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs

Looks good,
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>

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

* Re: [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset()
  2026-10-21  3:39 ` [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset() Qu Wenruo
@ 2026-10-21  6:00   ` Johannes Thumshirn
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Thumshirn @ 2026-10-21  6:00 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs

Looks good,
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>

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

* Re: [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len
  2026-10-21  3:39 ` [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len Qu Wenruo
@ 2026-10-21  6:01   ` Johannes Thumshirn
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Thumshirn @ 2026-10-21  6:01 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs

Looks good,
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>

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

* Re: [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len
  2026-10-21  3:39 ` [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len Qu Wenruo
@ 2026-10-21  6:46   ` Johannes Thumshirn
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Thumshirn @ 2026-10-21  6:46 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs

Looks good,
Reviewed-by: Johannes Thusmhirn <johannes.thumshirn@wdc.com>

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

* Re: [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len
  2026-10-21  3:39 ` [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len Qu Wenruo
@ 2026-10-21  6:47   ` Johannes Thumshirn
  0 siblings, 0 replies; 15+ messages in thread
From: Johannes Thumshirn @ 2026-10-21  6:47 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs

Looks good,
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>

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

end of thread, other threads:[~2026-10-21  6:47 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-21  3:39 [PATCH v2 0/9] btrfs: prepare for variable stripe_len Qu Wenruo
2026-10-21  3:39 ` [PATCH v2 1/9] btrfs: reduce the width of num_stripes and sub_stripes Qu Wenruo
2026-10-21  3:39 ` [PATCH v2 2/9] btrfs: load the on-disk btrfs_chunk::stripe_len Qu Wenruo
2026-10-21  3:39 ` [PATCH v2 3/9] btrfs: add btrfs_io_context::stripe_len and load it Qu Wenruo
2026-10-21  3:39 ` [PATCH v2 4/9] btrfs: change scrub to use stripe_len Qu Wenruo
2026-10-21  5:58   ` Johannes Thumshirn
2026-10-21  3:39 ` [PATCH v2 5/9] btrfs: add a new 'stripe_len' parameter to btrfs_stripe_nr_to_offset() Qu Wenruo
2026-10-21  6:00   ` Johannes Thumshirn
2026-10-21  3:39 ` [PATCH v2 6/9] btrfs: convert volumes.c and block-group.c to use map->stripe_len Qu Wenruo
2026-10-21  6:01   ` Johannes Thumshirn
2026-10-21  3:39 ` [PATCH v2 7/9] btrfs: convert raid56.c to use rbio->bioc->stripe_len Qu Wenruo
2026-10-21  6:46   ` Johannes Thumshirn
2026-10-21  3:39 ` [PATCH v2 8/9] btrfs: convert zoned.c to use map->stripe_len Qu Wenruo
2026-10-21  6:47   ` Johannes Thumshirn
2026-10-21  3:39 ` [PATCH v2 9/9] btrfs: introduce extra limits on btrfs_chunk::stripe_len Qu Wenruo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox