1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/slab.h>
4 #include <linux/blkdev.h>
8 #include "rcu-string.h"
10 /* Maximum number of zones to report per blkdev_report_zones() call */
11 #define BTRFS_REPORT_NR_ZONES 4096
13 static int copy_zone_info_cb(struct blk_zone *zone, unsigned int idx, void *data)
15 struct blk_zone *zones = data;
17 memcpy(&zones[idx], zone, sizeof(*zone));
22 static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
23 struct blk_zone *zones, unsigned int *nr_zones)
30 ret = blkdev_report_zones(device->bdev, pos >> SECTOR_SHIFT, *nr_zones,
31 copy_zone_info_cb, zones);
33 btrfs_err_in_rcu(device->fs_info,
34 "zoned: failed to read zone %llu on %s (devid %llu)",
35 pos, rcu_str_deref(device->name),
46 int btrfs_get_dev_zone_info(struct btrfs_device *device)
48 struct btrfs_zoned_device_info *zone_info = NULL;
49 struct block_device *bdev = device->bdev;
52 struct blk_zone *zones = NULL;
53 unsigned int i, nreported = 0, nr_zones;
54 unsigned int zone_sectors;
57 if (!bdev_is_zoned(bdev))
60 if (device->zone_info)
63 zone_info = kzalloc(sizeof(*zone_info), GFP_KERNEL);
67 nr_sectors = bdev->bd_part->nr_sects;
68 zone_sectors = bdev_zone_sectors(bdev);
69 /* Check if it's power of 2 (see is_power_of_2) */
70 ASSERT(zone_sectors != 0 && (zone_sectors & (zone_sectors - 1)) == 0);
71 zone_info->zone_size = zone_sectors << SECTOR_SHIFT;
72 zone_info->zone_size_shift = ilog2(zone_info->zone_size);
73 zone_info->nr_zones = nr_sectors >> ilog2(zone_sectors);
74 if (!IS_ALIGNED(nr_sectors, zone_sectors))
75 zone_info->nr_zones++;
77 zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
78 if (!zone_info->seq_zones) {
83 zone_info->empty_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
84 if (!zone_info->empty_zones) {
89 zones = kcalloc(BTRFS_REPORT_NR_ZONES, sizeof(struct blk_zone), GFP_KERNEL);
96 while (sector < nr_sectors) {
97 nr_zones = BTRFS_REPORT_NR_ZONES;
98 ret = btrfs_get_dev_zones(device, sector << SECTOR_SHIFT, zones,
103 for (i = 0; i < nr_zones; i++) {
104 if (zones[i].type == BLK_ZONE_TYPE_SEQWRITE_REQ)
105 __set_bit(nreported, zone_info->seq_zones);
106 if (zones[i].cond == BLK_ZONE_COND_EMPTY)
107 __set_bit(nreported, zone_info->empty_zones);
110 sector = zones[nr_zones - 1].start + zones[nr_zones - 1].len;
113 if (nreported != zone_info->nr_zones) {
114 btrfs_err_in_rcu(device->fs_info,
115 "inconsistent number of zones on %s (%u/%u)",
116 rcu_str_deref(device->name), nreported,
117 zone_info->nr_zones);
124 device->zone_info = zone_info;
126 /* device->fs_info is not safe to use for printing messages */
127 btrfs_info_in_rcu(NULL,
128 "host-%s zoned block device %s, %u zones of %llu bytes",
129 bdev_zoned_model(bdev) == BLK_ZONED_HM ? "managed" : "aware",
130 rcu_str_deref(device->name), zone_info->nr_zones,
131 zone_info->zone_size);
137 bitmap_free(zone_info->empty_zones);
138 bitmap_free(zone_info->seq_zones);
144 void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
146 struct btrfs_zoned_device_info *zone_info = device->zone_info;
151 bitmap_free(zone_info->seq_zones);
152 bitmap_free(zone_info->empty_zones);
154 device->zone_info = NULL;
157 int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
158 struct blk_zone *zone)
160 unsigned int nr_zones = 1;
163 ret = btrfs_get_dev_zones(device, pos, zone, &nr_zones);
164 if (ret != 0 || !nr_zones)
165 return ret ? ret : -EIO;
170 int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info)
172 struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
173 struct btrfs_device *device;
174 u64 zoned_devices = 0;
177 const bool incompat_zoned = btrfs_is_zoned(fs_info);
180 /* Count zoned devices */
181 list_for_each_entry(device, &fs_devices->devices, dev_list) {
182 enum blk_zoned_model model;
187 model = bdev_zoned_model(device->bdev);
188 if (model == BLK_ZONED_HM ||
189 (model == BLK_ZONED_HA && incompat_zoned)) {
192 zone_size = device->zone_info->zone_size;
193 } else if (device->zone_info->zone_size != zone_size) {
195 "zoned: unequal block device zone sizes: have %llu found %llu",
196 device->zone_info->zone_size,
205 if (!zoned_devices && !incompat_zoned)
208 if (!zoned_devices && incompat_zoned) {
209 /* No zoned block device found on ZONED filesystem */
211 "zoned: no zoned devices found on a zoned filesystem");
216 if (zoned_devices && !incompat_zoned) {
218 "zoned: mode not enabled but zoned device found");
223 if (zoned_devices != nr_devices) {
225 "zoned: cannot mix zoned and regular devices");
231 * stripe_size is always aligned to BTRFS_STRIPE_LEN in
232 * __btrfs_alloc_chunk(). Since we want stripe_len == zone_size,
233 * check the alignment here.
235 if (!IS_ALIGNED(zone_size, BTRFS_STRIPE_LEN)) {
237 "zoned: zone size %llu not aligned to stripe %u",
238 zone_size, BTRFS_STRIPE_LEN);
243 fs_info->zone_size = zone_size;
245 btrfs_info(fs_info, "zoned mode enabled with zone size %llu", zone_size);