Commit 6c1b1da5 authored by Ajay Joshi's avatar Ajay Joshi Committed by Jens Axboe

block: add zone open, close and finish operations

Zoned block devices (ZBC and ZAC devices) allow an explicit control
over the condition (state) of zones. The operations allowed are:
* Open a zone: Transition to open condition to indicate that a zone will
  actively be written
* Close a zone: Transition to closed condition to release the drive
  resources used for writing to a zone
* Finish a zone: Transition an open or closed zone to the full
  condition to prevent write operations

To enable this control for in-kernel zoned block device users, define
the new request operations REQ_OP_ZONE_OPEN, REQ_OP_ZONE_CLOSE
and REQ_OP_ZONE_FINISH as well as the generic function
blkdev_zone_mgmt() for submitting these operations on a range of zones.
This results in blkdev_reset_zones() removal and replacement with this
new zone magement function. Users of blkdev_reset_zones() (f2fs and
dm-zoned) are updated accordingly.

Contains contributions from Matias Bjorling, Hans Holmberg,
Dmitry Fomichev, Keith Busch, Damien Le Moal and Christoph Hellwig.
Reviewed-by: default avatarJavier González <javier@javigon.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAjay Joshi <ajay.joshi@wdc.com>
Signed-off-by: default avatarMatias Bjorling <matias.bjorling@wdc.com>
Signed-off-by: default avatarHans Holmberg <hans.holmberg@wdc.com>
Signed-off-by: default avatarDmitry Fomichev <dmitry.fomichev@wdc.com>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
Signed-off-by: default avatarDamien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent c7a1d926
...@@ -132,6 +132,9 @@ static const char *const blk_op_name[] = { ...@@ -132,6 +132,9 @@ static const char *const blk_op_name[] = {
REQ_OP_NAME(SECURE_ERASE), REQ_OP_NAME(SECURE_ERASE),
REQ_OP_NAME(ZONE_RESET), REQ_OP_NAME(ZONE_RESET),
REQ_OP_NAME(ZONE_RESET_ALL), REQ_OP_NAME(ZONE_RESET_ALL),
REQ_OP_NAME(ZONE_OPEN),
REQ_OP_NAME(ZONE_CLOSE),
REQ_OP_NAME(ZONE_FINISH),
REQ_OP_NAME(WRITE_SAME), REQ_OP_NAME(WRITE_SAME),
REQ_OP_NAME(WRITE_ZEROES), REQ_OP_NAME(WRITE_ZEROES),
REQ_OP_NAME(SCSI_IN), REQ_OP_NAME(SCSI_IN),
...@@ -849,10 +852,10 @@ static inline int blk_partition_remap(struct bio *bio) ...@@ -849,10 +852,10 @@ static inline int blk_partition_remap(struct bio *bio)
goto out; goto out;
/* /*
* Zone reset does not include bi_size so bio_sectors() is always 0. * Zone management bios do not have a sector count but they do have
* Include a test for the reset op code and perform the remap if needed. * a start sector filled out and need to be remapped.
*/ */
if (bio_sectors(bio) || bio_op(bio) == REQ_OP_ZONE_RESET) { if (bio_sectors(bio) || op_is_zone_mgmt(bio_op(bio))) {
if (bio_check_eod(bio, part_nr_sects_read(p))) if (bio_check_eod(bio, part_nr_sects_read(p)))
goto out; goto out;
bio->bi_iter.bi_sector += p->start_sect; bio->bi_iter.bi_sector += p->start_sect;
...@@ -936,6 +939,9 @@ generic_make_request_checks(struct bio *bio) ...@@ -936,6 +939,9 @@ generic_make_request_checks(struct bio *bio)
goto not_supported; goto not_supported;
break; break;
case REQ_OP_ZONE_RESET: case REQ_OP_ZONE_RESET:
case REQ_OP_ZONE_OPEN:
case REQ_OP_ZONE_CLOSE:
case REQ_OP_ZONE_FINISH:
if (!blk_queue_is_zoned(q)) if (!blk_queue_is_zoned(q))
goto not_supported; goto not_supported;
break; break;
......
...@@ -221,23 +221,27 @@ static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev, ...@@ -221,23 +221,27 @@ static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev,
} }
/** /**
* blkdev_reset_zones - Reset zones write pointer * blkdev_zone_mgmt - Execute a zone management operation on a range of zones
* @bdev: Target block device * @bdev: Target block device
* @sector: Start sector of the first zone to reset * @op: Operation to be performed on the zones
* @nr_sectors: Number of sectors, at least the length of one zone * @sector: Start sector of the first zone to operate on
* @nr_sectors: Number of sectors, should be at least the length of one zone and
* must be zone size aligned.
* @gfp_mask: Memory allocation flags (for bio_alloc) * @gfp_mask: Memory allocation flags (for bio_alloc)
* *
* Description: * Description:
* Reset the write pointer of the zones contained in the range * Perform the specified operation on the range of zones specified by
* @sector..@sector+@nr_sectors. Specifying the entire disk sector range * @sector..@sector+@nr_sectors. Specifying the entire disk sector range
* is valid, but the specified range should not contain conventional zones. * is valid, but the specified range should not contain conventional zones.
* The operation to execute on each zone can be a zone reset, open, close
* or finish request.
*/ */
int blkdev_reset_zones(struct block_device *bdev, int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
sector_t sector, sector_t nr_sectors, sector_t sector, sector_t nr_sectors,
gfp_t gfp_mask) gfp_t gfp_mask)
{ {
struct request_queue *q = bdev_get_queue(bdev); struct request_queue *q = bdev_get_queue(bdev);
sector_t zone_sectors; sector_t zone_sectors = blk_queue_zone_sectors(q);
sector_t end_sector = sector + nr_sectors; sector_t end_sector = sector + nr_sectors;
struct bio *bio = NULL; struct bio *bio = NULL;
int ret; int ret;
...@@ -248,12 +252,14 @@ int blkdev_reset_zones(struct block_device *bdev, ...@@ -248,12 +252,14 @@ int blkdev_reset_zones(struct block_device *bdev,
if (bdev_read_only(bdev)) if (bdev_read_only(bdev))
return -EPERM; return -EPERM;
if (!op_is_zone_mgmt(op))
return -EOPNOTSUPP;
if (!nr_sectors || end_sector > bdev->bd_part->nr_sects) if (!nr_sectors || end_sector > bdev->bd_part->nr_sects)
/* Out of range */ /* Out of range */
return -EINVAL; return -EINVAL;
/* Check alignment (handle eventual smaller last zone) */ /* Check alignment (handle eventual smaller last zone) */
zone_sectors = blk_queue_zone_sectors(q);
if (sector & (zone_sectors - 1)) if (sector & (zone_sectors - 1))
return -EINVAL; return -EINVAL;
...@@ -269,12 +275,13 @@ int blkdev_reset_zones(struct block_device *bdev, ...@@ -269,12 +275,13 @@ int blkdev_reset_zones(struct block_device *bdev,
* Special case for the zone reset operation that reset all * Special case for the zone reset operation that reset all
* zones, this is useful for applications like mkfs. * zones, this is useful for applications like mkfs.
*/ */
if (blkdev_allow_reset_all_zones(bdev, sector, nr_sectors)) { if (op == REQ_OP_ZONE_RESET &&
blkdev_allow_reset_all_zones(bdev, sector, nr_sectors)) {
bio->bi_opf = REQ_OP_ZONE_RESET_ALL; bio->bi_opf = REQ_OP_ZONE_RESET_ALL;
break; break;
} }
bio->bi_opf = REQ_OP_ZONE_RESET; bio->bi_opf = op;
bio->bi_iter.bi_sector = sector; bio->bi_iter.bi_sector = sector;
sector += zone_sectors; sector += zone_sectors;
...@@ -287,7 +294,7 @@ int blkdev_reset_zones(struct block_device *bdev, ...@@ -287,7 +294,7 @@ int blkdev_reset_zones(struct block_device *bdev,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(blkdev_reset_zones); EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);
/* /*
* BLKREPORTZONE ioctl processing. * BLKREPORTZONE ioctl processing.
...@@ -379,8 +386,8 @@ int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -379,8 +386,8 @@ int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode,
if (copy_from_user(&zrange, argp, sizeof(struct blk_zone_range))) if (copy_from_user(&zrange, argp, sizeof(struct blk_zone_range)))
return -EFAULT; return -EFAULT;
return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors, return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
GFP_KERNEL); zrange.sector, zrange.nr_sectors, GFP_KERNEL);
} }
static inline unsigned long *blk_alloc_zone_bitmap(int node, static inline unsigned long *blk_alloc_zone_bitmap(int node,
......
...@@ -1312,9 +1312,9 @@ static int dmz_reset_zone(struct dmz_metadata *zmd, struct dm_zone *zone) ...@@ -1312,9 +1312,9 @@ static int dmz_reset_zone(struct dmz_metadata *zmd, struct dm_zone *zone)
if (!dmz_is_empty(zone) || dmz_seq_write_err(zone)) { if (!dmz_is_empty(zone) || dmz_seq_write_err(zone)) {
struct dmz_dev *dev = zmd->dev; struct dmz_dev *dev = zmd->dev;
ret = blkdev_reset_zones(dev->bdev, ret = blkdev_zone_mgmt(dev->bdev, REQ_OP_ZONE_RESET,
dmz_start_sect(zmd, zone), dmz_start_sect(zmd, zone),
dev->zone_nr_sectors, GFP_NOIO); dev->zone_nr_sectors, GFP_NOIO);
if (ret) { if (ret) {
dmz_dev_err(dev, "Reset zone %u failed %d", dmz_dev_err(dev, "Reset zone %u failed %d",
dmz_id(zmd, zone), ret); dmz_id(zmd, zone), ret);
......
...@@ -1771,7 +1771,8 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, ...@@ -1771,7 +1771,8 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
return -EIO; return -EIO;
} }
trace_f2fs_issue_reset_zone(bdev, blkstart); trace_f2fs_issue_reset_zone(bdev, blkstart);
return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS); return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
sector, nr_sects, GFP_NOFS);
} }
/* For conventional zones, use regular discard if supported */ /* For conventional zones, use regular discard if supported */
......
...@@ -290,6 +290,12 @@ enum req_opf { ...@@ -290,6 +290,12 @@ enum req_opf {
REQ_OP_ZONE_RESET_ALL = 8, REQ_OP_ZONE_RESET_ALL = 8,
/* write the zero filled sector many times */ /* write the zero filled sector many times */
REQ_OP_WRITE_ZEROES = 9, REQ_OP_WRITE_ZEROES = 9,
/* Open a zone */
REQ_OP_ZONE_OPEN = 10,
/* Close a zone */
REQ_OP_ZONE_CLOSE = 11,
/* Transition a zone to full */
REQ_OP_ZONE_FINISH = 12,
/* SCSI passthrough using struct scsi_request */ /* SCSI passthrough using struct scsi_request */
REQ_OP_SCSI_IN = 32, REQ_OP_SCSI_IN = 32,
...@@ -417,6 +423,25 @@ static inline bool op_is_discard(unsigned int op) ...@@ -417,6 +423,25 @@ static inline bool op_is_discard(unsigned int op)
return (op & REQ_OP_MASK) == REQ_OP_DISCARD; return (op & REQ_OP_MASK) == REQ_OP_DISCARD;
} }
/*
* Check if a bio or request operation is a zone management operation, with
* the exception of REQ_OP_ZONE_RESET_ALL which is treated as a special case
* due to its different handling in the block layer and device response in
* case of command failure.
*/
static inline bool op_is_zone_mgmt(enum req_opf op)
{
switch (op & REQ_OP_MASK) {
case REQ_OP_ZONE_RESET:
case REQ_OP_ZONE_OPEN:
case REQ_OP_ZONE_CLOSE:
case REQ_OP_ZONE_FINISH:
return true;
default:
return false;
}
}
static inline int op_stat_group(unsigned int op) static inline int op_stat_group(unsigned int op)
{ {
if (op_is_discard(op)) if (op_is_discard(op))
......
...@@ -360,8 +360,9 @@ extern unsigned int blkdev_nr_zones(struct block_device *bdev); ...@@ -360,8 +360,9 @@ extern unsigned int blkdev_nr_zones(struct block_device *bdev);
extern int blkdev_report_zones(struct block_device *bdev, extern int blkdev_report_zones(struct block_device *bdev,
sector_t sector, struct blk_zone *zones, sector_t sector, struct blk_zone *zones,
unsigned int *nr_zones); unsigned int *nr_zones);
extern int blkdev_reset_zones(struct block_device *bdev, sector_t sectors, extern int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
sector_t nr_sectors, gfp_t gfp_mask); sector_t sectors, sector_t nr_sectors,
gfp_t gfp_mask);
extern int blk_revalidate_disk_zones(struct gendisk *disk); extern int blk_revalidate_disk_zones(struct gendisk *disk);
extern int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode, extern int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment