]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: zoned: always set max_active_zones for zoned devices
authorJohannes Thumshirn <johannes.thumshirn@wdc.com>
Fri, 22 May 2026 09:22:12 +0000 (11:22 +0200)
committerJohannes Thumshirn <johannes.thumshirn@wdc.com>
Tue, 9 Jun 2026 16:22:44 +0000 (18:22 +0200)
When a block device does not report a maximum number of open or active
zones,  currently assign BTRFS_DEFAULT_MAX_ACTIVE_ZONES (128) to
the internal limit, if the device has more than
BTRFS_DEFAULT_MAX_ACTIVE_ZONES zones.

But if the device has less than BTRFS_DEFAULT_MAX_ACTIVE_ZONES the
internal max_active_zones limit will stay at 0, even if the device has
zone resource limits. Furthermore, if the device has a total number of
zones that is less than BTRFS_DEFAULT_MAX_ACTIVE_ZONE, max_active_zones
should be set to at most the number of zones.

Also move the max_active_zone calculation and setting into a dedicated
helper, to shrink btrfs_get_dev_zone_info().

Fixes: 04147d8394e8 ("btrfs: zoned: limit active zones to max_open_zones")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/zoned.c

index e3e141258a22b50dba58a8ac9c88ffdca6d593b7..0d850e71af74189cd8d51ebef53e406e55388ca3 100644 (file)
@@ -356,12 +356,33 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
        return ret;
 }
 
+static int btrfs_get_max_active_zones(struct btrfs_device *device,
+                                     struct btrfs_zoned_device_info *zone_info)
+{
+       struct block_device *bdev = device->bdev;
+       int max_active_zones;
+
+       if (unlikely(zone_info->nr_zones < BTRFS_MIN_ACTIVE_ZONES)) {
+               btrfs_err(device->fs_info, "zoned: not enough zones to mount filesystem: %u < %d",
+                         zone_info->nr_zones, BTRFS_MIN_ACTIVE_ZONES);
+               return -EINVAL;
+       }
+
+       max_active_zones = min_not_zero(bdev_max_active_zones(bdev),
+                                       bdev_max_open_zones(bdev));
+       if (max_active_zones == 0)
+               max_active_zones = min(zone_info->nr_zones / 4,
+                                      BTRFS_DEFAULT_MAX_ACTIVE_ZONES);
+
+       zone_info->max_active_zones = max(max_active_zones, BTRFS_MIN_ACTIVE_ZONES);
+       return 0;
+}
+
 int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
 {
        struct btrfs_fs_info *fs_info = device->fs_info;
        struct btrfs_zoned_device_info *zone_info = NULL;
        struct block_device *bdev = device->bdev;
-       unsigned int max_active_zones;
        unsigned int nactive;
        sector_t nr_sectors;
        sector_t sector = 0;
@@ -426,19 +447,9 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
        if (!IS_ALIGNED(nr_sectors, zone_sectors))
                zone_info->nr_zones++;
 
-       max_active_zones = min_not_zero(bdev_max_active_zones(bdev),
-                                       bdev_max_open_zones(bdev));
-       if (!max_active_zones && zone_info->nr_zones > BTRFS_DEFAULT_MAX_ACTIVE_ZONES)
-               max_active_zones = BTRFS_DEFAULT_MAX_ACTIVE_ZONES;
-       if (max_active_zones && max_active_zones < BTRFS_MIN_ACTIVE_ZONES) {
-               btrfs_err(fs_info,
-"zoned: %s: max active zones %u is too small, need at least %u active zones",
-                                rcu_dereference(device->name), max_active_zones,
-                                BTRFS_MIN_ACTIVE_ZONES);
-               ret = -EINVAL;
+       ret = btrfs_get_max_active_zones(device, zone_info);
+       if (ret)
                goto out;
-       }
-       zone_info->max_active_zones = max_active_zones;
 
        zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
        if (!zone_info->seq_zones) {
@@ -519,26 +530,29 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
                goto out;
        }
 
-       if (max_active_zones) {
-               if (unlikely(nactive > max_active_zones)) {
-                       if (bdev_max_active_zones(bdev) == 0) {
-                               max_active_zones = 0;
-                               zone_info->max_active_zones = 0;
-                               goto validate;
-                       }
+       if (unlikely(nactive > zone_info->max_active_zones)) {
+               if (bdev_max_active_zones(bdev) > 0) {
                        btrfs_err(device->fs_info,
-                       "zoned: %u active zones on %s exceeds max_active_zones %u",
-                                        nactive, rcu_dereference(device->name),
-                                        max_active_zones);
+                                       "zoned: %u active zones on %s exceeds max_active_zones %u",
+                                       nactive, rcu_dereference(device->name),
+                                       zone_info->max_active_zones);
                        ret = -EIO;
                        goto out;
                }
+
+               /*
+                * This is for backward compatibility with old filesystems that
+                * have a lot of active zones because the device doesn't report
+                * a maximum number of zones and we previously didn't care for
+                * the limit.
+                */
+               zone_info->max_active_zones = 0;
+       } else {
                atomic_set(&zone_info->active_zones_left,
-                          max_active_zones - nactive);
+                               zone_info->max_active_zones - nactive);
                set_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags);
        }
 
-validate:
        /* Validate superblock log */
        nr_zones = BTRFS_NR_SB_LOG_ZONES;
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {