diff --git a/block/blk-zoned.c b/block/blk-zoned.c index dfe39222c935..33eeed0fdadb 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -1673,6 +1673,25 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx, return -ENODEV; } + if (zone->start == 0) { + if (zone->len == 0) { + pr_warn("%s: Invalid zero zone size", disk->disk_name); + return -ENODEV; + } + + /* + * Non power-of-2 zone size support was added to remove the gap + * between zone capacity and zone size. Though it is technically + * possible to have gaps in a non power-of-2 device, Linux + * requires the zone size to be equal to zone capacity for non + * power-of-2 zoned devices. + */ + if (!is_power_of_2(zone->len) && zone->capacity < zone->len) { + pr_err("%s: Invalid zone capacity %lld with non power-of-2 zone size %lld", + disk->disk_name, zone->capacity, zone->len); + return -ENODEV; + } + } /* * All zones must have the same size, with the exception on an eventual * smaller last zone. @@ -1748,9 +1767,8 @@ int blk_revalidate_disk_zones(struct gendisk *disk) * Checks that the device driver indicated a valid zone size and that * the max zone append limit is set. */ - if (!zone_sectors || !is_power_of_2(zone_sectors)) { - pr_warn("%s: Invalid non power of two zone size (%llu)\n", - disk->disk_name, zone_sectors); + if (!zone_sectors) { + pr_warn("%s: Invalid zone size\n", disk->disk_name); return -ENODEV; } @@ -1765,7 +1783,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk) * GFP_NOIO was specified. */ args.disk = disk; - args.nr_zones = (capacity + zone_sectors - 1) >> ilog2(zone_sectors); + args.nr_zones = div64_u64(capacity + zone_sectors - 1, zone_sectors); noio_flag = memalloc_noio_save(); ret = disk_revalidate_zone_resources(disk, args.nr_zones); if (ret) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1aad2ee18f4e..ee99ca51539e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -723,9 +723,13 @@ static inline bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs) static inline unsigned int disk_zone_no(struct gendisk *disk, sector_t sector) { + const sector_t zone_sectors = disk->queue->limits.chunk_sectors; + if (!blk_queue_is_zoned(disk->queue)) return 0; - return sector >> ilog2(disk->queue->limits.chunk_sectors); + if (is_power_of_2(zone_sectors)) + return sector >> ilog2(zone_sectors); + return div64_u64(sector, zone_sectors); } static inline unsigned int bdev_nr_zones(struct block_device *bdev) @@ -1404,7 +1408,17 @@ static inline sector_t bdev_zone_sectors(struct block_device *bdev) static inline sector_t bdev_offset_from_zone_start(struct block_device *bdev, sector_t sector) { - return sector & (bdev_zone_sectors(bdev) - 1); + sector_t zone_sectors = bdev_zone_sectors(bdev); + u64 remainder = 0; + + if (!bdev_is_zoned(bdev)) + return 0; + + if (is_power_of_2(zone_sectors)) + return sector & (zone_sectors - 1); + + div64_u64_rem(sector, zone_sectors, &remainder); + return remainder; } static inline sector_t bio_offset_from_zone_start(struct bio *bio)