]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - block/blk-zoned.c
Merge remote-tracking branches 'asoc/topic/rockchip', 'asoc/topic/rt5514', 'asoc...
[karo-tx-linux.git] / block / blk-zoned.c
1 /*
2  * Zoned block device handling
3  *
4  * Copyright (c) 2015, Hannes Reinecke
5  * Copyright (c) 2015, SUSE Linux GmbH
6  *
7  * Copyright (c) 2016, Damien Le Moal
8  * Copyright (c) 2016, Western Digital
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/rbtree.h>
14 #include <linux/blkdev.h>
15
16 static inline sector_t blk_zone_start(struct request_queue *q,
17                                       sector_t sector)
18 {
19         sector_t zone_mask = blk_queue_zone_sectors(q) - 1;
20
21         return sector & ~zone_mask;
22 }
23
24 /*
25  * Check that a zone report belongs to the partition.
26  * If yes, fix its start sector and write pointer, copy it in the
27  * zone information array and return true. Return false otherwise.
28  */
29 static bool blkdev_report_zone(struct block_device *bdev,
30                                struct blk_zone *rep,
31                                struct blk_zone *zone)
32 {
33         sector_t offset = get_start_sect(bdev);
34
35         if (rep->start < offset)
36                 return false;
37
38         rep->start -= offset;
39         if (rep->start + rep->len > bdev->bd_part->nr_sects)
40                 return false;
41
42         if (rep->type == BLK_ZONE_TYPE_CONVENTIONAL)
43                 rep->wp = rep->start + rep->len;
44         else
45                 rep->wp -= offset;
46         memcpy(zone, rep, sizeof(struct blk_zone));
47
48         return true;
49 }
50
51 /**
52  * blkdev_report_zones - Get zones information
53  * @bdev:       Target block device
54  * @sector:     Sector from which to report zones
55  * @zones:      Array of zone structures where to return the zones information
56  * @nr_zones:   Number of zone structures in the zone array
57  * @gfp_mask:   Memory allocation flags (for bio_alloc)
58  *
59  * Description:
60  *    Get zone information starting from the zone containing @sector.
61  *    The number of zone information reported may be less than the number
62  *    requested by @nr_zones. The number of zones actually reported is
63  *    returned in @nr_zones.
64  */
65 int blkdev_report_zones(struct block_device *bdev,
66                         sector_t sector,
67                         struct blk_zone *zones,
68                         unsigned int *nr_zones,
69                         gfp_t gfp_mask)
70 {
71         struct request_queue *q = bdev_get_queue(bdev);
72         struct blk_zone_report_hdr *hdr;
73         unsigned int nrz = *nr_zones;
74         struct page *page;
75         unsigned int nr_rep;
76         size_t rep_bytes;
77         unsigned int nr_pages;
78         struct bio *bio;
79         struct bio_vec *bv;
80         unsigned int i, n, nz;
81         unsigned int ofst;
82         void *addr;
83         int ret;
84
85         if (!q)
86                 return -ENXIO;
87
88         if (!blk_queue_is_zoned(q))
89                 return -EOPNOTSUPP;
90
91         if (!nrz)
92                 return 0;
93
94         if (sector > bdev->bd_part->nr_sects) {
95                 *nr_zones = 0;
96                 return 0;
97         }
98
99         /*
100          * The zone report has a header. So make room for it in the
101          * payload. Also make sure that the report fits in a single BIO
102          * that will not be split down the stack.
103          */
104         rep_bytes = sizeof(struct blk_zone_report_hdr) +
105                 sizeof(struct blk_zone) * nrz;
106         rep_bytes = (rep_bytes + PAGE_SIZE - 1) & PAGE_MASK;
107         if (rep_bytes > (queue_max_sectors(q) << 9))
108                 rep_bytes = queue_max_sectors(q) << 9;
109
110         nr_pages = min_t(unsigned int, BIO_MAX_PAGES,
111                          rep_bytes >> PAGE_SHIFT);
112         nr_pages = min_t(unsigned int, nr_pages,
113                          queue_max_segments(q));
114
115         bio = bio_alloc(gfp_mask, nr_pages);
116         if (!bio)
117                 return -ENOMEM;
118
119         bio->bi_bdev = bdev;
120         bio->bi_iter.bi_sector = blk_zone_start(q, sector);
121         bio_set_op_attrs(bio, REQ_OP_ZONE_REPORT, 0);
122
123         for (i = 0; i < nr_pages; i++) {
124                 page = alloc_page(gfp_mask);
125                 if (!page) {
126                         ret = -ENOMEM;
127                         goto out;
128                 }
129                 if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
130                         __free_page(page);
131                         break;
132                 }
133         }
134
135         if (i == 0)
136                 ret = -ENOMEM;
137         else
138                 ret = submit_bio_wait(bio);
139         if (ret)
140                 goto out;
141
142         /*
143          * Process the report result: skip the header and go through the
144          * reported zones to fixup and fixup the zone information for
145          * partitions. At the same time, return the zone information into
146          * the zone array.
147          */
148         n = 0;
149         nz = 0;
150         nr_rep = 0;
151         bio_for_each_segment_all(bv, bio, i) {
152
153                 if (!bv->bv_page)
154                         break;
155
156                 addr = kmap_atomic(bv->bv_page);
157
158                 /* Get header in the first page */
159                 ofst = 0;
160                 if (!nr_rep) {
161                         hdr = (struct blk_zone_report_hdr *) addr;
162                         nr_rep = hdr->nr_zones;
163                         ofst = sizeof(struct blk_zone_report_hdr);
164                 }
165
166                 /* Fixup and report zones */
167                 while (ofst < bv->bv_len &&
168                        n < nr_rep && nz < nrz) {
169                         if (blkdev_report_zone(bdev, addr + ofst, &zones[nz]))
170                                 nz++;
171                         ofst += sizeof(struct blk_zone);
172                         n++;
173                 }
174
175                 kunmap_atomic(addr);
176
177                 if (n >= nr_rep || nz >= nrz)
178                         break;
179
180         }
181
182         *nr_zones = nz;
183 out:
184         bio_for_each_segment_all(bv, bio, i)
185                 __free_page(bv->bv_page);
186         bio_put(bio);
187
188         return ret;
189 }
190 EXPORT_SYMBOL_GPL(blkdev_report_zones);
191
192 /**
193  * blkdev_reset_zones - Reset zones write pointer
194  * @bdev:       Target block device
195  * @sector:     Start sector of the first zone to reset
196  * @nr_sectors: Number of sectors, at least the length of one zone
197  * @gfp_mask:   Memory allocation flags (for bio_alloc)
198  *
199  * Description:
200  *    Reset the write pointer of the zones contained in the range
201  *    @sector..@sector+@nr_sectors. Specifying the entire disk sector range
202  *    is valid, but the specified range should not contain conventional zones.
203  */
204 int blkdev_reset_zones(struct block_device *bdev,
205                        sector_t sector, sector_t nr_sectors,
206                        gfp_t gfp_mask)
207 {
208         struct request_queue *q = bdev_get_queue(bdev);
209         sector_t zone_sectors;
210         sector_t end_sector = sector + nr_sectors;
211         struct bio *bio;
212         int ret;
213
214         if (!q)
215                 return -ENXIO;
216
217         if (!blk_queue_is_zoned(q))
218                 return -EOPNOTSUPP;
219
220         if (end_sector > bdev->bd_part->nr_sects)
221                 /* Out of range */
222                 return -EINVAL;
223
224         /* Check alignment (handle eventual smaller last zone) */
225         zone_sectors = blk_queue_zone_sectors(q);
226         if (sector & (zone_sectors - 1))
227                 return -EINVAL;
228
229         if ((nr_sectors & (zone_sectors - 1)) &&
230             end_sector != bdev->bd_part->nr_sects)
231                 return -EINVAL;
232
233         while (sector < end_sector) {
234
235                 bio = bio_alloc(gfp_mask, 0);
236                 bio->bi_iter.bi_sector = sector;
237                 bio->bi_bdev = bdev;
238                 bio_set_op_attrs(bio, REQ_OP_ZONE_RESET, 0);
239
240                 ret = submit_bio_wait(bio);
241                 bio_put(bio);
242
243                 if (ret)
244                         return ret;
245
246                 sector += zone_sectors;
247
248                 /* This may take a while, so be nice to others */
249                 cond_resched();
250
251         }
252
253         return 0;
254 }
255 EXPORT_SYMBOL_GPL(blkdev_reset_zones);
256
257 /**
258  * BLKREPORTZONE ioctl processing.
259  * Called from blkdev_ioctl.
260  */
261 int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
262                               unsigned int cmd, unsigned long arg)
263 {
264         void __user *argp = (void __user *)arg;
265         struct request_queue *q;
266         struct blk_zone_report rep;
267         struct blk_zone *zones;
268         int ret;
269
270         if (!argp)
271                 return -EINVAL;
272
273         q = bdev_get_queue(bdev);
274         if (!q)
275                 return -ENXIO;
276
277         if (!blk_queue_is_zoned(q))
278                 return -ENOTTY;
279
280         if (!capable(CAP_SYS_ADMIN))
281                 return -EACCES;
282
283         if (copy_from_user(&rep, argp, sizeof(struct blk_zone_report)))
284                 return -EFAULT;
285
286         if (!rep.nr_zones)
287                 return -EINVAL;
288
289         zones = kcalloc(rep.nr_zones, sizeof(struct blk_zone), GFP_KERNEL);
290         if (!zones)
291                 return -ENOMEM;
292
293         ret = blkdev_report_zones(bdev, rep.sector,
294                                   zones, &rep.nr_zones,
295                                   GFP_KERNEL);
296         if (ret)
297                 goto out;
298
299         if (copy_to_user(argp, &rep, sizeof(struct blk_zone_report))) {
300                 ret = -EFAULT;
301                 goto out;
302         }
303
304         if (rep.nr_zones) {
305                 if (copy_to_user(argp + sizeof(struct blk_zone_report), zones,
306                                  sizeof(struct blk_zone) * rep.nr_zones))
307                         ret = -EFAULT;
308         }
309
310  out:
311         kfree(zones);
312
313         return ret;
314 }
315
316 /**
317  * BLKRESETZONE ioctl processing.
318  * Called from blkdev_ioctl.
319  */
320 int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode,
321                              unsigned int cmd, unsigned long arg)
322 {
323         void __user *argp = (void __user *)arg;
324         struct request_queue *q;
325         struct blk_zone_range zrange;
326
327         if (!argp)
328                 return -EINVAL;
329
330         q = bdev_get_queue(bdev);
331         if (!q)
332                 return -ENXIO;
333
334         if (!blk_queue_is_zoned(q))
335                 return -ENOTTY;
336
337         if (!capable(CAP_SYS_ADMIN))
338                 return -EACCES;
339
340         if (!(mode & FMODE_WRITE))
341                 return -EBADF;
342
343         if (copy_from_user(&zrange, argp, sizeof(struct blk_zone_range)))
344                 return -EFAULT;
345
346         return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors,
347                                   GFP_KERNEL);
348 }