sd_zbc_revalidate_zones() skips revalidating the zones of a ZBC device if the zone size and total number of zones of the disk has not changed. This is to avoid the sometimes slow report zones for the entire disk that blk_revalidate_disk_zones() will generate. For ZBC devices that support the storage element depopulation feature, a revalidation after executing a passthrough REMOVE ELEMENT AND TRUNCATE command will thus correctly trigger a zone revalidation as the disk capacity and number of zones changed. However, for devices supporting the data preserving head depopulation variant of the depopulation feature (REMOVE ELEMENT AND MODIFY ZONES command), a disk capacity and number of zones does not change after a head is depopulated. Only the conditions of the zones served by the head that was depopulated change. These zones are transitioned to either read-only or offline. While the block layer storage element management functions always call blk_revalidate_disk_zones() to catch these changes, a user scan, libata device rescan after EH or a depopulation or restoration action executed through SG_IO would not. In these cases, not calling blk_revalidate_disk_zones() prevents the block layer from taking appropriate actions on the zone write plugs of the disk. Avoid any issue with the block layer view of the zone conditions by not skipping the call to blk_revalidate_disk_zones() for disks that support both the GET PHYSICAL ELEMENTS STATUS and REMOVE ELEMENT AND MODIFY ZONES commands. This check is done from sd_zbc_read_zones() using the helper function sd_zbc_check_depop_supported(). The new SCSI disk flags depop_report_supported and depop_modify_zones_supported are defined to remember the result of this check. This change will slow down a little SMR disk revalidation. However, user forced revalidations are normally rare, and other revalidations triggered by udev only happen when the disk device files is not in use anymore (closed). So this slow down should in practice not be an issue. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig --- drivers/scsi/sd.h | 2 ++ drivers/scsi/sd_zbc.c | 47 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 574af8243016..84c87d0f05d8 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -156,6 +156,8 @@ struct scsi_disk { unsigned ignore_medium_access_errors : 1; unsigned rscs : 1; /* reduced stream control support */ unsigned use_atomic_write_boundary : 1; + unsigned depop_report_supported : 1; + unsigned depop_modify_zones_supported : 1; }; #define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev) diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 8cbf171e6fab..bf2e60071d57 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -516,6 +516,33 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp, unsigned char *buf, return 0; } +/* + * sd_zbc_check_depop_supported - Check if the device supports storage element + * depopulation + * @sdkp: Target SCSI disk + * @buf: command buffer + * + * Check if the device supports the REMOVE ELEMENT AND MODIFY ZONES command. + */ +static void sd_zbc_check_depop_supported(struct scsi_disk *sdkp, + unsigned char *buf) +{ + /* Mandate support for reporting storage elements health. */ + sdkp->depop_report_supported = + scsi_report_opcode(sdkp->device, buf, SD_BUF_SIZE, + SERVICE_ACTION_IN_16, + SAI_GET_PHYSICAL_ELEMENT_STATUS) == 1; + if (!sdkp->depop_report_supported) { + sdkp->depop_modify_zones_supported = false; + return; + } + + sdkp->depop_modify_zones_supported = + scsi_report_opcode(sdkp->device, buf, SD_BUF_SIZE, + SERVICE_ACTION_IN_16, + SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES) == 1; +} + static void sd_zbc_print_zones(struct scsi_disk *sdkp) { if (sdkp->device->type != TYPE_ZBC || !sdkp->capacity) @@ -545,6 +572,7 @@ int sd_zbc_revalidate_zones(struct scsi_disk *sdkp) u32 zone_blocks = sdkp->early_zone_info.zone_blocks; unsigned int nr_zones = sdkp->early_zone_info.nr_zones; unsigned int flags; + bool print_zones; int ret; /* @@ -554,11 +582,22 @@ int sd_zbc_revalidate_zones(struct scsi_disk *sdkp) if (!blk_queue_is_zoned(q)) return 0; + /* + * If the zone size and number of zones has not changed, and the disk + * does not support depopulating heads, skip the rather slow call to + * blk_revalidate_disk_zones(). + */ if (sdkp->zone_info.zone_blocks == zone_blocks && sdkp->zone_info.nr_zones == nr_zones && - disk->nr_zones == nr_zones) + disk->nr_zones == nr_zones && + !sdkp->depop_modify_zones_supported) return 0; + print_zones = + sdkp->zone_info.zone_blocks != zone_blocks || + sdkp->zone_info.nr_zones != nr_zones || + disk->nr_zones != nr_zones; + sdkp->zone_info.zone_blocks = zone_blocks; sdkp->zone_info.nr_zones = nr_zones; @@ -571,7 +610,8 @@ int sd_zbc_revalidate_zones(struct scsi_disk *sdkp) return ret; } - sd_zbc_print_zones(sdkp); + if (print_zones) + sd_zbc_print_zones(sdkp); return 0; } @@ -610,6 +650,9 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, struct queue_limits *lim, sdkp->device->use_10_for_rw = 0; sdkp->device->use_16_for_sync = 1; + /* Check if storage element depopulation is supported. */ + sd_zbc_check_depop_supported(sdkp, buf); + /* Check zoned block device characteristics (unconstrained reads) */ ret = sd_zbc_check_zoned_characteristics(sdkp, buf); if (ret) -- 2.56.0