summaryrefslogtreecommitdiff
path: root/block/blk-zoned.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/blk-zoned.c')
-rw-r--r--block/blk-zoned.c39
1 files changed, 39 insertions, 0 deletions
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 6c503824ba3f..4bc5f260248a 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -202,6 +202,42 @@ int blkdev_report_zones(struct block_device *bdev, sector_t sector,
}
EXPORT_SYMBOL_GPL(blkdev_report_zones);
+/*
+ * Special case of zone reset operation to reset all zones in one command,
+ * useful for applications like mkfs.
+ */
+static int __blkdev_reset_all_zones(struct block_device *bdev, gfp_t gfp_mask)
+{
+ struct bio *bio = bio_alloc(gfp_mask, 0);
+ int ret;
+
+ /* across the zones operations, don't need any sectors */
+ bio_set_dev(bio, bdev);
+ bio_set_op_attrs(bio, REQ_OP_ZONE_RESET_ALL, 0);
+
+ ret = submit_bio_wait(bio);
+ bio_put(bio);
+
+ return ret;
+}
+
+static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev,
+ sector_t nr_sectors)
+{
+ if (!blk_queue_zone_resetall(bdev_get_queue(bdev)))
+ return false;
+
+ if (nr_sectors != part_nr_sects_read(bdev->bd_part))
+ return false;
+ /*
+ * REQ_OP_ZONE_RESET_ALL can be executed only if the block device is
+ * the entire disk, that is, if the blocks device start offset is 0 and
+ * its capacity is the same as the entire disk.
+ */
+ return get_start_sect(bdev) == 0 &&
+ part_nr_sects_read(bdev->bd_part) == get_capacity(bdev->bd_disk);
+}
+
/**
* blkdev_reset_zones - Reset zones write pointer
* @bdev: Target block device
@@ -235,6 +271,9 @@ int blkdev_reset_zones(struct block_device *bdev,
/* Out of range */
return -EINVAL;
+ if (blkdev_allow_reset_all_zones(bdev, nr_sectors))
+ return __blkdev_reset_all_zones(bdev, gfp_mask);
+
/* Check alignment (handle eventual smaller last zone) */
zone_sectors = blk_queue_zone_sectors(q);
if (sector & (zone_sectors - 1))