block: allow partitions on host aware zone devices

Host-aware SMR drives can be used with the commands to explicitly manage
zone state, but they can also be used as normal disks. In the former
case it makes perfect sense to allow partitions on them, in the latter
it does not, just like for host managed devices. Add a check to
add_partition to allow partitions on host aware devices, but give
up any zone management capabilities in that case, which also catches
the previously missed case of adding a partition vs just scanning it.

Because sd can rescan the attribute at runtime it needs to check if
a disk has partitions, for which a new helper is added to genhd.h.

Fixes: 5eac3eb30c9a ("block: Remove partition support for zoned block devices")
Reported-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Tested-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by Christoph Hellwig and committed by Jens Axboe b7205307 ad6bf88a

+39 -8
+22 -4
block/partition-generic.c
··· 321 const char *dname; 322 int err; 323 324 err = disk_expand_part_tbl(disk, partno); 325 if (err) 326 return ERR_PTR(err); ··· 519 520 part = add_partition(disk, p, from, size, state->parts[p].flags, 521 &state->parts[p].info); 522 - if (IS_ERR(part)) { 523 printk(KERN_ERR " %s: p%d could not be added: %ld\n", 524 disk->disk_name, p, -PTR_ERR(part)); 525 return true; ··· 558 } 559 560 /* 561 - * Partitions are not supported on zoned block devices. 562 */ 563 - if (bdev_is_zoned(bdev)) { 564 - pr_warn("%s: ignoring partition table on zoned block device\n", 565 disk->disk_name); 566 ret = 0; 567 goto out_free_state;
··· 321 const char *dname; 322 int err; 323 324 + /* 325 + * Partitions are not supported on zoned block devices that are used as 326 + * such. 327 + */ 328 + switch (disk->queue->limits.zoned) { 329 + case BLK_ZONED_HM: 330 + pr_warn("%s: partitions not supported on host managed zoned block device\n", 331 + disk->disk_name); 332 + return ERR_PTR(-ENXIO); 333 + case BLK_ZONED_HA: 334 + pr_info("%s: disabling host aware zoned block device support due to partitions\n", 335 + disk->disk_name); 336 + disk->queue->limits.zoned = BLK_ZONED_NONE; 337 + break; 338 + case BLK_ZONED_NONE: 339 + break; 340 + } 341 + 342 err = disk_expand_part_tbl(disk, partno); 343 if (err) 344 return ERR_PTR(err); ··· 501 502 part = add_partition(disk, p, from, size, state->parts[p].flags, 503 &state->parts[p].info); 504 + if (IS_ERR(part) && PTR_ERR(part) != -ENXIO) { 505 printk(KERN_ERR " %s: p%d could not be added: %ld\n", 506 disk->disk_name, p, -PTR_ERR(part)); 507 return true; ··· 540 } 541 542 /* 543 + * Partitions are not supported on host managed zoned block devices. 544 */ 545 + if (disk->queue->limits.zoned == BLK_ZONED_HM) { 546 + pr_warn("%s: ignoring partition table on host managed zoned block device\n", 547 disk->disk_name); 548 ret = 0; 549 goto out_free_state;
+5 -4
drivers/scsi/sd.c
··· 2956 q->limits.zoned = BLK_ZONED_HM; 2957 } else { 2958 sdkp->zoned = (buffer[8] >> 4) & 3; 2959 - if (sdkp->zoned == 1) 2960 /* Host-aware */ 2961 q->limits.zoned = BLK_ZONED_HA; 2962 - else 2963 /* 2964 - * Treat drive-managed devices as 2965 - * regular block devices. 2966 */ 2967 q->limits.zoned = BLK_ZONED_NONE; 2968 } 2969 if (blk_queue_is_zoned(q) && sdkp->first_scan) 2970 sd_printk(KERN_NOTICE, sdkp, "Host-%s zoned block device\n",
··· 2956 q->limits.zoned = BLK_ZONED_HM; 2957 } else { 2958 sdkp->zoned = (buffer[8] >> 4) & 3; 2959 + if (sdkp->zoned == 1 && !disk_has_partitions(sdkp->disk)) { 2960 /* Host-aware */ 2961 q->limits.zoned = BLK_ZONED_HA; 2962 + } else { 2963 /* 2964 + * Treat drive-managed devices and host-aware devices 2965 + * with partitions as regular block devices. 2966 */ 2967 q->limits.zoned = BLK_ZONED_NONE; 2968 + } 2969 } 2970 if (blk_queue_is_zoned(q) && sdkp->first_scan) 2971 sd_printk(KERN_NOTICE, sdkp, "Host-%s zoned block device\n",
+12
include/linux/genhd.h
··· 245 !(disk->flags & GENHD_FL_NO_PART_SCAN); 246 } 247 248 static inline dev_t disk_devt(struct gendisk *disk) 249 { 250 return MKDEV(disk->major, disk->first_minor);
··· 245 !(disk->flags & GENHD_FL_NO_PART_SCAN); 246 } 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 static inline dev_t disk_devt(struct gendisk *disk) 261 { 262 return MKDEV(disk->major, disk->first_minor);