block: Fix partition support for host aware zoned block devices

Commit b72053072c0b ("block: allow partitions on host aware zone
devices") introduced the helper function disk_has_partitions() to check
if a given disk has valid partitions. However, since this function result
directly depends on the disk partition table length rather than the
actual existence of valid partitions in the table, it returns true even
after all partitions are removed from the disk. For host aware zoned
block devices, this results in zone management support to be kept
disabled even after removing all partitions.

Fix this by changing disk_has_partitions() to walk through the partition
table entries and return true if and only if a valid non-zero size
partition is found.

Fixes: b72053072c0b ("block: allow partitions on host aware zone devices")
Cc: stable@vger.kernel.org # 5.5
Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by Shin'ichiro Kawasaki and committed by Jens Axboe b53df2e7 cc3200ea

Changed files
+37 -12
block
include
linux
+36
block/genhd.c
··· 301 301 } 302 302 EXPORT_SYMBOL_GPL(disk_map_sector_rcu); 303 303 304 + /** 305 + * disk_has_partitions 306 + * @disk: gendisk of interest 307 + * 308 + * Walk through the partition table and check if valid partition exists. 309 + * 310 + * CONTEXT: 311 + * Don't care. 312 + * 313 + * RETURNS: 314 + * True if the gendisk has at least one valid non-zero size partition. 315 + * Otherwise false. 316 + */ 317 + bool disk_has_partitions(struct gendisk *disk) 318 + { 319 + struct disk_part_tbl *ptbl; 320 + int i; 321 + bool ret = false; 322 + 323 + rcu_read_lock(); 324 + ptbl = rcu_dereference(disk->part_tbl); 325 + 326 + /* Iterate partitions skipping the whole device at index 0 */ 327 + for (i = 1; i < ptbl->len; i++) { 328 + if (rcu_dereference(ptbl->part[i])) { 329 + ret = true; 330 + break; 331 + } 332 + } 333 + 334 + rcu_read_unlock(); 335 + 336 + return ret; 337 + } 338 + EXPORT_SYMBOL_GPL(disk_has_partitions); 339 + 304 340 /* 305 341 * Can be deleted altogether. Later. 306 342 *
+1 -12
include/linux/genhd.h
··· 245 245 !(disk->flags & GENHD_FL_NO_PART_SCAN); 246 246 } 247 247 248 - static inline bool disk_has_partitions(struct gendisk *disk) 249 - { 250 - bool ret = false; 251 - 252 - rcu_read_lock(); 253 - if (rcu_dereference(disk->part_tbl)->len > 1) 254 - ret = true; 255 - rcu_read_unlock(); 256 - 257 - return ret; 258 - } 259 - 260 248 static inline dev_t disk_devt(struct gendisk *disk) 261 249 { 262 250 return MKDEV(disk->major, disk->first_minor); ··· 286 298 287 299 extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, 288 300 sector_t sector); 301 + bool disk_has_partitions(struct gendisk *disk); 289 302 290 303 /* 291 304 * Macros to operate on percpu disk statistics: