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>
This commit is contained in:

committed by
Jens Axboe

parent
965b652e90
commit
e76239a374
@@ -87,7 +87,9 @@ struct nullb {
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
int null_zone_init(struct nullb_device *dev);
|
||||
void null_zone_exit(struct nullb_device *dev);
|
||||
blk_status_t null_zone_report(struct nullb *nullb, struct bio *bio);
|
||||
int null_zone_report(struct gendisk *disk, sector_t sector,
|
||||
struct blk_zone *zones, unsigned int *nr_zones,
|
||||
gfp_t gfp_mask);
|
||||
void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
|
||||
unsigned int nr_sectors);
|
||||
void null_zone_reset(struct nullb_cmd *cmd, sector_t sector);
|
||||
@@ -97,10 +99,11 @@ static inline int null_zone_init(struct nullb_device *dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void null_zone_exit(struct nullb_device *dev) {}
|
||||
static inline blk_status_t null_zone_report(struct nullb *nullb,
|
||||
struct bio *bio)
|
||||
static inline int null_zone_report(struct gendisk *disk, sector_t sector,
|
||||
struct blk_zone *zones,
|
||||
unsigned int *nr_zones, gfp_t gfp_mask)
|
||||
{
|
||||
return BLK_STS_NOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
|
||||
unsigned int nr_sectors)
|
||||
|
@@ -1129,34 +1129,12 @@ static void null_restart_queue_async(struct nullb *nullb)
|
||||
blk_mq_start_stopped_hw_queues(q, true);
|
||||
}
|
||||
|
||||
static bool cmd_report_zone(struct nullb *nullb, struct nullb_cmd *cmd)
|
||||
{
|
||||
struct nullb_device *dev = cmd->nq->dev;
|
||||
|
||||
if (dev->queue_mode == NULL_Q_BIO) {
|
||||
if (bio_op(cmd->bio) == REQ_OP_ZONE_REPORT) {
|
||||
cmd->error = null_zone_report(nullb, cmd->bio);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (req_op(cmd->rq) == REQ_OP_ZONE_REPORT) {
|
||||
cmd->error = null_zone_report(nullb, cmd->rq->bio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static blk_status_t null_handle_cmd(struct nullb_cmd *cmd)
|
||||
{
|
||||
struct nullb_device *dev = cmd->nq->dev;
|
||||
struct nullb *nullb = dev->nullb;
|
||||
int err = 0;
|
||||
|
||||
if (cmd_report_zone(nullb, cmd))
|
||||
goto out;
|
||||
|
||||
if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) {
|
||||
struct request *rq = cmd->rq;
|
||||
|
||||
@@ -1443,6 +1421,7 @@ static const struct block_device_operations null_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = null_open,
|
||||
.release = null_release,
|
||||
.report_zones = null_zone_report,
|
||||
};
|
||||
|
||||
static void null_init_queue(struct nullb *nullb, struct nullb_queue *nq)
|
||||
|
@@ -48,54 +48,27 @@ void null_zone_exit(struct nullb_device *dev)
|
||||
kvfree(dev->zones);
|
||||
}
|
||||
|
||||
static void null_zone_fill_bio(struct nullb_device *dev, struct bio *bio,
|
||||
unsigned int zno, unsigned int nr_zones)
|
||||
{
|
||||
struct blk_zone_report_hdr *hdr = NULL;
|
||||
struct bio_vec bvec;
|
||||
struct bvec_iter iter;
|
||||
void *addr;
|
||||
unsigned int zones_to_cpy;
|
||||
|
||||
bio_for_each_segment(bvec, bio, iter) {
|
||||
addr = kmap_atomic(bvec.bv_page);
|
||||
|
||||
zones_to_cpy = bvec.bv_len / sizeof(struct blk_zone);
|
||||
|
||||
if (!hdr) {
|
||||
hdr = (struct blk_zone_report_hdr *)addr;
|
||||
hdr->nr_zones = nr_zones;
|
||||
zones_to_cpy--;
|
||||
addr += sizeof(struct blk_zone_report_hdr);
|
||||
}
|
||||
|
||||
zones_to_cpy = min_t(unsigned int, zones_to_cpy, nr_zones);
|
||||
|
||||
memcpy(addr, &dev->zones[zno],
|
||||
zones_to_cpy * sizeof(struct blk_zone));
|
||||
|
||||
kunmap_atomic(addr);
|
||||
|
||||
nr_zones -= zones_to_cpy;
|
||||
zno += zones_to_cpy;
|
||||
|
||||
if (!nr_zones)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
blk_status_t null_zone_report(struct nullb *nullb, struct bio *bio)
|
||||
int null_zone_report(struct gendisk *disk, sector_t sector,
|
||||
struct blk_zone *zones, unsigned int *nr_zones,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct nullb *nullb = disk->private_data;
|
||||
struct nullb_device *dev = nullb->dev;
|
||||
unsigned int zno = null_zone_no(dev, bio->bi_iter.bi_sector);
|
||||
unsigned int nr_zones = dev->nr_zones - zno;
|
||||
unsigned int max_zones;
|
||||
unsigned int zno, nrz = 0;
|
||||
|
||||
max_zones = (bio->bi_iter.bi_size / sizeof(struct blk_zone)) - 1;
|
||||
nr_zones = min_t(unsigned int, nr_zones, max_zones);
|
||||
null_zone_fill_bio(nullb->dev, bio, zno, nr_zones);
|
||||
if (!dev->zoned)
|
||||
/* Not a zoned null device */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return BLK_STS_OK;
|
||||
zno = null_zone_no(dev, sector);
|
||||
if (zno < dev->nr_zones) {
|
||||
nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno);
|
||||
memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone));
|
||||
}
|
||||
|
||||
*nr_zones = nrz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
|
||||
|
Reference in New Issue
Block a user