]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: allow remapped chunks to have zero stripes
authorMark Harmstone <mark@harmstone.com>
Wed, 7 Jan 2026 14:09:03 +0000 (14:09 +0000)
committerDavid Sterba <dsterba@suse.com>
Tue, 3 Feb 2026 06:54:34 +0000 (07:54 +0100)
When a chunk has been fully remapped, we are going to set its
num_stripes to 0, as it will no longer represent a physical location on
disk.

Change tree-checker to allow for this, and fix read_one_chunk() to avoid
a divide by zero.

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/tree-checker.c
fs/btrfs/volumes.c

index a6c158cd8fcd2a098c0a499585e0e26059f368a0..ead2e1e2a0bb2a10df2e652ea67edefb8200c8a5 100644 (file)
@@ -816,6 +816,32 @@ static void chunk_err(const struct btrfs_fs_info *fs_info,
        va_end(args);
 }
 
+static bool valid_stripe_count(u64 profile, u16 num_stripes, u16 sub_stripes)
+{
+       switch (profile) {
+       case BTRFS_BLOCK_GROUP_RAID0:
+               return true;
+       case BTRFS_BLOCK_GROUP_RAID10:
+               return sub_stripes == btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes;
+       case BTRFS_BLOCK_GROUP_RAID1:
+               return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1].devs_min;
+       case BTRFS_BLOCK_GROUP_RAID1C3:
+               return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1C3].devs_min;
+       case BTRFS_BLOCK_GROUP_RAID1C4:
+               return num_stripes == btrfs_raid_array[BTRFS_RAID_RAID1C4].devs_min;
+       case BTRFS_BLOCK_GROUP_RAID5:
+               return num_stripes >= btrfs_raid_array[BTRFS_RAID_RAID5].devs_min;
+       case BTRFS_BLOCK_GROUP_RAID6:
+               return num_stripes >= btrfs_raid_array[BTRFS_RAID_RAID6].devs_min;
+       case BTRFS_BLOCK_GROUP_DUP:
+               return num_stripes == btrfs_raid_array[BTRFS_RAID_DUP].dev_stripes;
+       case 0: /* SINGLE */
+               return num_stripes == btrfs_raid_array[BTRFS_RAID_SINGLE].dev_stripes;
+       default:
+               BUG();
+       }
+}
+
 /*
  * The common chunk check which could also work on super block sys chunk array.
  *
@@ -839,6 +865,7 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info,
        u64 features;
        u32 chunk_sector_size;
        bool mixed = false;
+       bool remapped;
        int raid_index;
        int nparity;
        int ncopies;
@@ -861,13 +888,14 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info,
        raid_index = btrfs_bg_flags_to_raid_index(type);
        ncopies = btrfs_raid_array[raid_index].ncopies;
        nparity = btrfs_raid_array[raid_index].nparity;
+       remapped = (type & BTRFS_BLOCK_GROUP_REMAPPED);
 
-       if (unlikely(!num_stripes)) {
+       if (unlikely(!remapped && !num_stripes)) {
                chunk_err(fs_info, leaf, chunk, logical,
                          "invalid chunk num_stripes, have %u", num_stripes);
                return -EUCLEAN;
        }
-       if (unlikely(num_stripes < ncopies)) {
+       if (unlikely(num_stripes != 0 && num_stripes < ncopies)) {
                chunk_err(fs_info, leaf, chunk, logical,
                          "invalid chunk num_stripes < ncopies, have %u < %d",
                          num_stripes, ncopies);
@@ -965,22 +993,9 @@ int btrfs_check_chunk_valid(const struct btrfs_fs_info *fs_info,
                }
        }
 
-       if (unlikely((type & BTRFS_BLOCK_GROUP_RAID10 &&
-                     sub_stripes != btrfs_raid_array[BTRFS_RAID_RAID10].sub_stripes) ||
-                    (type & BTRFS_BLOCK_GROUP_RAID1 &&
-                     num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1].devs_min) ||
-                    (type & BTRFS_BLOCK_GROUP_RAID1C3 &&
-                     num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C3].devs_min) ||
-                    (type & BTRFS_BLOCK_GROUP_RAID1C4 &&
-                     num_stripes != btrfs_raid_array[BTRFS_RAID_RAID1C4].devs_min) ||
-                    (type & BTRFS_BLOCK_GROUP_RAID5 &&
-                     num_stripes < btrfs_raid_array[BTRFS_RAID_RAID5].devs_min) ||
-                    (type & BTRFS_BLOCK_GROUP_RAID6 &&
-                     num_stripes < btrfs_raid_array[BTRFS_RAID_RAID6].devs_min) ||
-                    (type & BTRFS_BLOCK_GROUP_DUP &&
-                     num_stripes != btrfs_raid_array[BTRFS_RAID_DUP].dev_stripes) ||
-                    ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
-                     num_stripes != btrfs_raid_array[BTRFS_RAID_SINGLE].dev_stripes))) {
+       if (!remapped &&
+           !valid_stripe_count(type & BTRFS_BLOCK_GROUP_PROFILE_MASK,
+                               num_stripes, sub_stripes)) {
                chunk_err(fs_info, leaf, chunk, logical,
                        "invalid num_stripes:sub_stripes %u:%u for profile %llu",
                        num_stripes, sub_stripes,
@@ -1004,11 +1019,11 @@ static int check_leaf_chunk_item(struct extent_buffer *leaf,
        struct btrfs_fs_info *fs_info = leaf->fs_info;
        int num_stripes;
 
-       if (unlikely(btrfs_item_size(leaf, slot) < sizeof(struct btrfs_chunk))) {
+       if (unlikely(btrfs_item_size(leaf, slot) < offsetof(struct btrfs_chunk, stripe))) {
                chunk_err(fs_info, leaf, chunk, key->offset,
                        "invalid chunk item size: have %u expect [%zu, %u)",
                        btrfs_item_size(leaf, slot),
-                       sizeof(struct btrfs_chunk),
+                       offsetof(struct btrfs_chunk, stripe),
                        BTRFS_LEAF_DATA_SIZE(fs_info));
                return -EUCLEAN;
        }
index eda6505f3ee59e2004f772b8ad21d164e13087bd..2c9b55f66cc39c8215508a2a8839ffaeb58c7b08 100644 (file)
@@ -7047,7 +7047,12 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
         */
        map->sub_stripes = btrfs_raid_array[index].sub_stripes;
        map->verified_stripes = 0;
-       map->stripe_size = btrfs_calc_stripe_length(map);
+
+       if (num_stripes > 0)
+               map->stripe_size = btrfs_calc_stripe_length(map);
+       else
+               map->stripe_size = 0;
+
        for (i = 0; i < num_stripes; i++) {
                map->stripes[i].physical =
                        btrfs_stripe_offset_nr(leaf, chunk, i);