]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: zoned: fix alloc_offset calculation for partly conventional block groups
authorJohannes Thumshirn <johannes.thumshirn@wdc.com>
Fri, 6 Jun 2025 07:17:41 +0000 (09:17 +0200)
committerDavid Sterba <dsterba@suse.com>
Thu, 19 Jun 2025 13:21:15 +0000 (15:21 +0200)
When one of two zones composing a DUP block group is a conventional zone,
we have the zone_info[i]->alloc_offset = WP_CONVENTIONAL. That will, of
course, not match the write pointer of the other zone, and fails that
block group.

This commit solves that issue by properly recovering the emulated write
pointer from the last allocated extent. The offset for the SINGLE, DUP,
and RAID1 are straight-forward: it is same as the end of last allocated
extent. The RAID0 and RAID10 are a bit tricky that we need to do the math
of striping.

This is the kernel equivalent of Naohiro's user-space commit:
"btrfs-progs: zoned: fix alloc_offset calculation for partly
conventional block groups".

Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/zoned.c

index b5b0156d5b95a8c8e166e85bf2d51da437d9ce2a..9430b34d3cbb8e53ca19b6e917952852d22acf85 100644 (file)
@@ -1403,7 +1403,8 @@ static int btrfs_load_block_group_single(struct btrfs_block_group *bg,
 static int btrfs_load_block_group_dup(struct btrfs_block_group *bg,
                                      struct btrfs_chunk_map *map,
                                      struct zone_info *zone_info,
-                                     unsigned long *active)
+                                     unsigned long *active,
+                                     u64 last_alloc)
 {
        struct btrfs_fs_info *fs_info = bg->fs_info;
 
@@ -1426,6 +1427,13 @@ static int btrfs_load_block_group_dup(struct btrfs_block_group *bg,
                          zone_info[1].physical);
                return -EIO;
        }
+
+       if (zone_info[0].alloc_offset == WP_CONVENTIONAL)
+               zone_info[0].alloc_offset = last_alloc;
+
+       if (zone_info[1].alloc_offset == WP_CONVENTIONAL)
+               zone_info[1].alloc_offset = last_alloc;
+
        if (zone_info[0].alloc_offset != zone_info[1].alloc_offset) {
                btrfs_err(bg->fs_info,
                          "zoned: write pointer offset mismatch of zones in DUP profile");
@@ -1446,7 +1454,8 @@ static int btrfs_load_block_group_dup(struct btrfs_block_group *bg,
 static int btrfs_load_block_group_raid1(struct btrfs_block_group *bg,
                                        struct btrfs_chunk_map *map,
                                        struct zone_info *zone_info,
-                                       unsigned long *active)
+                                       unsigned long *active,
+                                       u64 last_alloc)
 {
        struct btrfs_fs_info *fs_info = bg->fs_info;
        int i;
@@ -1461,10 +1470,12 @@ static int btrfs_load_block_group_raid1(struct btrfs_block_group *bg,
        bg->zone_capacity = min_not_zero(zone_info[0].capacity, zone_info[1].capacity);
 
        for (i = 0; i < map->num_stripes; i++) {
-               if (zone_info[i].alloc_offset == WP_MISSING_DEV ||
-                   zone_info[i].alloc_offset == WP_CONVENTIONAL)
+               if (zone_info[i].alloc_offset == WP_MISSING_DEV)
                        continue;
 
+               if (zone_info[i].alloc_offset == WP_CONVENTIONAL)
+                       zone_info[i].alloc_offset = last_alloc;
+
                if ((zone_info[0].alloc_offset != zone_info[i].alloc_offset) &&
                    !btrfs_test_opt(fs_info, DEGRADED)) {
                        btrfs_err(fs_info,
@@ -1494,7 +1505,8 @@ static int btrfs_load_block_group_raid1(struct btrfs_block_group *bg,
 static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
                                        struct btrfs_chunk_map *map,
                                        struct zone_info *zone_info,
-                                       unsigned long *active)
+                                       unsigned long *active,
+                                       u64 last_alloc)
 {
        struct btrfs_fs_info *fs_info = bg->fs_info;
 
@@ -1505,10 +1517,29 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
        }
 
        for (int i = 0; i < map->num_stripes; i++) {
-               if (zone_info[i].alloc_offset == WP_MISSING_DEV ||
-                   zone_info[i].alloc_offset == WP_CONVENTIONAL)
+               if (zone_info[i].alloc_offset == WP_MISSING_DEV)
                        continue;
 
+               if (zone_info[i].alloc_offset == WP_CONVENTIONAL) {
+                       u64 stripe_nr, full_stripe_nr;
+                       u64 stripe_offset;
+                       int stripe_index;
+
+                       stripe_nr = div64_u64(last_alloc, map->stripe_size);
+                       stripe_offset = stripe_nr * map->stripe_size;
+                       full_stripe_nr = div_u64(stripe_nr, map->num_stripes);
+                       div_u64_rem(stripe_nr, map->num_stripes, &stripe_index);
+
+                       zone_info[i].alloc_offset =
+                               full_stripe_nr * map->stripe_size;
+
+                       if (stripe_index > i)
+                               zone_info[i].alloc_offset += map->stripe_size;
+                       else if (stripe_index == i)
+                               zone_info[i].alloc_offset +=
+                                       (last_alloc - stripe_offset);
+               }
+
                if (test_bit(0, active) != test_bit(i, active)) {
                        if (!btrfs_zone_activate(bg))
                                return -EIO;
@@ -1526,7 +1557,8 @@ static int btrfs_load_block_group_raid0(struct btrfs_block_group *bg,
 static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
                                         struct btrfs_chunk_map *map,
                                         struct zone_info *zone_info,
-                                        unsigned long *active)
+                                        unsigned long *active,
+                                        u64 last_alloc)
 {
        struct btrfs_fs_info *fs_info = bg->fs_info;
 
@@ -1537,8 +1569,7 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
        }
 
        for (int i = 0; i < map->num_stripes; i++) {
-               if (zone_info[i].alloc_offset == WP_MISSING_DEV ||
-                   zone_info[i].alloc_offset == WP_CONVENTIONAL)
+               if (zone_info[i].alloc_offset == WP_MISSING_DEV)
                        continue;
 
                if (test_bit(0, active) != test_bit(i, active)) {
@@ -1549,6 +1580,29 @@ static int btrfs_load_block_group_raid10(struct btrfs_block_group *bg,
                                set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &bg->runtime_flags);
                }
 
+               if (zone_info[i].alloc_offset == WP_CONVENTIONAL) {
+                       u64 stripe_nr, full_stripe_nr;
+                       u64 stripe_offset;
+                       int stripe_index;
+
+                       stripe_nr = div64_u64(last_alloc, map->stripe_size);
+                       stripe_offset = stripe_nr * map->stripe_size;
+                       full_stripe_nr = div_u64(stripe_nr,
+                                        map->num_stripes / map->sub_stripes);
+                       div_u64_rem(stripe_nr,
+                                   (map->num_stripes / map->sub_stripes),
+                                   &stripe_index);
+
+                       zone_info[i].alloc_offset =
+                               full_stripe_nr * map->stripe_size;
+
+                       if (stripe_index > (i / map->sub_stripes))
+                               zone_info[i].alloc_offset += map->stripe_size;
+                       else if (stripe_index == (i / map->sub_stripes))
+                               zone_info[i].alloc_offset +=
+                                       (last_alloc - stripe_offset);
+               }
+
                if ((i % map->sub_stripes) == 0) {
                        bg->zone_capacity += zone_info[i].capacity;
                        bg->alloc_offset += zone_info[i].alloc_offset;
@@ -1637,18 +1691,22 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new)
                ret = btrfs_load_block_group_single(cache, &zone_info[0], active);
                break;
        case BTRFS_BLOCK_GROUP_DUP:
-               ret = btrfs_load_block_group_dup(cache, map, zone_info, active);
+               ret = btrfs_load_block_group_dup(cache, map, zone_info, active,
+                                                last_alloc);
                break;
        case BTRFS_BLOCK_GROUP_RAID1:
        case BTRFS_BLOCK_GROUP_RAID1C3:
        case BTRFS_BLOCK_GROUP_RAID1C4:
-               ret = btrfs_load_block_group_raid1(cache, map, zone_info, active);
+               ret = btrfs_load_block_group_raid1(cache, map, zone_info,
+                                                  active, last_alloc);
                break;
        case BTRFS_BLOCK_GROUP_RAID0:
-               ret = btrfs_load_block_group_raid0(cache, map, zone_info, active);
+               ret = btrfs_load_block_group_raid0(cache, map, zone_info,
+                                                  active, last_alloc);
                break;
        case BTRFS_BLOCK_GROUP_RAID10:
-               ret = btrfs_load_block_group_raid10(cache, map, zone_info, active);
+               ret = btrfs_load_block_group_raid10(cache, map, zone_info,
+                                                   active, last_alloc);
                break;
        case BTRFS_BLOCK_GROUP_RAID5:
        case BTRFS_BLOCK_GROUP_RAID6: