Skip to content

Commit 1ee533e

Browse files
damien-lemoalsnitm
authored andcommitted
block: improve handling of all zones reset operation
SCSI, ZNS and null_blk zoned devices support resetting all zones using a single command (REQ_OP_ZONE_RESET_ALL), as indicated using the device request queue flag QUEUE_FLAG_ZONE_RESETALL. This flag is not set for device mapper targets creating zoned devices. In this case, a user request for resetting all zones of a device is processed in blkdev_zone_mgmt() by issuing a REQ_OP_ZONE_RESET operation for each zone of the device. This leads to different behaviors of the BLKRESETZONE ioctl() depending on the target device support for the reset all operation. E.g. blkzone reset /dev/sdX will reset all zones of a SCSI device using a single command that will ignore conventional, read-only or offline zones. But a dm-linear device including conventional, read-only or offline zones cannot be reset in the same manner as some of the single zone reset operations issued by blkdev_zone_mgmt() will fail. E.g.: blkzone reset /dev/dm-Y blkzone: /dev/dm-0: BLKRESETZONE ioctl failed: Remote I/O error To simplify applications and tools development, unify the behavior of the all-zone reset operation by modifying blkdev_zone_mgmt() to not issue a zone reset operation for conventional, read-only and offline zones, thus mimicking what an actual reset-all device command does on a device supporting REQ_OP_ZONE_RESET_ALL. This emulation is done using the new function blkdev_zone_reset_all_emulated(). The zones needing a reset are identified using a bitmap that is initialized using a zone report. Since empty zones do not need a reset, also ignore these zones. The function blkdev_zone_reset_all() is introduced for block devices natively supporting reset all operations. blkdev_zone_mgmt() is modified to call either function to execute an all zone reset request. Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> [hch: split into multiple functions] Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Acked-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
1 parent bf14e2b commit 1ee533e

File tree

1 file changed

+92
-27
lines changed

1 file changed

+92
-27
lines changed

block/blk-zoned.c

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,89 @@ int blkdev_report_zones(struct block_device *bdev, sector_t sector,
161161
}
162162
EXPORT_SYMBOL_GPL(blkdev_report_zones);
163163

164-
static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev,
165-
sector_t sector,
166-
sector_t nr_sectors)
164+
static inline unsigned long *blk_alloc_zone_bitmap(int node,
165+
unsigned int nr_zones)
167166
{
168-
if (!blk_queue_zone_resetall(bdev_get_queue(bdev)))
169-
return false;
167+
return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
168+
GFP_NOIO, node);
169+
}
170170

171+
static int blk_zone_need_reset_cb(struct blk_zone *zone, unsigned int idx,
172+
void *data)
173+
{
171174
/*
172-
* REQ_OP_ZONE_RESET_ALL can be executed only if the number of sectors
173-
* of the applicable zone range is the entire disk.
175+
* For an all-zones reset, ignore conventional, empty, read-only
176+
* and offline zones.
174177
*/
175-
return !sector && nr_sectors == get_capacity(bdev->bd_disk);
178+
switch (zone->cond) {
179+
case BLK_ZONE_COND_NOT_WP:
180+
case BLK_ZONE_COND_EMPTY:
181+
case BLK_ZONE_COND_READONLY:
182+
case BLK_ZONE_COND_OFFLINE:
183+
return 0;
184+
default:
185+
set_bit(idx, (unsigned long *)data);
186+
return 0;
187+
}
188+
}
189+
190+
static int blkdev_zone_reset_all_emulated(struct block_device *bdev,
191+
gfp_t gfp_mask)
192+
{
193+
struct request_queue *q = bdev_get_queue(bdev);
194+
sector_t capacity = get_capacity(bdev->bd_disk);
195+
sector_t zone_sectors = blk_queue_zone_sectors(q);
196+
unsigned long *need_reset;
197+
struct bio *bio = NULL;
198+
sector_t sector = 0;
199+
int ret;
200+
201+
need_reset = blk_alloc_zone_bitmap(q->node, q->nr_zones);
202+
if (!need_reset)
203+
return -ENOMEM;
204+
205+
ret = bdev->bd_disk->fops->report_zones(bdev->bd_disk, 0,
206+
q->nr_zones, blk_zone_need_reset_cb,
207+
need_reset);
208+
if (ret < 0)
209+
goto out_free_need_reset;
210+
211+
ret = 0;
212+
while (sector < capacity) {
213+
if (!test_bit(blk_queue_zone_no(q, sector), need_reset)) {
214+
sector += zone_sectors;
215+
continue;
216+
}
217+
218+
bio = blk_next_bio(bio, 0, gfp_mask);
219+
bio_set_dev(bio, bdev);
220+
bio->bi_opf = REQ_OP_ZONE_RESET | REQ_SYNC;
221+
bio->bi_iter.bi_sector = sector;
222+
sector += zone_sectors;
223+
224+
/* This may take a while, so be nice to others */
225+
cond_resched();
226+
}
227+
228+
if (bio) {
229+
ret = submit_bio_wait(bio);
230+
bio_put(bio);
231+
}
232+
233+
out_free_need_reset:
234+
kfree(need_reset);
235+
return ret;
236+
}
237+
238+
static int blkdev_zone_reset_all(struct block_device *bdev, gfp_t gfp_mask)
239+
{
240+
struct bio bio;
241+
242+
bio_init(&bio, NULL, 0);
243+
bio_set_dev(&bio, bdev);
244+
bio.bi_opf = REQ_OP_ZONE_RESET_ALL | REQ_SYNC;
245+
246+
return submit_bio_wait(&bio);
176247
}
177248

178249
/**
@@ -200,7 +271,7 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
200271
sector_t capacity = get_capacity(bdev->bd_disk);
201272
sector_t end_sector = sector + nr_sectors;
202273
struct bio *bio = NULL;
203-
int ret;
274+
int ret = 0;
204275

205276
if (!blk_queue_is_zoned(q))
206277
return -EOPNOTSUPP;
@@ -222,20 +293,21 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
222293
if ((nr_sectors & (zone_sectors - 1)) && end_sector != capacity)
223294
return -EINVAL;
224295

296+
/*
297+
* In the case of a zone reset operation over all zones,
298+
* REQ_OP_ZONE_RESET_ALL can be used with devices supporting this
299+
* command. For other devices, we emulate this command behavior by
300+
* identifying the zones needing a reset.
301+
*/
302+
if (op == REQ_OP_ZONE_RESET && sector == 0 && nr_sectors == capacity) {
303+
if (!blk_queue_zone_resetall(q))
304+
return blkdev_zone_reset_all_emulated(bdev, gfp_mask);
305+
return blkdev_zone_reset_all(bdev, gfp_mask);
306+
}
307+
225308
while (sector < end_sector) {
226309
bio = blk_next_bio(bio, 0, gfp_mask);
227310
bio_set_dev(bio, bdev);
228-
229-
/*
230-
* Special case for the zone reset operation that reset all
231-
* zones, this is useful for applications like mkfs.
232-
*/
233-
if (op == REQ_OP_ZONE_RESET &&
234-
blkdev_allow_reset_all_zones(bdev, sector, nr_sectors)) {
235-
bio->bi_opf = REQ_OP_ZONE_RESET_ALL | REQ_SYNC;
236-
break;
237-
}
238-
239311
bio->bi_opf = op | REQ_SYNC;
240312
bio->bi_iter.bi_sector = sector;
241313
sector += zone_sectors;
@@ -396,13 +468,6 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode,
396468
return ret;
397469
}
398470

399-
static inline unsigned long *blk_alloc_zone_bitmap(int node,
400-
unsigned int nr_zones)
401-
{
402-
return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
403-
GFP_NOIO, node);
404-
}
405-
406471
void blk_queue_free_zone_bitmaps(struct request_queue *q)
407472
{
408473
kfree(q->conv_zones_bitmap);

0 commit comments

Comments
 (0)