diff options
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/sd.h | 4 | ||||
-rw-r--r-- | drivers/scsi/sd_zbc.c | 235 |
2 files changed, 83 insertions, 156 deletions
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index bf2102a749bc..42fd3f00e4a5 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -213,8 +213,8 @@ blk_status_t sd_zbc_setup_zone_mgmt_cmnd(struct scsi_cmnd *cmd, unsigned char op, bool all); 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); +int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, + unsigned int nr_zones, report_zones_cb cb, void *data); #else /* CONFIG_BLK_DEV_ZONED */ diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 39f10ec0dfcf..0e5ede48f045 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -19,34 +19,27 @@ #include "sd.h" -/** - * sd_zbc_parse_report - Convert a zone descriptor to a struct blk_zone, - * @sdkp: The disk the report originated from - * @buf: Address of the report zone descriptor - * @zone: the destination zone structure - * - * All LBA sized values are converted to 512B sectors unit. - */ -static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, - struct blk_zone *zone) +static int sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf, + unsigned int idx, report_zones_cb cb, void *data) { struct scsi_device *sdp = sdkp->device; + struct blk_zone zone = { 0 }; - memset(zone, 0, sizeof(struct blk_zone)); - - zone->type = buf[0] & 0x0f; - zone->cond = (buf[1] >> 4) & 0xf; + zone.type = buf[0] & 0x0f; + zone.cond = (buf[1] >> 4) & 0xf; if (buf[1] & 0x01) - zone->reset = 1; + zone.reset = 1; if (buf[1] & 0x02) - zone->non_seq = 1; - - zone->len = logical_to_sectors(sdp, get_unaligned_be64(&buf[8])); - zone->start = logical_to_sectors(sdp, get_unaligned_be64(&buf[16])); - zone->wp = logical_to_sectors(sdp, get_unaligned_be64(&buf[24])); - if (zone->type != ZBC_ZONE_TYPE_CONV && - zone->cond == ZBC_ZONE_COND_FULL) - zone->wp = zone->start + zone->len; + zone.non_seq = 1; + + zone.len = logical_to_sectors(sdp, get_unaligned_be64(&buf[8])); + zone.start = logical_to_sectors(sdp, get_unaligned_be64(&buf[16])); + zone.wp = logical_to_sectors(sdp, get_unaligned_be64(&buf[24])); + if (zone.type != ZBC_ZONE_TYPE_CONV && + zone.cond == ZBC_ZONE_COND_FULL) + zone.wp = zone.start + zone.len; + + return cb(&zone, idx, data); } /** @@ -104,11 +97,6 @@ static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf, return 0; } -/* - * Maximum number of zones to get with one report zones command. - */ -#define SD_ZBC_REPORT_MAX_ZONES 8192U - /** * Allocate a buffer for report zones reply. * @sdkp: The target disk @@ -138,75 +126,83 @@ static void *sd_zbc_alloc_report_buffer(struct scsi_disk *sdkp, * sure that the allocated buffer can always be mapped by limiting the * number of pages allocated to the HBA max segments limit. */ - nr_zones = min(nr_zones, SD_ZBC_REPORT_MAX_ZONES); - bufsize = roundup((nr_zones + 1) * 64, 512); + nr_zones = min(nr_zones, sdkp->nr_zones); + bufsize = roundup((nr_zones + 1) * 64, SECTOR_SIZE); bufsize = min_t(size_t, bufsize, queue_max_hw_sectors(q) << SECTOR_SHIFT); bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT); - buf = vzalloc(bufsize); - if (buf) - *buflen = bufsize; + while (bufsize >= SECTOR_SIZE) { + buf = __vmalloc(bufsize, + GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY, + PAGE_KERNEL); + if (buf) { + *buflen = bufsize; + return buf; + } + bufsize >>= 1; + } - return buf; + return NULL; } /** - * 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 - * - * Execute a report zones command on the target disk. + * sd_zbc_zone_sectors - Get the device zone size in number of 512B sectors. + * @sdkp: The target disk */ +static inline sector_t sd_zbc_zone_sectors(struct scsi_disk *sdkp) +{ + return logical_to_sectors(sdkp->device, sdkp->zone_blocks); +} + int sd_zbc_report_zones(struct gendisk *disk, sector_t sector, - struct blk_zone *zones, unsigned int *nr_zones) + unsigned int nr_zones, report_zones_cb cb, void *data) { struct scsi_disk *sdkp = scsi_disk(disk); - unsigned int i, nrz = *nr_zones; + unsigned int nr, i; unsigned char *buf; - size_t buflen = 0, offset = 0; - int ret = 0; + size_t offset, buflen = 0; + int zone_idx = 0; + int ret; if (!sd_is_zoned(sdkp)) /* Not a zoned device */ return -EOPNOTSUPP; - buf = sd_zbc_alloc_report_buffer(sdkp, nrz, &buflen); + buf = sd_zbc_alloc_report_buffer(sdkp, nr_zones, &buflen); if (!buf) return -ENOMEM; - ret = sd_zbc_do_report_zones(sdkp, buf, buflen, - sectors_to_logical(sdkp->device, sector), true); - if (ret) - goto out; + while (zone_idx < nr_zones && sector < get_capacity(disk)) { + ret = sd_zbc_do_report_zones(sdkp, buf, buflen, + sectors_to_logical(sdkp->device, sector), true); + if (ret) + goto out; + + offset = 0; + nr = min(nr_zones, get_unaligned_be32(&buf[0]) / 64); + if (!nr) + break; + + for (i = 0; i < nr && zone_idx < nr_zones; i++) { + offset += 64; + ret = sd_zbc_parse_report(sdkp, buf + offset, zone_idx, + cb, data); + if (ret) + goto out; + zone_idx++; + } - 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++; + sector += sd_zbc_zone_sectors(sdkp) * i; } - *nr_zones = nrz; - + ret = zone_idx; out: kvfree(buf); - return ret; } /** - * sd_zbc_zone_sectors - Get the device zone size in number of 512B sectors. - * @sdkp: The target disk - */ -static inline sector_t sd_zbc_zone_sectors(struct scsi_disk *sdkp) -{ - return logical_to_sectors(sdkp->device, sdkp->zone_blocks); -} - -/** * sd_zbc_setup_zone_mgmt_cmnd - Prepare a zone ZBC_OUT command. The operations * can be RESET WRITE POINTER, OPEN, CLOSE or FINISH. * @cmd: the command to setup @@ -339,32 +335,18 @@ 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 int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks) +static int sd_zbc_check_zones(struct scsi_disk *sdkp, unsigned char *buf, + u32 *zblocks) { - size_t bufsize, buflen; - unsigned int noio_flag; u64 zone_blocks = 0; - sector_t max_lba, block = 0; - unsigned char *buf; + sector_t max_lba; unsigned char *rec; int ret; - u8 same; - /* Do all memory allocations as if GFP_NOIO was specified */ - noio_flag = memalloc_noio_save(); - - /* Get a buffer */ - buf = sd_zbc_alloc_report_buffer(sdkp, SD_ZBC_REPORT_MAX_ZONES, - &bufsize); - if (!buf) { - ret = -ENOMEM; - goto out; - } - - /* Do a report zone to get max_lba and the same field */ - ret = sd_zbc_do_report_zones(sdkp, buf, bufsize, 0, false); + /* Do a report zone to get max_lba and the size of the first zone */ + ret = sd_zbc_do_report_zones(sdkp, buf, SD_BUF_SIZE, 0, false); if (ret) - goto out_free; + return ret; if (sdkp->rc_basis == 0) { /* The max_lba field is the capacity of this device */ @@ -379,82 +361,27 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks) } } - /* - * 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]; - zone_blocks = get_unaligned_be64(&rec[8]); - goto out; - } - - /* - * Check the size of all zones: all zones must be of - * equal size, except the last zone which can be smaller - * than other zones. - */ - do { - - /* Parse REPORT ZONES header */ - buflen = min_t(size_t, get_unaligned_be32(&buf[0]) + 64, - bufsize); - rec = buf + 64; - - /* Parse zone descriptors */ - while (rec < buf + buflen) { - u64 this_zone_blocks = get_unaligned_be64(&rec[8]); - - if (zone_blocks == 0) { - zone_blocks = this_zone_blocks; - } else if (this_zone_blocks != zone_blocks && - (block + this_zone_blocks < sdkp->capacity - || this_zone_blocks > zone_blocks)) { - zone_blocks = 0; - goto out; - } - block += this_zone_blocks; - rec += 64; - } - - if (block < sdkp->capacity) { - ret = sd_zbc_do_report_zones(sdkp, buf, bufsize, block, - true); - if (ret) - goto out_free; - } - - } while (block < sdkp->capacity); - -out: - if (!zone_blocks) { - if (sdkp->first_scan) - sd_printk(KERN_NOTICE, sdkp, - "Devices with non constant zone " - "size are not supported\n"); - ret = -ENODEV; - } else if (!is_power_of_2(zone_blocks)) { + /* Parse REPORT ZONES header */ + rec = buf + 64; + zone_blocks = get_unaligned_be64(&rec[8]); + if (!zone_blocks || !is_power_of_2(zone_blocks)) { if (sdkp->first_scan) sd_printk(KERN_NOTICE, sdkp, "Devices with non power of 2 zone " "size are not supported\n"); - ret = -ENODEV; - } else if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) { + return -ENODEV; + } + + if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) { if (sdkp->first_scan) sd_printk(KERN_NOTICE, sdkp, "Zone size too large\n"); - ret = -EFBIG; - } else { - *zblocks = zone_blocks; - ret = 0; + return -EFBIG; } -out_free: - memalloc_noio_restore(noio_flag); - kvfree(buf); + *zblocks = zone_blocks; - return ret; + return 0; } int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) @@ -480,7 +407,7 @@ 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. */ - ret = sd_zbc_check_zones(sdkp, &zone_blocks); + ret = sd_zbc_check_zones(sdkp, buf, &zone_blocks); if (ret != 0) goto err; |