Skip to content

Commit

Permalink
block: Introduce blk_revalidate_disk_zones()
Browse files Browse the repository at this point in the history
Drivers exposing zoned block devices have to initialize and maintain
correctness (i.e. revalidate) of the device zone bitmaps attached to
the device request queue (seq_zones_bitmap and seq_zones_wlock).

To simplify coding this, introduce a generic helper function
blk_revalidate_disk_zones() suitable for most (and likely all) cases.
This new function always update the seq_zones_bitmap and seq_zones_wlock
bitmaps as well as the queue nr_zones field when called for a disk
using a request based queue. For a disk using a BIO based queue, only
the number of zones is updated since these queues do not have
schedulers and so do not need the zone bitmaps.

With this change, the zone bitmap initialization code in sd_zbc.c can be
replaced with a call to this function in sd_zbc_read_zones(), which is
called from the disk revalidate block operation method.

A call to blk_revalidate_disk_zones() is also added to the null_blk
driver for devices created with the zoned mode enabled.

Finally, to ensure that zoned devices created with dm-linear or
dm-flakey expose the correct number of zones through sysfs, a call to
blk_revalidate_disk_zones() is added to dm_table_set_restrictions().

The zone bitmaps allocated and initialized with
blk_revalidate_disk_zones() are freed automatically from
__blk_release_queue() using the block internal function
blk_queue_free_zone_bitmaps().

Reviewed-by: Hannes Reinecke <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Reviewed-by: Martin K. Petersen <[email protected]>
Reviewed-by: Mike Snitzer <[email protected]>
Signed-off-by: Damien Le Moal <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
damien-lemoal authored and axboe committed Oct 25, 2018
1 parent e76239a commit bf50545
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 198 deletions.
2 changes: 2 additions & 0 deletions block/blk-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,8 @@ static void __blk_release_queue(struct work_struct *work)
if (q->queue_tags)
__blk_queue_free_tags(q);

blk_queue_free_zone_bitmaps(q);

if (!q->mq_ops) {
if (q->exit_rq_fn)
q->exit_rq_fn(q, q->fq->flush_rq);
Expand Down
136 changes: 136 additions & 0 deletions block/blk-zoned.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/rbtree.h>
#include <linux/blkdev.h>
#include <linux/blk-mq.h>

#include "blk.h"

Expand Down Expand Up @@ -359,3 +360,138 @@ int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode,
return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors,
GFP_KERNEL);
}

static inline unsigned long *blk_alloc_zone_bitmap(int node,
unsigned int nr_zones)
{
return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
GFP_NOIO, node);
}

/*
* Allocate an array of struct blk_zone to get nr_zones zone information.
* The allocated array may be smaller than nr_zones.
*/
static struct blk_zone *blk_alloc_zones(int node, unsigned int *nr_zones)
{
size_t size = *nr_zones * sizeof(struct blk_zone);
struct page *page;
int order;

for (order = get_order(size); order > 0; order--) {
page = alloc_pages_node(node, GFP_NOIO | __GFP_ZERO, order);
if (page) {
*nr_zones = min_t(unsigned int, *nr_zones,
(PAGE_SIZE << order) / sizeof(struct blk_zone));
return page_address(page);
}
}

return NULL;
}

void blk_queue_free_zone_bitmaps(struct request_queue *q)
{
kfree(q->seq_zones_bitmap);
q->seq_zones_bitmap = NULL;
kfree(q->seq_zones_wlock);
q->seq_zones_wlock = NULL;
}

/**
* blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
* @disk: Target disk
*
* Helper function for low-level device drivers to (re) allocate and initialize
* a disk request queue zone bitmaps. This functions should normally be called
* within the disk ->revalidate method. For BIO based queues, no zone bitmap
* is allocated.
*/
int blk_revalidate_disk_zones(struct gendisk *disk)
{
struct request_queue *q = disk->queue;
unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
unsigned int i, rep_nr_zones = 0, z = 0, nrz;
struct blk_zone *zones = NULL;
sector_t sector = 0;
int ret = 0;

/*
* BIO based queues do not use a scheduler so only q->nr_zones
* needs to be updated so that the sysfs exposed value is correct.
*/
if (!queue_is_rq_based(q)) {
q->nr_zones = nr_zones;
return 0;
}

if (!blk_queue_is_zoned(q) || !nr_zones) {
nr_zones = 0;
goto update;
}

/* Allocate bitmaps */
ret = -ENOMEM;
seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
if (!seq_zones_wlock)
goto out;
seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
if (!seq_zones_bitmap)
goto out;

/* Get zone information and initialize seq_zones_bitmap */
rep_nr_zones = nr_zones;
zones = blk_alloc_zones(q->node, &rep_nr_zones);
if (!zones)
goto out;

while (z < nr_zones) {
nrz = min(nr_zones - z, rep_nr_zones);
ret = blk_report_zones(disk, sector, zones, &nrz, GFP_NOIO);
if (ret)
goto out;
if (!nrz)
break;
for (i = 0; i < nrz; i++) {
if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
set_bit(z, seq_zones_bitmap);
z++;
}
sector += nrz * blk_queue_zone_sectors(q);
}

if (WARN_ON(z != nr_zones)) {
ret = -EIO;
goto out;
}

update:
/*
* Install the new bitmaps, making sure the queue is stopped and
* all I/Os are completed (i.e. a scheduler is not referencing the
* bitmaps).
*/
blk_mq_freeze_queue(q);
q->nr_zones = nr_zones;
swap(q->seq_zones_wlock, seq_zones_wlock);
swap(q->seq_zones_bitmap, seq_zones_bitmap);
blk_mq_unfreeze_queue(q);

out:
free_pages((unsigned long)zones,
get_order(rep_nr_zones * sizeof(struct blk_zone)));
kfree(seq_zones_wlock);
kfree(seq_zones_bitmap);

if (ret) {
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
blk_mq_freeze_queue(q);
blk_queue_free_zone_bitmaps(q);
blk_mq_unfreeze_queue(q);
}

return ret;
}
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);

6 changes: 6 additions & 0 deletions block/blk.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,10 @@ static inline int blk_iolatency_init(struct request_queue *q) { return 0; }

struct bio *blk_next_bio(struct bio *bio, unsigned int nr_pages, gfp_t gfp);

#ifdef CONFIG_BLK_DEV_ZONED
void blk_queue_free_zone_bitmaps(struct request_queue *q);
#else
static inline void blk_queue_free_zone_bitmaps(struct request_queue *q) {}
#endif

#endif /* BLK_INTERNAL_H */
7 changes: 7 additions & 0 deletions drivers/block/null_blk_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1528,6 +1528,13 @@ static int null_gendisk_register(struct nullb *nullb)
disk->queue = nullb->q;
strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);

if (nullb->dev->zoned) {
int ret = blk_revalidate_disk_zones(disk);

if (ret != 0)
return ret;
}

add_disk(disk);
return 0;
}
Expand Down
10 changes: 10 additions & 0 deletions drivers/md/dm-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,16 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
*/
if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);

/*
* For a zoned target, the number of zones should be updated for the
* correct value to be exposed in sysfs queue/nr_zones. For a BIO based
* target, this is all that is needed. For a request based target, the
* queue zone bitmaps must also be updated.
* Use blk_revalidate_disk_zones() to handle this.
*/
if (blk_queue_is_zoned(q))
blk_revalidate_disk_zones(t->md->disk);
}

unsigned int dm_table_get_num_targets(struct dm_table *t)
Expand Down
2 changes: 0 additions & 2 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3414,8 +3414,6 @@ static int sd_remove(struct device *dev)
del_gendisk(sdkp->disk);
sd_shutdown(dev);

sd_zbc_remove(sdkp);

free_opal_dev(sdkp->opal_dev);

blk_register_region(devt, SD_MINORS, NULL,
Expand Down
4 changes: 0 additions & 4 deletions drivers/scsi/sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ struct scsi_disk {
#ifdef CONFIG_BLK_DEV_ZONED
u32 nr_zones;
u32 zone_blocks;
u32 zone_shift;
u32 zones_optimal_open;
u32 zones_optimal_nonseq;
u32 zones_max_open;
Expand Down Expand Up @@ -271,7 +270,6 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp)
#ifdef CONFIG_BLK_DEV_ZONED

extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer);
extern void sd_zbc_remove(struct scsi_disk *sdkp);
extern void sd_zbc_print_zones(struct scsi_disk *sdkp);
extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd);
extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
Expand All @@ -288,8 +286,6 @@ static inline int sd_zbc_read_zones(struct scsi_disk *sdkp,
return 0;
}

static inline void sd_zbc_remove(struct scsi_disk *sdkp) {}

static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {}

static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd)
Expand Down
Loading

0 comments on commit bf50545

Please sign in to comment.