Skip to content

Commit 03e51c4

Browse files
damien-lemoalmartinkpetersen
authored andcommitted
scsi: block: Improve checks in blk_revalidate_disk_zones()
blk_revalidate_disk_zones() implements checks of the zones of a zoned block device, verifying that the zone size is a power of 2 number of sectors, that all zones (except possibly the last one) have the same size and that zones cover the entire addressing space of the device. While these checks are appropriate to verify that well tested hardware devices have an adequate zone configurations, they lack in certain areas which may result in issues with emulated devices implemented with user drivers such as ublk or tcmu. Specifically, this function does not check if the device driver indicated support for the mandatory zone append writes, that is, if the device max_zone_append_sectors queue limit is set to a non-zero value. Additionally, invalid zones such as a zero length zone with a start sector equal to the device capacity will not be detected and result in out of bounds use of the zone bitmaps prepared with the callback function blk_revalidate_zone_cb(). Improve blk_revalidate_disk_zones() to address these inadequate checks, relying on the fact that all device drivers supporting zoned block devices must set the device zone size (chunk_sectors queue limit) and the max_zone_append_sectors queue limit before executing this function. The check for a non-zero max_zone_append_sectors value is done in blk_revalidate_disk_zones() before executing the zone report. The zone report callback function blk_revalidate_zone_cb() is also modified to add a check that a zone start is below the device capacity. The check that the zone size is a power of 2 number of sectors is moved to blk_revalidate_disk_zones() as the zone size is already known. Similarly, the number of zones of the device can be calculated in blk_revalidate_disk_zones() before executing the zone report. The kdoc comment for blk_revalidate_disk_zones() is also updated to mention that device drivers must set the device zone size and the max_zone_append_sectors queue limit before calling this function. Signed-off-by: Damien Le Moal <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Bart Van Assche <[email protected]> Reviewed-by: Johannes Thumshirn <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent a3d96ed commit 03e51c4

File tree

1 file changed

+50
-36
lines changed

1 file changed

+50
-36
lines changed

block/blk-zoned.c

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,6 @@ struct blk_revalidate_zone_args {
448448
unsigned long *conv_zones_bitmap;
449449
unsigned long *seq_zones_wlock;
450450
unsigned int nr_zones;
451-
sector_t zone_sectors;
452451
sector_t sector;
453452
};
454453

@@ -462,38 +461,34 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
462461
struct gendisk *disk = args->disk;
463462
struct request_queue *q = disk->queue;
464463
sector_t capacity = get_capacity(disk);
464+
sector_t zone_sectors = q->limits.chunk_sectors;
465+
466+
/* Check for bad zones and holes in the zone report */
467+
if (zone->start != args->sector) {
468+
pr_warn("%s: Zone gap at sectors %llu..%llu\n",
469+
disk->disk_name, args->sector, zone->start);
470+
return -ENODEV;
471+
}
472+
473+
if (zone->start >= capacity || !zone->len) {
474+
pr_warn("%s: Invalid zone start %llu, length %llu\n",
475+
disk->disk_name, zone->start, zone->len);
476+
return -ENODEV;
477+
}
465478

466479
/*
467480
* All zones must have the same size, with the exception on an eventual
468481
* smaller last zone.
469482
*/
470-
if (zone->start == 0) {
471-
if (zone->len == 0 || !is_power_of_2(zone->len)) {
472-
pr_warn("%s: Invalid zoned device with non power of two zone size (%llu)\n",
473-
disk->disk_name, zone->len);
474-
return -ENODEV;
475-
}
476-
477-
args->zone_sectors = zone->len;
478-
args->nr_zones = (capacity + zone->len - 1) >> ilog2(zone->len);
479-
} else if (zone->start + args->zone_sectors < capacity) {
480-
if (zone->len != args->zone_sectors) {
483+
if (zone->start + zone->len < capacity) {
484+
if (zone->len != zone_sectors) {
481485
pr_warn("%s: Invalid zoned device with non constant zone size\n",
482486
disk->disk_name);
483487
return -ENODEV;
484488
}
485-
} else {
486-
if (zone->len > args->zone_sectors) {
487-
pr_warn("%s: Invalid zoned device with larger last zone size\n",
488-
disk->disk_name);
489-
return -ENODEV;
490-
}
491-
}
492-
493-
/* Check for holes in the zone report */
494-
if (zone->start != args->sector) {
495-
pr_warn("%s: Zone gap at sectors %llu..%llu\n",
496-
disk->disk_name, args->sector, zone->start);
489+
} else if (zone->len > zone_sectors) {
490+
pr_warn("%s: Invalid zoned device with larger last zone size\n",
491+
disk->disk_name);
497492
return -ENODEV;
498493
}
499494

@@ -532,11 +527,13 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
532527
* @disk: Target disk
533528
* @update_driver_data: Callback to update driver data on the frozen disk
534529
*
535-
* Helper function for low-level device drivers to (re) allocate and initialize
536-
* a disk request queue zone bitmaps. This functions should normally be called
537-
* within the disk ->revalidate method for blk-mq based drivers. For BIO based
538-
* drivers only q->nr_zones needs to be updated so that the sysfs exposed value
539-
* is correct.
530+
* Helper function for low-level device drivers to check and (re) allocate and
531+
* initialize a disk request queue zone bitmaps. This functions should normally
532+
* be called within the disk ->revalidate method for blk-mq based drivers.
533+
* Before calling this function, the device driver must already have set the
534+
* device zone size (chunk_sector limit) and the max zone append limit.
535+
* For BIO based drivers, this function cannot be used. BIO based device drivers
536+
* only need to set disk->nr_zones so that the sysfs exposed value is correct.
540537
* If the @update_driver_data callback function is not NULL, the callback is
541538
* executed with the device request queue frozen after all zones have been
542539
* checked.
@@ -545,9 +542,9 @@ int blk_revalidate_disk_zones(struct gendisk *disk,
545542
void (*update_driver_data)(struct gendisk *disk))
546543
{
547544
struct request_queue *q = disk->queue;
548-
struct blk_revalidate_zone_args args = {
549-
.disk = disk,
550-
};
545+
sector_t zone_sectors = q->limits.chunk_sectors;
546+
sector_t capacity = get_capacity(disk);
547+
struct blk_revalidate_zone_args args = { };
551548
unsigned int noio_flag;
552549
int ret;
553550

@@ -556,13 +553,31 @@ int blk_revalidate_disk_zones(struct gendisk *disk,
556553
if (WARN_ON_ONCE(!queue_is_mq(q)))
557554
return -EIO;
558555

559-
if (!get_capacity(disk))
560-
return -EIO;
556+
if (!capacity)
557+
return -ENODEV;
558+
559+
/*
560+
* Checks that the device driver indicated a valid zone size and that
561+
* the max zone append limit is set.
562+
*/
563+
if (!zone_sectors || !is_power_of_2(zone_sectors)) {
564+
pr_warn("%s: Invalid non power of two zone size (%llu)\n",
565+
disk->disk_name, zone_sectors);
566+
return -ENODEV;
567+
}
568+
569+
if (!q->limits.max_zone_append_sectors) {
570+
pr_warn("%s: Invalid 0 maximum zone append limit\n",
571+
disk->disk_name);
572+
return -ENODEV;
573+
}
561574

562575
/*
563576
* Ensure that all memory allocations in this context are done as if
564577
* GFP_NOIO was specified.
565578
*/
579+
args.disk = disk;
580+
args.nr_zones = (capacity + zone_sectors - 1) >> ilog2(zone_sectors);
566581
noio_flag = memalloc_noio_save();
567582
ret = disk->fops->report_zones(disk, 0, UINT_MAX,
568583
blk_revalidate_zone_cb, &args);
@@ -576,7 +591,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk,
576591
* If zones where reported, make sure that the entire disk capacity
577592
* has been checked.
578593
*/
579-
if (ret > 0 && args.sector != get_capacity(disk)) {
594+
if (ret > 0 && args.sector != capacity) {
580595
pr_warn("%s: Missing zones from sector %llu\n",
581596
disk->disk_name, args.sector);
582597
ret = -ENODEV;
@@ -589,7 +604,6 @@ int blk_revalidate_disk_zones(struct gendisk *disk,
589604
*/
590605
blk_mq_freeze_queue(q);
591606
if (ret > 0) {
592-
blk_queue_chunk_sectors(q, args.zone_sectors);
593607
disk->nr_zones = args.nr_zones;
594608
swap(disk->seq_zones_wlock, args.seq_zones_wlock);
595609
swap(disk->conv_zones_bitmap, args.conv_zones_bitmap);

0 commit comments

Comments
 (0)