From 7f9d35d24c56576a8c18a8b6f74711c7f3c9c6c0 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 12 Oct 2018 19:08:40 +0900 Subject: scsi: sd_zbc: Rearrange code Move the urswrz check out of sd_zbc_read_zones() and into sd_zbc_read_zoned_characteristics() where that value is obtained (read from the disk zoned characteristics VPD page). Since this function now does more than simply reading the VPD page, rename it to sd_zbc_check_zoned_characteristics(). Also fix the error message displayed when reading that VPD page fails. Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Acked-by: Martin K. Petersen Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/scsi/sd_zbc.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 412c1787dcd9..0b7d8787f785 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -305,19 +305,19 @@ void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, } /** - * sd_zbc_read_zoned_characteristics - Read zoned block device characteristics + * sd_zbc_check_zoned_characteristics - Check zoned block device characteristics * @sdkp: Target disk * @buf: Buffer where to store the VPD page data * - * Read VPD page B6. + * Read VPD page B6, get information and check that reads are unconstrained. */ -static int sd_zbc_read_zoned_characteristics(struct scsi_disk *sdkp, - unsigned char *buf) +static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp, + unsigned char *buf) { if (scsi_get_vpd_page(sdkp->device, 0xb6, buf, 64)) { sd_printk(KERN_NOTICE, sdkp, - "Unconstrained-read check failed\n"); + "Read zoned characteristics VPD page failed\n"); return -ENODEV; } @@ -335,6 +335,18 @@ static int sd_zbc_read_zoned_characteristics(struct scsi_disk *sdkp, sdkp->zones_max_open = get_unaligned_be32(&buf[16]); } + /* + * Check for unconstrained reads: host-managed devices with + * constrained reads (drives failing read after write pointer) + * are not supported. + */ + if (!sdkp->urswrz) { + if (sdkp->first_scan) + sd_printk(KERN_NOTICE, sdkp, + "constrained reads devices are not supported\n"); + return -ENODEV; + } + return 0; } @@ -675,24 +687,11 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) */ return 0; - /* Get zoned block device characteristics */ - ret = sd_zbc_read_zoned_characteristics(sdkp, buf); + /* Check zoned block device characteristics (unconstrained reads) */ + ret = sd_zbc_check_zoned_characteristics(sdkp, buf); if (ret) goto err; - /* - * Check for unconstrained reads: host-managed devices with - * constrained reads (drives failing read after write pointer) - * are not supported. - */ - if (!sdkp->urswrz) { - if (sdkp->first_scan) - sd_printk(KERN_NOTICE, sdkp, - "constrained reads devices are not supported\n"); - ret = -ENODEV; - goto err; - } - /* Check capacity */ ret = sd_zbc_check_capacity(sdkp, buf); if (ret) -- cgit v1.2.1 From d2e428e49eec9b7ff175d7d5941d50e5a7d15324 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 12 Oct 2018 19:08:41 +0900 Subject: scsi: sd_zbc: Reduce boot device scan and revalidate time Handling checks of ZBC device capacity using the max_lba field of the REPORT ZONES command reply for disks with rc_basis == 0 can be done using the same report zones command reply used to check the "same" field. Avoid executing a report zones command solely to check the disk capacity by merging sd_zbc_check_capacity() into sd_zbc_check_zone_size() and renaming that function to sd_zbc_check_zones(). This removes a costly execution of a full report zones command and so reduces device scan duration at boot time as well as the duration of disk revalidate calls. Furthermore, setting the partial report bit in the REPORT ZONES command cdb can significantly reduce this command execution time as the device does not have to count and report the total number of zones that could be reported assuming a large enough reply buffer. A non-partial zone report is necessary only for the first execution of report zones used to check the same field value (to ensure that this value applies to all zones of the disk). All other calls to sd_zbc_report_zones() can use a partial report to reduce execution time. Using a 14 TB ZBC disk, these simple changes reduce device scan time at boot from about 3.5s down to about 900ms. Disk revalidate times are also reduced from about 450ms down to 230ms. Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Acked-by: Martin K. Petersen Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/scsi/sd_zbc.c | 94 ++++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 54 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 0b7d8787f785..ca73c46931c0 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -67,11 +67,17 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, * @buf: Buffer to use for the reply * @buflen: the buffer size * @lba: Start LBA of the report + * @partial: Do partial report * * For internal use during device validation. + * Using partial=true can significantly speed up execution of a report zones + * command because the disk does not have to count all possible report matching + * zones and will only report the count of zones fitting in the command reply + * buffer. */ static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf, - unsigned int buflen, sector_t lba) + unsigned int buflen, sector_t lba, + bool partial) { struct scsi_device *sdp = sdkp->device; const int timeout = sdp->request_queue->rq_timeout; @@ -85,6 +91,8 @@ static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf, cmd[1] = ZI_REPORT_ZONES; put_unaligned_be64(lba, &cmd[2]); put_unaligned_be32(buflen, &cmd[10]); + if (partial) + cmd[14] = ZBC_REPORT_ZONE_PARTIAL; memset(buf, 0, buflen); result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, @@ -350,60 +358,25 @@ static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp, return 0; } -/** - * sd_zbc_check_capacity - Check reported capacity. - * @sdkp: Target disk - * @buf: Buffer to use for commands - * - * ZBC drive may report only the capacity of the first conventional zones at - * LBA 0. This is indicated by the RC_BASIS field of the read capacity reply. - * Check this here. If the disk reported only its conventional zones capacity, - * get the total capacity by doing a report zones. - */ -static int sd_zbc_check_capacity(struct scsi_disk *sdkp, unsigned char *buf) -{ - sector_t lba; - int ret; - - if (sdkp->rc_basis != 0) - return 0; - - /* Do a report zone to get the maximum LBA to check capacity */ - ret = sd_zbc_report_zones(sdkp, buf, SD_BUF_SIZE, 0); - if (ret) - return ret; - - /* The max_lba field is the capacity of this device */ - lba = get_unaligned_be64(&buf[8]); - if (lba + 1 == sdkp->capacity) - return 0; - - if (sdkp->first_scan) - sd_printk(KERN_WARNING, sdkp, - "Changing capacity from %llu to max LBA+1 %llu\n", - (unsigned long long)sdkp->capacity, - (unsigned long long)lba + 1); - sdkp->capacity = lba + 1; - - return 0; -} - #define SD_ZBC_BUF_SIZE 131072U /** - * sd_zbc_check_zone_size - Check the device zone sizes + * sd_zbc_check_zones - Check the device capacity and zone sizes * @sdkp: Target disk * - * Check that all zones of the device are equal. The last zone can however - * be smaller. The zone size must also be a power of two number of LBAs. + * Check that the device capacity as reported by READ CAPACITY matches the + * max_lba value (plus one)of the report zones command reply. Also check that + * all zones of the device have an equal size, only allowing the last zone of + * the disk to have a smaller size (runt zone). The zone size must also be a + * power of two. * * Returns the zone size in number of blocks upon success or an error code * upon failure. */ -static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp) +static s64 sd_zbc_check_zones(struct scsi_disk *sdkp) { u64 zone_blocks = 0; - sector_t block = 0; + sector_t max_lba, block = 0; unsigned char *buf; unsigned char *rec; unsigned int buf_len; @@ -416,11 +389,28 @@ static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp) if (!buf) return -ENOMEM; - /* Do a report zone to get the same field */ - ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0); + /* Do a report zone to get max_lba and the same field */ + ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false); if (ret) goto out_free; + if (sdkp->rc_basis == 0) { + /* The max_lba field is the capacity of this device */ + max_lba = get_unaligned_be64(&buf[8]); + if (sdkp->capacity != max_lba + 1) { + if (sdkp->first_scan) + sd_printk(KERN_WARNING, sdkp, + "Changing capacity from %llu to max LBA+1 %llu\n", + (unsigned long long)sdkp->capacity, + (unsigned long long)max_lba + 1); + sdkp->capacity = max_lba + 1; + } + } + + /* + * Check same field: for any value other than 0, we know that all zones + * have the same size. + */ same = buf[4] & 0x0f; if (same > 0) { rec = &buf[64]; @@ -458,7 +448,7 @@ static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp) if (block < sdkp->capacity) { ret = sd_zbc_report_zones(sdkp, buf, - SD_ZBC_BUF_SIZE, block); + SD_ZBC_BUF_SIZE, block, true); if (ret) goto out_free; } @@ -574,7 +564,8 @@ sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp, u32 zone_shift, goto out; while (lba < sdkp->capacity) { - ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, lba); + ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, + lba, true); if (ret) goto out; lba = sd_zbc_get_seq_zones(sdkp, buf, SD_ZBC_BUF_SIZE, @@ -692,16 +683,11 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) if (ret) goto err; - /* Check capacity */ - ret = sd_zbc_check_capacity(sdkp, buf); - if (ret) - goto err; - /* * Check zone size: only devices with a constant zone size (except * an eventual last runt zone) that is a power of 2 are supported. */ - zone_blocks = sd_zbc_check_zone_size(sdkp); + zone_blocks = sd_zbc_check_zones(sdkp); ret = -EFBIG; if (zone_blocks != (u32)zone_blocks) goto err; -- cgit v1.2.1 From 5f832a395859de7191e638e3777ae57f37d46d08 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 12 Oct 2018 19:08:42 +0900 Subject: scsi: sd_zbc: Fix sd_zbc_check_zones() error checks The unsigned 32 bits overflow check for the zone size value is already done within sd_zbc_check_zones() with the test: } else if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) { so there is no need to check again for an out of range value in sd_zbc_read_zones(). Simplify the code and fix sd_zbc_check_zones() error return to -EFBIG instead of -ENODEV if the zone size is too large. Change the return type of sd_zbc_check_zones() to an int for the error code and return the zone size (zone_blocks) through a u32 pointer to avoid overflowing the signed 32 return value. Reviewed-by: Hannes Reinecke Acked-by: Martin K. Petersen Reviewed-by: Christoph Hellwig Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/scsi/sd_zbc.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index ca73c46931c0..0678e1e108b0 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -373,7 +373,7 @@ static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp, * Returns the zone size in number of blocks upon success or an error code * upon failure. */ -static s64 sd_zbc_check_zones(struct scsi_disk *sdkp) +static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks) { u64 zone_blocks = 0; sector_t max_lba, block = 0; @@ -381,7 +381,7 @@ static s64 sd_zbc_check_zones(struct scsi_disk *sdkp) unsigned char *rec; unsigned int buf_len; unsigned int list_length; - s64 ret; + int ret; u8 same; /* Get a buffer */ @@ -472,9 +472,10 @@ out: if (sdkp->first_scan) sd_printk(KERN_NOTICE, sdkp, "Zone size too large\n"); - ret = -ENODEV; + ret = -EFBIG; } else { - ret = zone_blocks; + *zblocks = zone_blocks; + ret = 0; } out_free: @@ -668,7 +669,7 @@ err: int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) { - int64_t zone_blocks; + u32 zone_blocks; int ret; if (!sd_is_zoned(sdkp)) @@ -687,12 +688,8 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) * Check zone size: only devices with a constant zone size (except * an eventual last runt zone) that is a power of 2 are supported. */ - zone_blocks = sd_zbc_check_zones(sdkp); - ret = -EFBIG; - if (zone_blocks != (u32)zone_blocks) - goto err; - ret = zone_blocks; - if (ret < 0) + ret = sd_zbc_check_zones(sdkp, &zone_blocks); + if (ret != 0) goto err; /* The drive satisfies the kernel restrictions: set it up */ -- cgit v1.2.1 From e76239a3748c90a8b0e197f8f4544a8ce52f126e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 12 Oct 2018 19:08:49 +0900 Subject: block: add a report_zones method Dispatching a report zones command through the request queue is a major pain due to the command reply payload rewriting necessary. Given that blkdev_report_zones() is executing everything synchronously, implement report zones as a block device file operation instead, allowing major simplification of the code in many places. sd, null-blk, dm-linear and dm-flakey being the only block device drivers supporting exposing zoned block devices, these drivers are modified to provide the device side implementation of the report_zones() block device file operation. For device mappers, a new report_zones() target type operation is defined so that the upper block layer calls blkdev_report_zones() can be propagated down to the underlying devices of the dm targets. Implementation for this new operation is added to the dm-linear and dm-flakey targets. Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig [Damien] * Changed method block_device argument to gendisk * Various bug fixes and improvements * Added support for null_blk, dm-linear and dm-flakey. Reviewed-by: Martin K. Petersen Reviewed-by: Mike Snitzer Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/scsi/sd.c | 13 +---- drivers/scsi/sd.h | 11 ++-- drivers/scsi/sd_zbc.c | 153 ++++++++++++++++---------------------------------- 3 files changed, 53 insertions(+), 124 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index b762d0fd773c..42c0f299021d 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1272,8 +1272,6 @@ static int sd_init_command(struct scsi_cmnd *cmd) case REQ_OP_READ: case REQ_OP_WRITE: return sd_setup_read_write_cmnd(cmd); - case REQ_OP_ZONE_REPORT: - return sd_zbc_setup_report_cmnd(cmd); case REQ_OP_ZONE_RESET: return sd_zbc_setup_reset_cmnd(cmd); default: @@ -1802,6 +1800,7 @@ static const struct block_device_operations sd_fops = { .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, + .report_zones = sd_zbc_report_zones, .pr_ops = &sd_pr_ops, }; @@ -1953,16 +1952,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) scsi_set_resid(SCpnt, blk_rq_bytes(req)); } break; - case REQ_OP_ZONE_REPORT: - if (!result) { - good_bytes = scsi_bufflen(SCpnt) - - scsi_get_resid(SCpnt); - scsi_set_resid(SCpnt, 0); - } else { - good_bytes = 0; - scsi_set_resid(SCpnt, blk_rq_bytes(req)); - } - break; default: /* * In case of bogus fw or device, we could end up having diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index a7d4f50b67d4..f72f20fd0d8b 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -273,10 +273,12 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp) extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer); extern void sd_zbc_remove(struct scsi_disk *sdkp); extern void sd_zbc_print_zones(struct scsi_disk *sdkp); -extern int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd); extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd); extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, struct scsi_sense_hdr *sshdr); +extern int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, + struct blk_zone *zones, unsigned int *nr_zones, + gfp_t gfp_mask); #else /* CONFIG_BLK_DEV_ZONED */ @@ -290,11 +292,6 @@ static inline void sd_zbc_remove(struct scsi_disk *sdkp) {} static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {} -static inline int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) -{ - return BLKPREP_INVALID; -} - static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) { return BLKPREP_INVALID; @@ -304,6 +301,8 @@ static inline void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, struct scsi_sense_hdr *sshdr) {} +#define sd_zbc_report_zones NULL + #endif /* CONFIG_BLK_DEV_ZONED */ #endif /* _SCSI_DISK_H */ diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 0678e1e108b0..0f2cfc81fce3 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -62,7 +62,7 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, } /** - * sd_zbc_report_zones - Issue a REPORT ZONES scsi command. + * sd_zbc_do_report_zones - Issue a REPORT ZONES scsi command. * @sdkp: The target disk * @buf: Buffer to use for the reply * @buflen: the buffer size @@ -75,9 +75,9 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, * zones and will only report the count of zones fitting in the command reply * buffer. */ -static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf, - unsigned int buflen, sector_t lba, - bool partial) +static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf, + unsigned int buflen, sector_t lba, + bool partial) { struct scsi_device *sdp = sdkp->device; const int timeout = sdp->request_queue->rq_timeout; @@ -118,108 +118,56 @@ static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf, } /** - * sd_zbc_setup_report_cmnd - Prepare a REPORT ZONES scsi command - * @cmd: The command to setup + * sd_zbc_report_zones - Disk report zones operation. + * @disk: The target disk + * @sector: Start 512B sector of the report + * @zones: Array of zone descriptors + * @nr_zones: Number of descriptors in the array + * @gfp_mask: Memory allocation mask * - * Call in sd_init_command() for a REQ_OP_ZONE_REPORT request. + * Execute a report zones command on the target disk. */ -int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) +int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, + struct blk_zone *zones, unsigned int *nr_zones, + gfp_t gfp_mask) { - struct request *rq = cmd->request; - struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); - sector_t lba, sector = blk_rq_pos(rq); - unsigned int nr_bytes = blk_rq_bytes(rq); - int ret; - - WARN_ON(nr_bytes == 0); + struct scsi_disk *sdkp = scsi_disk(disk); + unsigned int i, buflen, nrz = *nr_zones; + unsigned char *buf; + size_t offset = 0; + int ret = 0; if (!sd_is_zoned(sdkp)) /* Not a zoned device */ - return BLKPREP_KILL; - - ret = scsi_init_io(cmd); - if (ret != BLKPREP_OK) - return ret; - - cmd->cmd_len = 16; - memset(cmd->cmnd, 0, cmd->cmd_len); - cmd->cmnd[0] = ZBC_IN; - cmd->cmnd[1] = ZI_REPORT_ZONES; - lba = sectors_to_logical(sdkp->device, sector); - put_unaligned_be64(lba, &cmd->cmnd[2]); - put_unaligned_be32(nr_bytes, &cmd->cmnd[10]); - /* Do partial report for speeding things up */ - cmd->cmnd[14] = ZBC_REPORT_ZONE_PARTIAL; - - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->sdb.length = nr_bytes; - cmd->transfersize = sdkp->device->sector_size; - cmd->allowed = 0; + return -EOPNOTSUPP; - return BLKPREP_OK; -} - -/** - * sd_zbc_report_zones_complete - Process a REPORT ZONES scsi command reply. - * @scmd: The completed report zones command - * @good_bytes: reply size in bytes - * - * Convert all reported zone descriptors to struct blk_zone. The conversion - * is done in-place, directly in the request specified sg buffer. - */ -static void sd_zbc_report_zones_complete(struct scsi_cmnd *scmd, - unsigned int good_bytes) -{ - struct request *rq = scmd->request; - struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); - struct sg_mapping_iter miter; - struct blk_zone_report_hdr hdr; - struct blk_zone zone; - unsigned int offset, bytes = 0; - unsigned long flags; - u8 *buf; - - if (good_bytes < 64) - return; - - memset(&hdr, 0, sizeof(struct blk_zone_report_hdr)); - - sg_miter_start(&miter, scsi_sglist(scmd), scsi_sg_count(scmd), - SG_MITER_TO_SG | SG_MITER_ATOMIC); + /* + * Get a reply buffer for the number of requested zones plus a header. + * For ATA, buffers must be aligned to 512B. + */ + buflen = roundup((nrz + 1) * 64, 512); + buf = kmalloc(buflen, gfp_mask); + if (!buf) + return -ENOMEM; - local_irq_save(flags); - while (sg_miter_next(&miter) && bytes < good_bytes) { + ret = sd_zbc_do_report_zones(sdkp, buf, buflen, + sectors_to_logical(sdkp->device, sector), true); + if (ret) + goto out_free_buf; - buf = miter.addr; - offset = 0; + nrz = min(nrz, get_unaligned_be32(&buf[0]) / 64); + for (i = 0; i < nrz; i++) { + offset += 64; + sd_zbc_parse_report(sdkp, buf + offset, zones); + zones++; + } - if (bytes == 0) { - /* Set the report header */ - hdr.nr_zones = min_t(unsigned int, - (good_bytes - 64) / 64, - get_unaligned_be32(&buf[0]) / 64); - memcpy(buf, &hdr, sizeof(struct blk_zone_report_hdr)); - offset += 64; - bytes += 64; - } + *nr_zones = nrz; - /* Parse zone descriptors */ - while (offset < miter.length && hdr.nr_zones) { - WARN_ON(offset > miter.length); - buf = miter.addr + offset; - sd_zbc_parse_report(sdkp, buf, &zone); - memcpy(buf, &zone, sizeof(struct blk_zone)); - offset += 64; - bytes += 64; - hdr.nr_zones--; - } - - if (!hdr.nr_zones) - break; +out_free_buf: + kfree(buf); - } - sg_miter_stop(&miter); - local_irq_restore(flags); + return ret; } /** @@ -302,13 +250,6 @@ void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, case REQ_OP_WRITE_ZEROES: case REQ_OP_WRITE_SAME: break; - - case REQ_OP_ZONE_REPORT: - - if (!result) - sd_zbc_report_zones_complete(cmd, good_bytes); - break; - } } @@ -390,7 +331,7 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks) return -ENOMEM; /* Do a report zone to get max_lba and the same field */ - ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false); + ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false); if (ret) goto out_free; @@ -447,8 +388,8 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks) } if (block < sdkp->capacity) { - ret = sd_zbc_report_zones(sdkp, buf, - SD_ZBC_BUF_SIZE, block, true); + ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, + block, true); if (ret) goto out_free; } @@ -565,8 +506,8 @@ sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp, u32 zone_shift, goto out; while (lba < sdkp->capacity) { - ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, - lba, true); + ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, lba, + true); if (ret) goto out; lba = sd_zbc_get_seq_zones(sdkp, buf, SD_ZBC_BUF_SIZE, -- cgit v1.2.1 From bf5054569653c491ece544cc7ee333ae53b47121 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 12 Oct 2018 19:08:50 +0900 Subject: block: Introduce blk_revalidate_disk_zones() Drivers exposing zoned block devices have to initialize and maintain correctness (i.e. revalidate) of the device zone bitmaps attached to the device request queue (seq_zones_bitmap and seq_zones_wlock). To simplify coding this, introduce a generic helper function blk_revalidate_disk_zones() suitable for most (and likely all) cases. This new function always update the seq_zones_bitmap and seq_zones_wlock bitmaps as well as the queue nr_zones field when called for a disk using a request based queue. For a disk using a BIO based queue, only the number of zones is updated since these queues do not have schedulers and so do not need the zone bitmaps. With this change, the zone bitmap initialization code in sd_zbc.c can be replaced with a call to this function in sd_zbc_read_zones(), which is called from the disk revalidate block operation method. A call to blk_revalidate_disk_zones() is also added to the null_blk driver for devices created with the zoned mode enabled. Finally, to ensure that zoned devices created with dm-linear or dm-flakey expose the correct number of zones through sysfs, a call to blk_revalidate_disk_zones() is added to dm_table_set_restrictions(). The zone bitmaps allocated and initialized with blk_revalidate_disk_zones() are freed automatically from __blk_release_queue() using the block internal function blk_queue_free_zone_bitmaps(). Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Mike Snitzer Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/scsi/sd.c | 2 - drivers/scsi/sd.h | 4 - drivers/scsi/sd_zbc.c | 218 ++++++-------------------------------------------- 3 files changed, 26 insertions(+), 198 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 42c0f299021d..3bb2b3351e35 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3414,8 +3414,6 @@ static int sd_remove(struct device *dev) del_gendisk(sdkp->disk); sd_shutdown(dev); - sd_zbc_remove(sdkp); - free_opal_dev(sdkp->opal_dev); blk_register_region(devt, SD_MINORS, NULL, diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index f72f20fd0d8b..1d63f3a23ffb 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -76,7 +76,6 @@ struct scsi_disk { #ifdef CONFIG_BLK_DEV_ZONED u32 nr_zones; u32 zone_blocks; - u32 zone_shift; u32 zones_optimal_open; u32 zones_optimal_nonseq; u32 zones_max_open; @@ -271,7 +270,6 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp) #ifdef CONFIG_BLK_DEV_ZONED extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer); -extern void sd_zbc_remove(struct scsi_disk *sdkp); extern void sd_zbc_print_zones(struct scsi_disk *sdkp); extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd); extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, @@ -288,8 +286,6 @@ static inline int sd_zbc_read_zones(struct scsi_disk *sdkp, return 0; } -static inline void sd_zbc_remove(struct scsi_disk *sdkp) {} - static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {} static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 0f2cfc81fce3..e06c48c866e4 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -425,191 +425,10 @@ out_free: return ret; } -/** - * sd_zbc_alloc_zone_bitmap - Allocate a zone bitmap (one bit per zone). - * @nr_zones: Number of zones to allocate space for. - * @numa_node: NUMA node to allocate the memory from. - */ -static inline unsigned long * -sd_zbc_alloc_zone_bitmap(u32 nr_zones, int numa_node) -{ - return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long), - GFP_KERNEL, numa_node); -} - -/** - * sd_zbc_get_seq_zones - Parse report zones reply to identify sequential zones - * @sdkp: disk used - * @buf: report reply buffer - * @buflen: length of @buf - * @zone_shift: logarithm base 2 of the number of blocks in a zone - * @seq_zones_bitmap: bitmap of sequential zones to set - * - * Parse reported zone descriptors in @buf to identify sequential zones and - * set the reported zone bit in @seq_zones_bitmap accordingly. - * Since read-only and offline zones cannot be written, do not - * mark them as sequential in the bitmap. - * Return the LBA after the last zone reported. - */ -static sector_t sd_zbc_get_seq_zones(struct scsi_disk *sdkp, unsigned char *buf, - unsigned int buflen, u32 zone_shift, - unsigned long *seq_zones_bitmap) -{ - sector_t lba, next_lba = sdkp->capacity; - unsigned int buf_len, list_length; - unsigned char *rec; - u8 type, cond; - - list_length = get_unaligned_be32(&buf[0]) + 64; - buf_len = min(list_length, buflen); - rec = buf + 64; - - while (rec < buf + buf_len) { - type = rec[0] & 0x0f; - cond = (rec[1] >> 4) & 0xf; - lba = get_unaligned_be64(&rec[16]); - if (type != ZBC_ZONE_TYPE_CONV && - cond != ZBC_ZONE_COND_READONLY && - cond != ZBC_ZONE_COND_OFFLINE) - set_bit(lba >> zone_shift, seq_zones_bitmap); - next_lba = lba + get_unaligned_be64(&rec[8]); - rec += 64; - } - - return next_lba; -} - -/** - * sd_zbc_setup_seq_zones_bitmap - Initialize a seq zone bitmap. - * @sdkp: target disk - * @zone_shift: logarithm base 2 of the number of blocks in a zone - * @nr_zones: number of zones to set up a seq zone bitmap for - * - * Allocate a zone bitmap and initialize it by identifying sequential zones. - */ -static unsigned long * -sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp, u32 zone_shift, - u32 nr_zones) -{ - struct request_queue *q = sdkp->disk->queue; - unsigned long *seq_zones_bitmap; - sector_t lba = 0; - unsigned char *buf; - int ret = -ENOMEM; - - seq_zones_bitmap = sd_zbc_alloc_zone_bitmap(nr_zones, q->node); - if (!seq_zones_bitmap) - return ERR_PTR(-ENOMEM); - - buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL); - if (!buf) - goto out; - - while (lba < sdkp->capacity) { - ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, lba, - true); - if (ret) - goto out; - lba = sd_zbc_get_seq_zones(sdkp, buf, SD_ZBC_BUF_SIZE, - zone_shift, seq_zones_bitmap); - } - - if (lba != sdkp->capacity) { - /* Something went wrong */ - ret = -EIO; - } - -out: - kfree(buf); - if (ret) { - kfree(seq_zones_bitmap); - return ERR_PTR(ret); - } - return seq_zones_bitmap; -} - -static void sd_zbc_cleanup(struct scsi_disk *sdkp) -{ - struct request_queue *q = sdkp->disk->queue; - - kfree(q->seq_zones_bitmap); - q->seq_zones_bitmap = NULL; - - kfree(q->seq_zones_wlock); - q->seq_zones_wlock = NULL; - - q->nr_zones = 0; -} - -static int sd_zbc_setup(struct scsi_disk *sdkp, u32 zone_blocks) -{ - struct request_queue *q = sdkp->disk->queue; - u32 zone_shift = ilog2(zone_blocks); - u32 nr_zones; - int ret; - - /* chunk_sectors indicates the zone size */ - blk_queue_chunk_sectors(q, - logical_to_sectors(sdkp->device, zone_blocks)); - nr_zones = round_up(sdkp->capacity, zone_blocks) >> zone_shift; - - /* - * Initialize the device request queue information if the number - * of zones changed. - */ - if (nr_zones != sdkp->nr_zones || nr_zones != q->nr_zones) { - unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL; - size_t zone_bitmap_size; - - if (nr_zones) { - seq_zones_wlock = sd_zbc_alloc_zone_bitmap(nr_zones, - q->node); - if (!seq_zones_wlock) { - ret = -ENOMEM; - goto err; - } - - seq_zones_bitmap = sd_zbc_setup_seq_zones_bitmap(sdkp, - zone_shift, nr_zones); - if (IS_ERR(seq_zones_bitmap)) { - ret = PTR_ERR(seq_zones_bitmap); - kfree(seq_zones_wlock); - goto err; - } - } - zone_bitmap_size = BITS_TO_LONGS(nr_zones) * - sizeof(unsigned long); - blk_mq_freeze_queue(q); - if (q->nr_zones != nr_zones) { - /* READ16/WRITE16 is mandatory for ZBC disks */ - sdkp->device->use_16_for_rw = 1; - sdkp->device->use_10_for_rw = 0; - - sdkp->zone_blocks = zone_blocks; - sdkp->zone_shift = zone_shift; - sdkp->nr_zones = nr_zones; - q->nr_zones = nr_zones; - swap(q->seq_zones_wlock, seq_zones_wlock); - swap(q->seq_zones_bitmap, seq_zones_bitmap); - } else if (memcmp(q->seq_zones_bitmap, seq_zones_bitmap, - zone_bitmap_size) != 0) { - memcpy(q->seq_zones_bitmap, seq_zones_bitmap, - zone_bitmap_size); - } - blk_mq_unfreeze_queue(q); - kfree(seq_zones_wlock); - kfree(seq_zones_bitmap); - } - - return 0; - -err: - sd_zbc_cleanup(sdkp); - return ret; -} - int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) { + struct gendisk *disk = sdkp->disk; + unsigned int nr_zones; u32 zone_blocks; int ret; @@ -634,24 +453,39 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) goto err; /* The drive satisfies the kernel restrictions: set it up */ - ret = sd_zbc_setup(sdkp, zone_blocks); - if (ret) - goto err; + blk_queue_chunk_sectors(sdkp->disk->queue, + logical_to_sectors(sdkp->device, zone_blocks)); + nr_zones = round_up(sdkp->capacity, zone_blocks) >> ilog2(zone_blocks); + + /* READ16/WRITE16 is mandatory for ZBC disks */ + sdkp->device->use_16_for_rw = 1; + sdkp->device->use_10_for_rw = 0; + + /* + * If something changed, revalidate the disk zone bitmaps once we have + * the capacity, that is on the second revalidate execution during disk + * scan and always during normal revalidate. + */ + if (sdkp->first_scan) + return 0; + if (sdkp->zone_blocks != zone_blocks || + sdkp->nr_zones != nr_zones || + disk->queue->nr_zones != nr_zones) { + ret = blk_revalidate_disk_zones(disk); + if (ret != 0) + goto err; + sdkp->zone_blocks = zone_blocks; + sdkp->nr_zones = nr_zones; + } return 0; err: sdkp->capacity = 0; - sd_zbc_cleanup(sdkp); return ret; } -void sd_zbc_remove(struct scsi_disk *sdkp) -{ - sd_zbc_cleanup(sdkp); -} - void sd_zbc_print_zones(struct scsi_disk *sdkp) { if (!sd_is_zoned(sdkp) || !sdkp->capacity) -- cgit v1.2.1