summaryrefslogtreecommitdiffstats
path: root/block/blk-zoned.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2018-10-12 06:08:49 -0400
committerJens Axboe <axboe@kernel.dk>2018-10-25 13:17:40 -0400
commite76239a3748c90a8b0e197f8f4544a8ce52f126e (patch)
tree4e555b7fe24f1642eecc1cfbca563897cfeb6d4a /block/blk-zoned.c
parent965b652e901886ea54f93c60027b5be76328d958 (diff)
block: add a report_zones method
Dispatching a report zones command through the request queue is a major pain due to the command reply payload rewriting necessary. Given that blkdev_report_zones() is executing everything synchronously, implement report zones as a block device file operation instead, allowing major simplification of the code in many places. sd, null-blk, dm-linear and dm-flakey being the only block device drivers supporting exposing zoned block devices, these drivers are modified to provide the device side implementation of the report_zones() block device file operation. For device mappers, a new report_zones() target type operation is defined so that the upper block layer calls blkdev_report_zones() can be propagated down to the underlying devices of the dm targets. Implementation for this new operation is added to the dm-linear and dm-flakey targets. Reviewed-by: Hannes Reinecke <hare@suse.com> Signed-off-by: Christoph Hellwig <hch@lst.de> [Damien] * Changed method block_device argument to gendisk * Various bug fixes and improvements * Added support for null_blk, dm-linear and dm-flakey. Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Reviewed-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/blk-zoned.c')
-rw-r--r--block/blk-zoned.c164
1 files changed, 51 insertions, 113 deletions
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 5d967fd39fbd..90cf503091d5 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -93,13 +93,10 @@ unsigned int blkdev_nr_zones(struct block_device *bdev)
93EXPORT_SYMBOL_GPL(blkdev_nr_zones); 93EXPORT_SYMBOL_GPL(blkdev_nr_zones);
94 94
95/* 95/*
96 * Check that a zone report belongs to the partition. 96 * Check that a zone report belongs to this partition, and if yes, fix its start
97 * If yes, fix its start sector and write pointer, copy it in the 97 * sector and write pointer and return true. Return false otherwise.
98 * zone information array and return true. Return false otherwise.
99 */ 98 */
100static bool blkdev_report_zone(struct block_device *bdev, 99static bool blkdev_report_zone(struct block_device *bdev, struct blk_zone *rep)
101 struct blk_zone *rep,
102 struct blk_zone *zone)
103{ 100{
104 sector_t offset = get_start_sect(bdev); 101 sector_t offset = get_start_sect(bdev);
105 102
@@ -114,11 +111,36 @@ static bool blkdev_report_zone(struct block_device *bdev,
114 rep->wp = rep->start + rep->len; 111 rep->wp = rep->start + rep->len;
115 else 112 else
116 rep->wp -= offset; 113 rep->wp -= offset;
117 memcpy(zone, rep, sizeof(struct blk_zone));
118
119 return true; 114 return true;
120} 115}
121 116
117static int blk_report_zones(struct gendisk *disk, sector_t sector,
118 struct blk_zone *zones, unsigned int *nr_zones,
119 gfp_t gfp_mask)
120{
121 struct request_queue *q = disk->queue;
122 unsigned int z = 0, n, nrz = *nr_zones;
123 sector_t capacity = get_capacity(disk);
124 int ret;
125
126 while (z < nrz && sector < capacity) {
127 n = nrz - z;
128 ret = disk->fops->report_zones(disk, sector, &zones[z], &n,
129 gfp_mask);
130 if (ret)
131 return ret;
132 if (!n)
133 break;
134 sector += blk_queue_zone_sectors(q) * n;
135 z += n;
136 }
137
138 WARN_ON(z > *nr_zones);
139 *nr_zones = z;
140
141 return 0;
142}
143
122/** 144/**
123 * blkdev_report_zones - Get zones information 145 * blkdev_report_zones - Get zones information
124 * @bdev: Target block device 146 * @bdev: Target block device
@@ -133,130 +155,46 @@ static bool blkdev_report_zone(struct block_device *bdev,
133 * requested by @nr_zones. The number of zones actually reported is 155 * requested by @nr_zones. The number of zones actually reported is
134 * returned in @nr_zones. 156 * returned in @nr_zones.
135 */ 157 */
136int blkdev_report_zones(struct block_device *bdev, 158int blkdev_report_zones(struct block_device *bdev, sector_t sector,
137 sector_t sector, 159 struct blk_zone *zones, unsigned int *nr_zones,
138 struct blk_zone *zones,
139 unsigned int *nr_zones,
140 gfp_t gfp_mask) 160 gfp_t gfp_mask)
141{ 161{
142 struct request_queue *q = bdev_get_queue(bdev); 162 struct request_queue *q = bdev_get_queue(bdev);
143 struct blk_zone_report_hdr *hdr; 163 unsigned int i, nrz;
144 unsigned int nrz = *nr_zones;
145 struct page *page;
146 unsigned int nr_rep;
147 size_t rep_bytes;
148 unsigned int nr_pages;
149 struct bio *bio;
150 struct bio_vec *bv;
151 unsigned int i, n, nz;
152 unsigned int ofst;
153 void *addr;
154 int ret; 164 int ret;
155 165
156 if (!q)
157 return -ENXIO;
158
159 if (!blk_queue_is_zoned(q)) 166 if (!blk_queue_is_zoned(q))
160 return -EOPNOTSUPP; 167 return -EOPNOTSUPP;
161 168
162 if (!nrz)
163 return 0;
164
165 if (sector > bdev->bd_part->nr_sects) {
166 *nr_zones = 0;
167 return 0;
168 }
169
170 /* 169 /*
171 * The zone report has a header. So make room for it in the 170 * A block device that advertized itself as zoned must have a
172 * payload. Also make sure that the report fits in a single BIO 171 * report_zones method. If it does not have one defined, the device
173 * that will not be split down the stack. 172 * driver has a bug. So warn about that.
174 */ 173 */
175 rep_bytes = sizeof(struct blk_zone_report_hdr) + 174 if (WARN_ON_ONCE(!bdev->bd_disk->fops->report_zones))
176 sizeof(struct blk_zone) * nrz; 175 return -EOPNOTSUPP;
177 rep_bytes = (rep_bytes + PAGE_SIZE - 1) & PAGE_MASK;
178 if (rep_bytes > (queue_max_sectors(q) << 9))
179 rep_bytes = queue_max_sectors(q) << 9;
180
181 nr_pages = min_t(unsigned int, BIO_MAX_PAGES,
182 rep_bytes >> PAGE_SHIFT);
183 nr_pages = min_t(unsigned int, nr_pages,
184 queue_max_segments(q));
185
186 bio = bio_alloc(gfp_mask, nr_pages);
187 if (!bio)
188 return -ENOMEM;
189 176
190 bio_set_dev(bio, bdev); 177 if (!*nr_zones || sector >= bdev->bd_part->nr_sects) {
191 bio->bi_iter.bi_sector = blk_zone_start(q, sector); 178 *nr_zones = 0;
192 bio_set_op_attrs(bio, REQ_OP_ZONE_REPORT, 0); 179 return 0;
193
194 for (i = 0; i < nr_pages; i++) {
195 page = alloc_page(gfp_mask);
196 if (!page) {
197 ret = -ENOMEM;
198 goto out;
199 }
200 if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
201 __free_page(page);
202 break;
203 }
204 } 180 }
205 181
206 if (i == 0) 182 nrz = min(*nr_zones,
207 ret = -ENOMEM; 183 __blkdev_nr_zones(q, bdev->bd_part->nr_sects - sector));
208 else 184 ret = blk_report_zones(bdev->bd_disk, get_start_sect(bdev) + sector,
209 ret = submit_bio_wait(bio); 185 zones, &nrz, gfp_mask);
210 if (ret) 186 if (ret)
211 goto out; 187 return ret;
212
213 /*
214 * Process the report result: skip the header and go through the
215 * reported zones to fixup and fixup the zone information for
216 * partitions. At the same time, return the zone information into
217 * the zone array.
218 */
219 n = 0;
220 nz = 0;
221 nr_rep = 0;
222 bio_for_each_segment_all(bv, bio, i) {
223 188
224 if (!bv->bv_page) 189 for (i = 0; i < nrz; i++) {
190 if (!blkdev_report_zone(bdev, zones))
225 break; 191 break;
226 192 zones++;
227 addr = kmap_atomic(bv->bv_page);
228
229 /* Get header in the first page */
230 ofst = 0;
231 if (!nr_rep) {
232 hdr = addr;
233 nr_rep = hdr->nr_zones;
234 ofst = sizeof(struct blk_zone_report_hdr);
235 }
236
237 /* Fixup and report zones */
238 while (ofst < bv->bv_len &&
239 n < nr_rep && nz < nrz) {
240 if (blkdev_report_zone(bdev, addr + ofst, &zones[nz]))
241 nz++;
242 ofst += sizeof(struct blk_zone);
243 n++;
244 }
245
246 kunmap_atomic(addr);
247
248 if (n >= nr_rep || nz >= nrz)
249 break;
250
251 } 193 }
252 194
253 *nr_zones = nz; 195 *nr_zones = i;
254out:
255 bio_for_each_segment_all(bv, bio, i)
256 __free_page(bv->bv_page);
257 bio_put(bio);
258 196
259 return ret; 197 return 0;
260} 198}
261EXPORT_SYMBOL_GPL(blkdev_report_zones); 199EXPORT_SYMBOL_GPL(blkdev_report_zones);
262 200