Skip to content

Commit

Permalink
block: add zone open, close and finish operations
Browse files Browse the repository at this point in the history
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: Javier González <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Signed-off-by: Ajay Joshi <[email protected]>
Signed-off-by: Matias Bjorling <[email protected]>
Signed-off-by: Hans Holmberg <[email protected]>
Signed-off-by: Dmitry Fomichev <[email protected]>
Signed-off-by: Keith Busch <[email protected]>
Signed-off-by: Damien Le Moal <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
ajaysjoshi authored and axboe committed Nov 7, 2019
1 parent c7a1d92 commit 6c1b1da
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 23 deletions.
12 changes: 9 additions & 3 deletions block/blk-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ static const char *const blk_op_name[] = {
REQ_OP_NAME(SECURE_ERASE),
REQ_OP_NAME(ZONE_RESET),
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_ZEROES),
REQ_OP_NAME(SCSI_IN),
Expand Down Expand Up @@ -849,10 +852,10 @@ static inline int blk_partition_remap(struct bio *bio)
goto out;

/*
* Zone reset does not include bi_size so bio_sectors() is always 0.
* Include a test for the reset op code and perform the remap if needed.
* Zone management bios do not have a sector count but they do have
* 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)))
goto out;
bio->bi_iter.bi_sector += p->start_sect;
Expand Down Expand Up @@ -936,6 +939,9 @@ generic_make_request_checks(struct bio *bio)
goto not_supported;
break;
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))
goto not_supported;
break;
Expand Down
35 changes: 21 additions & 14 deletions block/blk-zoned.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* @sector: Start sector of the first zone to reset
* @nr_sectors: Number of sectors, at least the length of one zone
* @op: Operation to be performed on the zones
* @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)
*
* 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
* 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,
sector_t sector, sector_t nr_sectors,
gfp_t gfp_mask)
int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
sector_t sector, sector_t nr_sectors,
gfp_t gfp_mask)
{
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;
struct bio *bio = NULL;
int ret;
Expand All @@ -248,12 +252,14 @@ int blkdev_reset_zones(struct block_device *bdev,
if (bdev_read_only(bdev))
return -EPERM;

if (!op_is_zone_mgmt(op))
return -EOPNOTSUPP;

if (!nr_sectors || end_sector > bdev->bd_part->nr_sects)
/* Out of range */
return -EINVAL;

/* Check alignment (handle eventual smaller last zone) */
zone_sectors = blk_queue_zone_sectors(q);
if (sector & (zone_sectors - 1))
return -EINVAL;

Expand All @@ -269,12 +275,13 @@ int blkdev_reset_zones(struct block_device *bdev,
* Special case for the zone reset operation that reset all
* 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;
break;
}

bio->bi_opf = REQ_OP_ZONE_RESET;
bio->bi_opf = op;
bio->bi_iter.bi_sector = sector;
sector += zone_sectors;

Expand All @@ -287,7 +294,7 @@ int blkdev_reset_zones(struct block_device *bdev,

return ret;
}
EXPORT_SYMBOL_GPL(blkdev_reset_zones);
EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);

/*
* BLKREPORTZONE ioctl processing.
Expand Down Expand Up @@ -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)))
return -EFAULT;

return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors,
GFP_KERNEL);
return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
zrange.sector, zrange.nr_sectors, GFP_KERNEL);
}

static inline unsigned long *blk_alloc_zone_bitmap(int node,
Expand Down
6 changes: 3 additions & 3 deletions drivers/md/dm-zoned-metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
struct dmz_dev *dev = zmd->dev;

ret = blkdev_reset_zones(dev->bdev,
dmz_start_sect(zmd, zone),
dev->zone_nr_sectors, GFP_NOIO);
ret = blkdev_zone_mgmt(dev->bdev, REQ_OP_ZONE_RESET,
dmz_start_sect(zmd, zone),
dev->zone_nr_sectors, GFP_NOIO);
if (ret) {
dmz_dev_err(dev, "Reset zone %u failed %d",
dmz_id(zmd, zone), ret);
Expand Down
3 changes: 2 additions & 1 deletion fs/f2fs/segment.c
Original file line number Diff line number Diff line change
Expand Up @@ -1771,7 +1771,8 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
return -EIO;
}
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 */
Expand Down
25 changes: 25 additions & 0 deletions include/linux/blk_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ enum req_opf {
REQ_OP_ZONE_RESET_ALL = 8,
/* write the zero filled sector many times */
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 */
REQ_OP_SCSI_IN = 32,
Expand Down Expand Up @@ -417,6 +423,25 @@ static inline bool op_is_discard(unsigned int op)
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)
{
if (op_is_discard(op))
Expand Down
5 changes: 3 additions & 2 deletions include/linux/blkdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,9 @@ extern unsigned int blkdev_nr_zones(struct block_device *bdev);
extern int blkdev_report_zones(struct block_device *bdev,
sector_t sector, struct blk_zone *zones,
unsigned int *nr_zones);
extern int blkdev_reset_zones(struct block_device *bdev, sector_t sectors,
sector_t nr_sectors, gfp_t gfp_mask);
extern int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
sector_t sectors, sector_t nr_sectors,
gfp_t gfp_mask);
extern int blk_revalidate_disk_zones(struct gendisk *disk);

extern int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
Expand Down

0 comments on commit 6c1b1da

Please sign in to comment.