Skip to content

Commit bf50545

Browse files
damien-lemoalaxboe
authored andcommitted
block: Introduce blk_revalidate_disk_zones()
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 <hare@suse.com> Reviewed-by: Christoph Hellwig <hch@lst.de> 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>
1 parent e76239a commit bf50545

File tree

9 files changed

+194
-198
lines changed

9 files changed

+194
-198
lines changed

block/blk-sysfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,8 @@ static void __blk_release_queue(struct work_struct *work)
852852
if (q->queue_tags)
853853
__blk_queue_free_tags(q);
854854

855+
blk_queue_free_zone_bitmaps(q);
856+
855857
if (!q->mq_ops) {
856858
if (q->exit_rq_fn)
857859
q->exit_rq_fn(q, q->fq->flush_rq);

block/blk-zoned.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/module.h>
1313
#include <linux/rbtree.h>
1414
#include <linux/blkdev.h>
15+
#include <linux/blk-mq.h>
1516

1617
#include "blk.h"
1718

@@ -359,3 +360,138 @@ int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode,
359360
return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors,
360361
GFP_KERNEL);
361362
}
363+
364+
static inline unsigned long *blk_alloc_zone_bitmap(int node,
365+
unsigned int nr_zones)
366+
{
367+
return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
368+
GFP_NOIO, node);
369+
}
370+
371+
/*
372+
* Allocate an array of struct blk_zone to get nr_zones zone information.
373+
* The allocated array may be smaller than nr_zones.
374+
*/
375+
static struct blk_zone *blk_alloc_zones(int node, unsigned int *nr_zones)
376+
{
377+
size_t size = *nr_zones * sizeof(struct blk_zone);
378+
struct page *page;
379+
int order;
380+
381+
for (order = get_order(size); order > 0; order--) {
382+
page = alloc_pages_node(node, GFP_NOIO | __GFP_ZERO, order);
383+
if (page) {
384+
*nr_zones = min_t(unsigned int, *nr_zones,
385+
(PAGE_SIZE << order) / sizeof(struct blk_zone));
386+
return page_address(page);
387+
}
388+
}
389+
390+
return NULL;
391+
}
392+
393+
void blk_queue_free_zone_bitmaps(struct request_queue *q)
394+
{
395+
kfree(q->seq_zones_bitmap);
396+
q->seq_zones_bitmap = NULL;
397+
kfree(q->seq_zones_wlock);
398+
q->seq_zones_wlock = NULL;
399+
}
400+
401+
/**
402+
* blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
403+
* @disk: Target disk
404+
*
405+
* Helper function for low-level device drivers to (re) allocate and initialize
406+
* a disk request queue zone bitmaps. This functions should normally be called
407+
* within the disk ->revalidate method. For BIO based queues, no zone bitmap
408+
* is allocated.
409+
*/
410+
int blk_revalidate_disk_zones(struct gendisk *disk)
411+
{
412+
struct request_queue *q = disk->queue;
413+
unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
414+
unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
415+
unsigned int i, rep_nr_zones = 0, z = 0, nrz;
416+
struct blk_zone *zones = NULL;
417+
sector_t sector = 0;
418+
int ret = 0;
419+
420+
/*
421+
* BIO based queues do not use a scheduler so only q->nr_zones
422+
* needs to be updated so that the sysfs exposed value is correct.
423+
*/
424+
if (!queue_is_rq_based(q)) {
425+
q->nr_zones = nr_zones;
426+
return 0;
427+
}
428+
429+
if (!blk_queue_is_zoned(q) || !nr_zones) {
430+
nr_zones = 0;
431+
goto update;
432+
}
433+
434+
/* Allocate bitmaps */
435+
ret = -ENOMEM;
436+
seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
437+
if (!seq_zones_wlock)
438+
goto out;
439+
seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
440+
if (!seq_zones_bitmap)
441+
goto out;
442+
443+
/* Get zone information and initialize seq_zones_bitmap */
444+
rep_nr_zones = nr_zones;
445+
zones = blk_alloc_zones(q->node, &rep_nr_zones);
446+
if (!zones)
447+
goto out;
448+
449+
while (z < nr_zones) {
450+
nrz = min(nr_zones - z, rep_nr_zones);
451+
ret = blk_report_zones(disk, sector, zones, &nrz, GFP_NOIO);
452+
if (ret)
453+
goto out;
454+
if (!nrz)
455+
break;
456+
for (i = 0; i < nrz; i++) {
457+
if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
458+
set_bit(z, seq_zones_bitmap);
459+
z++;
460+
}
461+
sector += nrz * blk_queue_zone_sectors(q);
462+
}
463+
464+
if (WARN_ON(z != nr_zones)) {
465+
ret = -EIO;
466+
goto out;
467+
}
468+
469+
update:
470+
/*
471+
* Install the new bitmaps, making sure the queue is stopped and
472+
* all I/Os are completed (i.e. a scheduler is not referencing the
473+
* bitmaps).
474+
*/
475+
blk_mq_freeze_queue(q);
476+
q->nr_zones = nr_zones;
477+
swap(q->seq_zones_wlock, seq_zones_wlock);
478+
swap(q->seq_zones_bitmap, seq_zones_bitmap);
479+
blk_mq_unfreeze_queue(q);
480+
481+
out:
482+
free_pages((unsigned long)zones,
483+
get_order(rep_nr_zones * sizeof(struct blk_zone)));
484+
kfree(seq_zones_wlock);
485+
kfree(seq_zones_bitmap);
486+
487+
if (ret) {
488+
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
489+
blk_mq_freeze_queue(q);
490+
blk_queue_free_zone_bitmaps(q);
491+
blk_mq_unfreeze_queue(q);
492+
}
493+
494+
return ret;
495+
}
496+
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
497+

block/blk.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,4 +490,10 @@ static inline int blk_iolatency_init(struct request_queue *q) { return 0; }
490490

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

493+
#ifdef CONFIG_BLK_DEV_ZONED
494+
void blk_queue_free_zone_bitmaps(struct request_queue *q);
495+
#else
496+
static inline void blk_queue_free_zone_bitmaps(struct request_queue *q) {}
497+
#endif
498+
493499
#endif /* BLK_INTERNAL_H */

drivers/block/null_blk_main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,13 @@ static int null_gendisk_register(struct nullb *nullb)
15281528
disk->queue = nullb->q;
15291529
strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
15301530

1531+
if (nullb->dev->zoned) {
1532+
int ret = blk_revalidate_disk_zones(disk);
1533+
1534+
if (ret != 0)
1535+
return ret;
1536+
}
1537+
15311538
add_disk(disk);
15321539
return 0;
15331540
}

drivers/md/dm-table.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,16 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
19371937
*/
19381938
if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
19391939
blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
1940+
1941+
/*
1942+
* For a zoned target, the number of zones should be updated for the
1943+
* correct value to be exposed in sysfs queue/nr_zones. For a BIO based
1944+
* target, this is all that is needed. For a request based target, the
1945+
* queue zone bitmaps must also be updated.
1946+
* Use blk_revalidate_disk_zones() to handle this.
1947+
*/
1948+
if (blk_queue_is_zoned(q))
1949+
blk_revalidate_disk_zones(t->md->disk);
19401950
}
19411951

19421952
unsigned int dm_table_get_num_targets(struct dm_table *t)

drivers/scsi/sd.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3414,8 +3414,6 @@ static int sd_remove(struct device *dev)
34143414
del_gendisk(sdkp->disk);
34153415
sd_shutdown(dev);
34163416

3417-
sd_zbc_remove(sdkp);
3418-
34193417
free_opal_dev(sdkp->opal_dev);
34203418

34213419
blk_register_region(devt, SD_MINORS, NULL,

drivers/scsi/sd.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ struct scsi_disk {
7676
#ifdef CONFIG_BLK_DEV_ZONED
7777
u32 nr_zones;
7878
u32 zone_blocks;
79-
u32 zone_shift;
8079
u32 zones_optimal_open;
8180
u32 zones_optimal_nonseq;
8281
u32 zones_max_open;
@@ -271,7 +270,6 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp)
271270
#ifdef CONFIG_BLK_DEV_ZONED
272271

273272
extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer);
274-
extern void sd_zbc_remove(struct scsi_disk *sdkp);
275273
extern void sd_zbc_print_zones(struct scsi_disk *sdkp);
276274
extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd);
277275
extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
@@ -288,8 +286,6 @@ static inline int sd_zbc_read_zones(struct scsi_disk *sdkp,
288286
return 0;
289287
}
290288

291-
static inline void sd_zbc_remove(struct scsi_disk *sdkp) {}
292-
293289
static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {}
294290

295291
static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd)

0 commit comments

Comments
 (0)