/** * Get zoned block device characteristics * (Maximum or optimum number of open zones). */ int zbc_scsi_get_zbd_chars(zbc_device_t *dev) { zbc_sg_cmd_t cmd; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_INQUIRY, NULL, ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_INQUIRY_CDB_OPCODE; cmd.cdb[1] = 0x01; cmd.cdb[2] = 0xB6; zbc_sg_cmd_set_int16(&cmd.cdb[3], ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* URSWRZ (unrestricted read in sequential write required zone) flag */ dev->zbd_info.zbd_flags |= (cmd.out_buf[4] & 0x01) ? ZBC_UNRESTRICTED_READ : 0; /* Resource of handling zones */ dev->zbd_info.zbd_opt_nr_open_seq_pref = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); dev->zbd_info.zbd_opt_nr_non_seq_write_seq_pref = zbc_sg_cmd_get_int32(&cmd.out_buf[12]); dev->zbd_info.zbd_max_nr_open_seq_req = zbc_sg_cmd_get_int32(&cmd.out_buf[16]); if ( (dev->zbd_info.zbd_model == ZBC_DM_HOST_MANAGED) && (dev->zbd_info.zbd_max_nr_open_seq_req <= 0) ) { zbc_error("%s: invalid maximum number of open sequential write required zones for host-managed device\n", dev->zbd_filename); ret = -EINVAL; } out: zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Get zoned block device characteristics(Maximum or optimul number of opening zones). */ static int zbc_scsi_get_zbd_chars(zbc_device_t *dev) { zbc_sg_cmd_t cmd; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_INQUIRY, NULL, ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_INQUIRY_CDB_OPCODE; cmd.cdb[1] = 0x01; cmd.cdb[2] = 0xB6; zbc_sg_cmd_set_int16(&cmd.cdb[3], ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Resource of handling zones */ dev->zbd_info.zbd_opt_nr_open_seq_pref = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); dev->zbd_info.zbd_opt_nr_open_non_seq_write_seq_pref = zbc_sg_cmd_get_int32(&cmd.out_buf[12]); dev->zbd_info.zbd_max_nr_open_seq_req = zbc_sg_cmd_get_int32(&cmd.out_buf[16]); if ( dev->zbd_info.zbd_max_nr_open_seq_req <= 0 ) { zbc_error("%s: invalid maximum number of open sequential write required zones\n", dev->zbd_filename); ret = -EINVAL; } out: zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Get a device information (capacity & sector sizes). */ static int zbc_scsi_get_capacity(zbc_device_t *dev) { zbc_sg_cmd_t cmd; zbc_zone_t *zones = NULL; int logical_per_physical; unsigned int nr_zones = 0; uint64_t max_lba; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_READ_CAPACITY, NULL, ZBC_SG_READ_CAPACITY_REPLY_LEN); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_READ_CAPACITY_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_READ_CAPACITY_CDB_SA; zbc_sg_cmd_set_int32(&cmd.cdb[10], ZBC_SG_READ_CAPACITY_REPLY_LEN); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Logical block size */ dev->zbd_info.zbd_logical_block_size = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); if ( dev->zbd_info.zbd_logical_block_size <= 0 ) { zbc_error("%s: invalid logical sector size\n", dev->zbd_filename); ret = -EINVAL; goto out; } logical_per_physical = 1 << cmd.out_buf[13] & 0x0f; /* Check RC_BASIS field */ switch( (cmd.out_buf[12] & 0x30) >> 4 ) { case 0x00: /* The logical block address indicates the last LBA of the */ /* conventional zones at the beginning of the disk. To get */ /* the entire disk capacity, we need to get last LBA of */ /* the last zone of the disk. */ ret = zbc_scsi_report_zones(dev, 0, ZBC_RO_ALL, &max_lba, NULL, &nr_zones); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } /* Set the drive capacity to the reported max LBA */ dev->zbd_info.zbd_logical_blocks = max_lba + 1; break; case 0x01: /* The disk last LBA was reproted */ dev->zbd_info.zbd_logical_blocks = zbc_sg_cmd_get_int64(&cmd.out_buf[0]) + 1; break; default: zbc_error("%s: invalid RC_BASIS field encountered in READ CAPACITY result\n", dev->zbd_filename); ret = -EIO; goto out; } if ( ! dev->zbd_info.zbd_logical_blocks ) { zbc_error("%s: invalid capacity (logical blocks)\n", dev->zbd_filename); ret = -EINVAL; goto out; } dev->zbd_info.zbd_physical_block_size = dev->zbd_info.zbd_logical_block_size * logical_per_physical; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks / logical_per_physical; out: zbc_sg_cmd_destroy(&cmd); if ( zones ) { free(zones); } return( ret ); }
/** * Get a SCSI device zone information. */ int zbc_scsi_report_zones(zbc_device_t *dev, uint64_t start_lba, enum zbc_reporting_options ro, uint64_t *max_lba, zbc_zone_t *zones, unsigned int *nr_zones) { size_t bufsz = ZBC_ZONE_DESCRIPTOR_OFFSET; unsigned int i, nz, buf_nz; zbc_sg_cmd_t cmd; uint8_t *buf; int ret; if ( *nr_zones ) { bufsz += (size_t)*nr_zones * ZBC_ZONE_DESCRIPTOR_LENGTH; if ( bufsz > ZBC_SCSI_REPORT_ZONES_BUFSZ ) { bufsz = ZBC_SCSI_REPORT_ZONES_BUFSZ; } } /* For in kernel ATA translation: align to 512 B */ bufsz = (bufsz + 511) & ~511; /* Allocate and intialize report zones command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_REPORT_ZONES, NULL, bufsz); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB: * +=============================================================================+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * |Byte | | | | | | | | | * |=====+==========================+============================================| * | 0 | Operation Code (95h) | * |-----+-----------------------------------------------------------------------| * | 1 | Reserved | Service Action (00h) | * |-----+-----------------------------------------------------------------------| * | 2 | (MSB) | * |- - -+--- Zone Start LBA ---| * | 9 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 10 | (MSB) | * |- - -+--- Allocation Length ---| * | 13 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 14 |Partial |Reserved| Reporting Options | * |-----+-----------------------------------------------------------------------| * | 15 | Control | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_REPORT_ZONES_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_REPORT_ZONES_CDB_SA; zbc_sg_cmd_set_int64(&cmd.cdb[2], start_lba); zbc_sg_cmd_set_int32(&cmd.cdb[10], (unsigned int) bufsz); cmd.cdb[14] = ro & 0xbf; /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } if ( cmd.out_bufsz < ZBC_ZONE_DESCRIPTOR_OFFSET ) { zbc_error("Not enough data received (need at least %d B, got %zu B)\n", ZBC_ZONE_DESCRIPTOR_OFFSET, cmd.out_bufsz); ret = -EIO; goto out; } /* Process output: * +=============================================================================+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * |Byte | | | | | | | | | * |=====+=======================================================================| * | 0 | (MSB) | * |- - -+--- Zone List Length (n - 64) ---| * | 3 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 4 | Reserved | Same | * |-----+-----------------------------------------------------------------------| * | 5 | | * |- - -+--- Reserved ---| * | 7 | | * |-----+-----------------------------------------------------------------------| * | 8 | (MSB) | * |- - -+--- Maximum LBA ---| * | 15 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 16 | (MSB) | * |- - -+--- Reserved ---| * | 63 | (LSB) | * |=====+=======================================================================| * | | Vendor-Specific Parameters | * |=====+=======================================================================| * | 64 | (MSB) | * |- - -+--- Zone Descriptor [first] ---| * | 127 | (LSB) | * |-----+-----------------------------------------------------------------------| * | . | * | . | * | . | * |-----+-----------------------------------------------------------------------| * |n-63 | | * |- - -+--- Zone Descriptor [last] ---| * | n | | * +=============================================================================+ */ /* Get number of zones in result */ buf = (uint8_t *) cmd.out_buf; nz = zbc_sg_cmd_get_int32(buf) / ZBC_ZONE_DESCRIPTOR_LENGTH; if ( max_lba ) { *max_lba = zbc_sg_cmd_get_int64(&buf[8]); } if ( zones && nz ) { /* Get zone info */ if ( nz > *nr_zones ) { nz = *nr_zones; } buf_nz = (cmd.out_bufsz - ZBC_ZONE_DESCRIPTOR_OFFSET) / ZBC_ZONE_DESCRIPTOR_LENGTH; if ( nz > buf_nz ) { nz = buf_nz; } /* Get zone descriptors: * +=============================================================================+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * |Byte | | | | | | | | | * |=====+=======================================================================| * | 0 | Reserved | Zone type | * |-----+-----------------------------------------------------------------------| * | 1 | Zone condition | Reserved |non-seq | Reset | * |-----+-----------------------------------------------------------------------| * | 2 | | * |- - -+--- Reserved ---| * | 7 | | * |-----+-----------------------------------------------------------------------| * | 8 | (MSB) | * |- - -+--- Zone Length ---| * | 15 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 16 | (MSB) | * |- - -+--- Zone Start LBA ---| * | 23 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 24 | (MSB) | * |- - -+--- Write Pointer LBA ---| * | 31 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 32 | | * |- - -+--- Reserved ---| * | 63 | | * +=============================================================================+ */ buf += ZBC_ZONE_DESCRIPTOR_OFFSET; for(i = 0; i < nz; i++) { zones[i].zbz_type = buf[0] & 0x0f; zones[i].zbz_condition = (buf[1] >> 4) & 0x0f; zones[i].zbz_length = zbc_sg_cmd_get_int64(&buf[8]); zones[i].zbz_start = zbc_sg_cmd_get_int64(&buf[16]); zones[i].zbz_write_pointer = zbc_sg_cmd_get_int64(&buf[24]); zones[i].zbz_flags = buf[1] & 0x03; buf += ZBC_ZONE_DESCRIPTOR_LENGTH; } } /* Return number of zones */ *nr_zones = nz; out: /* Cleanup */ zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Get a device information (capacity & sector sizes). */ static int zbc_scsi_get_capacity(zbc_device_t *dev) { zbc_sg_cmd_t cmd; zbc_zone_t *zones = NULL; int logical_per_physical; unsigned int nr_zones = 0; uint64_t slba = 0; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_READ_CAPACITY, NULL, ZBC_SG_READ_CAPACITY_REPLY_LEN); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_READ_CAPACITY_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_READ_CAPACITY_CDB_SA; zbc_sg_cmd_set_int32(&cmd.cdb[10], ZBC_SG_READ_CAPACITY_REPLY_LEN); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Logical block size */ dev->zbd_info.zbd_logical_block_size = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); if ( dev->zbd_info.zbd_logical_block_size <= 0 ) { zbc_error("%s: invalid logical sector size\n", dev->zbd_filename); ret = -EINVAL; goto out; } logical_per_physical = 1 << cmd.out_buf[13] & 0x0f; /* Check RC_BASIS field */ switch( (cmd.out_buf[12] & 0x30) >> 4 ) { case 0x00: /* The logical block address indicates the last LBA of the */ /* conventional zones at the beginning of the disk. To get */ /* the entire disk capacity, we need to get last LBA of */ /* the last zone of the disk. */ ret = zbc_scsi_report_zones(dev, 0, 0, NULL, &nr_zones); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } if ( ! nr_zones ) { ret = -EIO; goto out; } /* Allocate zone array */ zones = (zbc_zone_t *) malloc(sizeof(zbc_zone_t) * nr_zones); if ( ! zones ) { zbc_error("No memory\n"); ret = -ENOMEM; goto out; } memset(zones, 0, sizeof(zbc_zone_t) * nr_zones); /* Get all zone information */ unsigned int n, z = 0, nz = 0; while ( nz < nr_zones ) { n= nr_zones - nz; ret = zbc_scsi_report_zones(dev, slba, 0, &zones[z], &n); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } if ( n == 0 ) { ret = -EIO; break; } nz += n; z += n; slba = zones[z - 1].zbz_start + zones[z - 1].zbz_length; } /* Get the drive capacity from the last zone last LBA */ dev->zbd_info.zbd_logical_blocks = zbc_zone_next_lba(&zones[nr_zones - 1]); break; case 0x01: /* The disk last LBA was reproted */ dev->zbd_info.zbd_logical_blocks = zbc_sg_cmd_get_int64(&cmd.out_buf[0]) + 1; break; default: zbc_error("%s: invalid RC_BASIS field encountered in READ CAPACITY result\n", dev->zbd_filename); ret = -EIO; goto out; } if ( ! dev->zbd_info.zbd_logical_blocks ) { zbc_error("%s: invalid capacity (logical blocks)\n", dev->zbd_filename); ret = -EINVAL; goto out; } dev->zbd_info.zbd_physical_block_size = dev->zbd_info.zbd_logical_block_size * logical_per_physical; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks / logical_per_physical; out: zbc_sg_cmd_destroy(&cmd); if ( zones ) { free(zones); } return( ret ); }