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;
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) {
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++) {