/** * Write to a ZBC device */ static int32_t zbc_scsi_pwrite(zbc_device_t *dev, zbc_zone_t *zone, const void *buf, uint32_t lba_count, uint64_t lba_ofst) { size_t sz = (size_t) lba_count * dev->zbd_info.zbd_logical_block_size; zbc_sg_cmd_t cmd; int ret; /* WRITE 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_WRITE, (uint8_t *)buf, sz); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_WRITE_CDB_OPCODE; cmd.cdb[1] = 0x10; zbc_sg_cmd_set_int64(&cmd.cdb[2], (zone->zbz_start + lba_ofst)); zbc_sg_cmd_set_int32(&cmd.cdb[10], lba_count); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret == 0 ) { ret = (sz - cmd.io_hdr.resid) / dev->zbd_info.zbd_logical_block_size; } zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Reset zone(s) write pointer. */ static int zbc_scsi_reset_write_pointer(zbc_device_t *dev, uint64_t start_lba) { zbc_sg_cmd_t cmd; int ret; /* Allocate and intialize reset write pointer command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_RESET_WRITE_POINTER, NULL, 0); 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 (94h) | * |-----+-----------------------------------------------------------------------| * | 1 | Reserved | Service Action (04h) | * |-----+-----------------------------------------------------------------------| * | 2 | (MSB) | * |- - -+--- Zone ID ---| * | 9 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 10 | (MSB) | * |- - -+--- Reserved ---| * | 13 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 14 | Reserved | Reset | * |-----+-----------------------------------------------------------------------| * | 15 | Control | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_RESET_WRITE_POINTER_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_RESET_WRITE_POINTER_CDB_SA; if ( start_lba == (uint64_t)-1 ) { /* Reset ALL zones */ cmd.cdb[14] = 0x01; } else { /* Reset only the zone at start_lba */ zbc_sg_cmd_set_int64(&cmd.cdb[2], start_lba); } /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); /* Cleanup */ zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Fill the buffer with the result of INQUIRY command. * buf must be at least ZBC_SG_INQUIRY_REPLY_LEN bytes long. */ int zbc_sg_cmd_inquiry(zbc_device_t *dev, void *buf) { zbc_sg_cmd_t cmd; int ret; /* Allocate and intialize inquiry command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_INQUIRY, NULL, ZBC_SG_INQUIRY_REPLY_LEN); if ( ret != 0 ) { zbc_error("%s: zbc_sg_cmd_init INQUIRY failed\n", dev->zbd_filename); return( ret ); } /* Fill command CDB: * +=============================================================================+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * |Byte | | | | | | | | | * |=====+=======================================================================| * | 0 | Operation Code (12h) | * |-----+-----------------------------------------------------------------------| * | 1 | Logical Unit Number | Reserved | EVPD | * |-----+-----------------------------------------------------------------------| * | 2 | Page Code | * |-----+-----------------------------------------------------------------------| * | 3 | (MSB) | * |- - -+--- Allocation Length ---| * | 4 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 5 | Control | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_INQUIRY_CDB_OPCODE; zbc_sg_cmd_set_int16(&cmd.cdb[3], ZBC_SG_INQUIRY_REPLY_LEN); /* Execute the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret == 0 ) { memcpy(buf, cmd.out_buf, ZBC_SG_INQUIRY_REPLY_LEN); } zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * 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 ); }
/** * Change the value of a zone write pointer ("emulated" ZBC devices only). */ static int zbc_scsi_set_write_pointer(zbc_device_t *dev, uint64_t start_lba, uint64_t wp_lba) { zbc_sg_cmd_t cmd; int ret; /* Allocate and intialize set zone command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_SET_WRITE_POINTER, NULL, 0); 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 (9Fh) | * |-----+-----------------------------------------------------------------------| * | 1 | Reserved | Service Action (16h) | * |-----+-----------------------------------------------------------------------| * | 2 | (MSB) | * |- - -+--- Start LBA ---| * | 8 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 9 | (MSB) | * |- - -+--- Write pointer LBA ---| * | 15 | (LSB) | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_SET_WRITE_POINTER_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_SET_WRITE_POINTER_CDB_SA; zbc_sg_cmd_set_bytes(&cmd.cdb[2], &start_lba, 7); zbc_sg_cmd_set_bytes(&cmd.cdb[9], &wp_lba, 7); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); /* Cleanup */ zbc_sg_cmd_destroy(&cmd); return( ret ); }
/** * Configure zones of a "emulated" ZBC device */ static int zbc_scsi_set_zones(zbc_device_t *dev, uint64_t conv_sz, uint64_t zone_sz) { zbc_sg_cmd_t cmd; int ret; /* Allocate and intialize set zone command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_SET_ZONES, NULL, 0); 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 (9Fh) | * |-----+-----------------------------------------------------------------------| * | 1 | Reserved | Service Action (15h) | * |-----+-----------------------------------------------------------------------| * | 2 | (MSB) | * |- - -+--- Conventional Zone Sise (LBA) ---| * | 8 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 9 | (MSB) | * |- - -+--- Zone Sise (LBA) ---| * | 15 | (LSB) | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_SET_ZONES_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_SET_ZONES_CDB_SA; zbc_sg_cmd_set_bytes(&cmd.cdb[2], &conv_sz, 7); zbc_sg_cmd_set_bytes(&cmd.cdb[9], &zone_sz, 7); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); /* Cleanup */ 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 ); }
/** * Test if unit is ready. This will retry 5 times if the command * returns "UNIT ATTENTION". */ int zbc_sg_cmd_test_unit_ready(zbc_device_t *dev) { zbc_sg_cmd_t cmd; int ret = -EAGAIN, retries = 5; while( retries && (ret == -EAGAIN) ) { retries--; /* Intialize command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_TEST_UNIT_READY, NULL, 0); if ( ret != 0 ) { zbc_error("%s: zbc_sg_cmd_init TEST UNIT READY failed\n", dev->zbd_filename); return( ret ); } cmd.cdb[0] = ZBC_SG_TEST_UNIT_READY_CDB_OPCODE; /* Execute the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( (ret != 0) && ((cmd.io_hdr.host_status == ZBC_SG_DID_SOFT_ERROR) || (cmd.io_hdr.sb_len_wr && (cmd.sense_buf[2] == 0x06))) ) { zbc_debug("%s: Unit attention required, %d / 5 retries left\n", dev->zbd_filename, retries); ret = -EAGAIN; } zbc_sg_cmd_destroy(&cmd); } if ( ret != 0 ) { ret = -ENXIO; } return( ret ); }
/** * Flush a ZBC device cache. */ static int zbc_scsi_flush(zbc_device_t *dev, uint64_t lba_ofst, uint32_t lba_count, int immediate) { zbc_sg_cmd_t cmd; int ret; /* SYNCHRONIZE CACHE 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_SYNC_CACHE, NULL, 0); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_SYNC_CACHE_CDB_OPCODE; if ( lba_ofst ) { zbc_sg_cmd_set_int64(&cmd.cdb[2], lba_ofst); } if ( lba_count ) { zbc_sg_cmd_set_int32(&cmd.cdb[10], lba_count); } if ( immediate ) { cmd.cdb[1] = 0x02; } /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); 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 information (model, vendor, ...) from a SCSI device. */ static int zbc_scsi_classify(zbc_device_t *dev) { zbc_sg_cmd_t cmd; int dev_type; int n, ret; /* Allocate and intialize inquiry command */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_INQUIRY, NULL, ZBC_SG_INQUIRY_REPLY_LEN); 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 (12h) | * |-----+-----------------------------------------------------------------------| * | 1 | Logical Unit Number | Reserved | EVPD | * |-----+-----------------------------------------------------------------------| * | 2 | Page Code | * |-----+-----------------------------------------------------------------------| * | 3 | (MSB) | * |- - -+--- Allocation Length ---| * | 4 | (LSB) | * |-----+-----------------------------------------------------------------------| * | 5 | Control | * +=============================================================================+ */ cmd.cdb[0] = ZBC_SG_INQUIRY_CDB_OPCODE; zbc_sg_cmd_set_int16(&cmd.cdb[3], ZBC_SG_INQUIRY_REPLY_LEN); /* Execute the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Make sure we are not dealing with an ATA device */ if ( strncmp((char *)&cmd.out_buf[8], "ATA", 3) == 0 ) { ret = -ENXIO; goto out; } /* This is a SCSI device */ dev->zbd_info.zbd_type = ZBC_DT_SCSI; /* Vendor identification */ n = zbc_sg_cmd_strcpy(&dev->zbd_info.zbd_vendor_id[0], (char *)&cmd.out_buf[8], 8); /* Product identification */ n += zbc_sg_cmd_strcpy(&dev->zbd_info.zbd_vendor_id[n], (char *)&cmd.out_buf[16], 16); /* Product revision */ n += zbc_sg_cmd_strcpy(&dev->zbd_info.zbd_vendor_id[n], (char *)&cmd.out_buf[32], 4); /* Now check the device type */ dev_type = (int)(cmd.out_buf[0] & 0x1f); if ( dev_type == ZBC_DEV_TYPE_HOST_MANAGED ) { /* Host-managed device */ zbc_debug("Host-managed ZBC disk signature detected\n"); dev->zbd_info.zbd_model = ZBC_DM_HOST_MANAGED; goto out; } if ( dev_type != ZBC_DEV_TYPE_STANDARD ) { /* Unsupported device */ ret = -ENXIO; goto out; } zbc_debug("Standard SCSI disk signature detected\n"); /* This may be a host-aware device: look at VPD */ /* page B1h (block device characteristics) */ memset(cmd.cdb, 0, sizeof(cmd.cdb)); memset(cmd.out_buf, 0, ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B1); cmd.cdb[0] = ZBC_SG_INQUIRY_CDB_OPCODE; cmd.cdb[1] = 0x01; cmd.cdb[2] = 0xB1; zbc_sg_cmd_set_int16(&cmd.cdb[3], ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B1); /* Execute the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret == 0 ) { if ( (cmd.out_buf[1] == 0xB1) && (cmd.out_buf[2] == 0x00) && (cmd.out_buf[3] == 0x3C) ) { switch( (cmd.out_buf[8] & 0x30) >> 4 ) { case 0x01: /* Host aware device */ zbc_debug("Host aware ZBC disk detected\n"); dev->zbd_info.zbd_model = ZBC_DM_HOST_AWARE; break; case 0x00: case 0x10: /* Standard or drive-managed device */ zbc_debug("Standard or drive managed SCSI disk detected\n"); dev->zbd_info.zbd_model = ZBC_DM_DRIVE_MANAGED; ret = -ENXIO; break; default: zbc_debug("Unknown device type\n"); dev->zbd_info.zbd_model = ZBC_DM_DRIVE_UNKNOWN; ret = -ENXIO; break; } } } out: zbc_sg_cmd_destroy(&cmd); 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 ); }