56bb701e3b5366fcb42303cfbadaf1e18d4de718
[linux-2.6-microblaze.git] / fs / btrfs / zoned.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/slab.h>
4 #include <linux/blkdev.h>
5 #include "ctree.h"
6 #include "volumes.h"
7 #include "zoned.h"
8 #include "rcu-string.h"
9
10 /* Maximum number of zones to report per blkdev_report_zones() call */
11 #define BTRFS_REPORT_NR_ZONES   4096
12
13 static int copy_zone_info_cb(struct blk_zone *zone, unsigned int idx, void *data)
14 {
15         struct blk_zone *zones = data;
16
17         memcpy(&zones[idx], zone, sizeof(*zone));
18
19         return 0;
20 }
21
22 static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
23                                struct blk_zone *zones, unsigned int *nr_zones)
24 {
25         int ret;
26
27         if (!*nr_zones)
28                 return 0;
29
30         ret = blkdev_report_zones(device->bdev, pos >> SECTOR_SHIFT, *nr_zones,
31                                   copy_zone_info_cb, zones);
32         if (ret < 0) {
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),
36                                  device->devid);
37                 return ret;
38         }
39         *nr_zones = ret;
40         if (!ret)
41                 return -EIO;
42
43         return 0;
44 }
45
46 int btrfs_get_dev_zone_info(struct btrfs_device *device)
47 {
48         struct btrfs_zoned_device_info *zone_info = NULL;
49         struct block_device *bdev = device->bdev;
50         sector_t nr_sectors;
51         sector_t sector = 0;
52         struct blk_zone *zones = NULL;
53         unsigned int i, nreported = 0, nr_zones;
54         unsigned int zone_sectors;
55         int ret;
56
57         if (!bdev_is_zoned(bdev))
58                 return 0;
59
60         if (device->zone_info)
61                 return 0;
62
63         zone_info = kzalloc(sizeof(*zone_info), GFP_KERNEL);
64         if (!zone_info)
65                 return -ENOMEM;
66
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++;
76
77         zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
78         if (!zone_info->seq_zones) {
79                 ret = -ENOMEM;
80                 goto out;
81         }
82
83         zone_info->empty_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
84         if (!zone_info->empty_zones) {
85                 ret = -ENOMEM;
86                 goto out;
87         }
88
89         zones = kcalloc(BTRFS_REPORT_NR_ZONES, sizeof(struct blk_zone), GFP_KERNEL);
90         if (!zones) {
91                 ret = -ENOMEM;
92                 goto out;
93         }
94
95         /* Get zones type */
96         while (sector < nr_sectors) {
97                 nr_zones = BTRFS_REPORT_NR_ZONES;
98                 ret = btrfs_get_dev_zones(device, sector << SECTOR_SHIFT, zones,
99                                           &nr_zones);
100                 if (ret)
101                         goto out;
102
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);
108                         nreported++;
109                 }
110                 sector = zones[nr_zones - 1].start + zones[nr_zones - 1].len;
111         }
112
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);
118                 ret = -EIO;
119                 goto out;
120         }
121
122         kfree(zones);
123
124         device->zone_info = zone_info;
125
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);
132
133         return 0;
134
135 out:
136         kfree(zones);
137         bitmap_free(zone_info->empty_zones);
138         bitmap_free(zone_info->seq_zones);
139         kfree(zone_info);
140
141         return ret;
142 }
143
144 void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
145 {
146         struct btrfs_zoned_device_info *zone_info = device->zone_info;
147
148         if (!zone_info)
149                 return;
150
151         bitmap_free(zone_info->seq_zones);
152         bitmap_free(zone_info->empty_zones);
153         kfree(zone_info);
154         device->zone_info = NULL;
155 }
156
157 int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
158                        struct blk_zone *zone)
159 {
160         unsigned int nr_zones = 1;
161         int ret;
162
163         ret = btrfs_get_dev_zones(device, pos, zone, &nr_zones);
164         if (ret != 0 || !nr_zones)
165                 return ret ? ret : -EIO;
166
167         return 0;
168 }
169
170 int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info)
171 {
172         struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
173         struct btrfs_device *device;
174         u64 zoned_devices = 0;
175         u64 nr_devices = 0;
176         u64 zone_size = 0;
177         const bool incompat_zoned = btrfs_is_zoned(fs_info);
178         int ret = 0;
179
180         /* Count zoned devices */
181         list_for_each_entry(device, &fs_devices->devices, dev_list) {
182                 enum blk_zoned_model model;
183
184                 if (!device->bdev)
185                         continue;
186
187                 model = bdev_zoned_model(device->bdev);
188                 if (model == BLK_ZONED_HM ||
189                     (model == BLK_ZONED_HA && incompat_zoned)) {
190                         zoned_devices++;
191                         if (!zone_size) {
192                                 zone_size = device->zone_info->zone_size;
193                         } else if (device->zone_info->zone_size != zone_size) {
194                                 btrfs_err(fs_info,
195                 "zoned: unequal block device zone sizes: have %llu found %llu",
196                                           device->zone_info->zone_size,
197                                           zone_size);
198                                 ret = -EINVAL;
199                                 goto out;
200                         }
201                 }
202                 nr_devices++;
203         }
204
205         if (!zoned_devices && !incompat_zoned)
206                 goto out;
207
208         if (!zoned_devices && incompat_zoned) {
209                 /* No zoned block device found on ZONED filesystem */
210                 btrfs_err(fs_info,
211                           "zoned: no zoned devices found on a zoned filesystem");
212                 ret = -EINVAL;
213                 goto out;
214         }
215
216         if (zoned_devices && !incompat_zoned) {
217                 btrfs_err(fs_info,
218                           "zoned: mode not enabled but zoned device found");
219                 ret = -EINVAL;
220                 goto out;
221         }
222
223         if (zoned_devices != nr_devices) {
224                 btrfs_err(fs_info,
225                           "zoned: cannot mix zoned and regular devices");
226                 ret = -EINVAL;
227                 goto out;
228         }
229
230         /*
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.
234          */
235         if (!IS_ALIGNED(zone_size, BTRFS_STRIPE_LEN)) {
236                 btrfs_err(fs_info,
237                           "zoned: zone size %llu not aligned to stripe %u",
238                           zone_size, BTRFS_STRIPE_LEN);
239                 ret = -EINVAL;
240                 goto out;
241         }
242
243         fs_info->zone_size = zone_size;
244
245         btrfs_info(fs_info, "zoned mode enabled with zone size %llu", zone_size);
246 out:
247         return ret;
248 }