/** * 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 ); }
/** * Open an emulation device or file. */ static int zbc_fake_open(const char *filename, int flags, struct zbc_device **pdev) { zbc_fake_device_t *fdev; int fd, ret; zbc_debug("%s: ########## Trying FAKE driver ##########\n", filename); /* Open emulation device/file */ fd = open(filename, zbc_open_flags(flags)); if ( fd < 0 ) { zbc_error("%s: open failed %d (%s)\n", filename, errno, strerror(errno)); ret = -errno; goto out; } /* Allocate a handle */ ret = -ENOMEM; fdev = calloc(1, sizeof(*fdev)); if ( ! fdev ) { goto out; } fdev->dev.zbd_fd = fd; fdev->zbd_meta_fd = -1; fdev->dev.zbd_filename = strdup(filename); if ( ! fdev->dev.zbd_filename ) { goto out_free_dev; } /* Set the fake device information */ ret = zbc_fake_set_info(&fdev->dev); if ( ret != 0 ) { goto out_free_filename; } /* Open metadata */ ret = zbc_fake_open_metadata(fdev); if ( ret ) { goto out_free_filename; } *pdev = &fdev->dev; zbc_debug("%s: ########## FAKE driver succeeded ##########\n", filename); return 0; out_free_filename: free(fdev->dev.zbd_filename); out_free_dev: free(fdev); out: if ( fd >= 0 ) { close(fd); } zbc_debug("%s: ########## FAKE driver failed %d ##########\n", filename, ret); return ret; }
/** * Set a device info. */ static int zbc_fake_set_info(struct zbc_device *dev) { unsigned long long size64; struct stat st; int size32; int ret; /* Get device stats */ if ( fstat(dev->zbd_fd, &st) < 0 ) { ret = -errno; zbc_error("%s: stat failed %d (%s)\n", dev->zbd_filename, errno, strerror(errno)); return ret; } if ( S_ISBLK(st.st_mode) ) { /* Get logical block size */ ret = ioctl(dev->zbd_fd, BLKSSZGET, &size32); if ( ret != 0 ) { ret = -errno; zbc_error("%s: ioctl BLKSSZGET failed %d (%s)\n", dev->zbd_filename, errno, strerror(errno)); return ret; } dev->zbd_info.zbd_logical_block_size = size32; /* Get physical block size */ ret = ioctl(dev->zbd_fd, BLKPBSZGET, &size32); if ( ret != 0 ) { ret = -errno; zbc_error("%s: ioctl BLKPBSZGET failed %d (%s)\n", dev->zbd_filename, errno, strerror(errno)); return ret; } dev->zbd_info.zbd_physical_block_size = size32; /* Get capacity (B) */ ret = ioctl(dev->zbd_fd, BLKGETSIZE64, &size64); if ( ret != 0 ) { ret = -errno; zbc_error("%s: ioctl BLKGETSIZE64 failed %d (%s)\n", dev->zbd_filename, errno, strerror(errno)); return ret; } if ( dev->zbd_info.zbd_logical_block_size <= 0 ) { zbc_error("%s: invalid logical sector size %d\n", dev->zbd_filename, size32); return -EINVAL; } dev->zbd_info.zbd_logical_blocks = size64 / dev->zbd_info.zbd_logical_block_size; if ( dev->zbd_info.zbd_physical_block_size <= 0 ) { zbc_error("%s: invalid physical sector size %d\n", dev->zbd_filename, size32); return -EINVAL; } dev->zbd_info.zbd_physical_blocks = size64 / dev->zbd_info.zbd_physical_block_size; } else if ( S_ISREG(st.st_mode) ) { /* Default value for files */ dev->zbd_info.zbd_logical_block_size = ZBC_FAKE_FILE_SECTOR_SIZE; dev->zbd_info.zbd_logical_blocks = st.st_size / ZBC_FAKE_FILE_SECTOR_SIZE; dev->zbd_info.zbd_physical_block_size = dev->zbd_info.zbd_logical_block_size; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks; } else { return -ENXIO; } /* Check */ if ( ! dev->zbd_info.zbd_logical_blocks ) { zbc_error("%s: invalid capacity (logical blocks)\n", dev->zbd_filename); return -EINVAL; } if ( ! dev->zbd_info.zbd_physical_blocks ) { zbc_error("%s: invalid capacity (physical blocks)\n", dev->zbd_filename); return -EINVAL; } /* Finish setting */ dev->zbd_info.zbd_type = ZBC_DT_FAKE; dev->zbd_info.zbd_model = ZBC_DM_HOST_MANAGED; strncpy(dev->zbd_info.zbd_vendor_id, "FAKE HGST HM libzbc", ZBC_DEVICE_INFO_LENGTH - 1); dev->zbd_info.zbd_opt_nr_open_seq_pref = 0; dev->zbd_info.zbd_opt_nr_non_seq_write_seq_pref = 0; dev->zbd_info.zbd_max_nr_open_seq_req = ZBC_FAKE_MAX_OPEN_NR_ZONES; /* Get maximum command size */ zbc_sg_get_max_cmd_blocks(dev); return 0; }
/** * Open metadata file of a fake device. */ static int zbc_fake_open_metadata(zbc_fake_device_t *fdev) { char meta_path[512]; struct stat st; int ret; zbc_fake_dev_meta_path(fdev, meta_path); zbc_debug("%s: using meta file %s\n", fdev->dev.zbd_filename, meta_path); pthread_mutexattr_init(&fdev->zbd_mutex_attr); pthread_mutexattr_setpshared(&fdev->zbd_mutex_attr, PTHREAD_PROCESS_SHARED); fdev->zbd_meta_fd = open(meta_path, O_RDWR); if ( fdev->zbd_meta_fd < 0 ) { /* Metadata does not exist yet, we'll have to wait for a set_zones call */ if ( errno == ENOENT ) { return 0; } ret = -errno; zbc_error("%s: open metadata file %s failed %d (%s)\n", fdev->dev.zbd_filename, meta_path, errno, strerror(errno)); goto out; } if ( fstat(fdev->zbd_meta_fd, &st) < 0 ) { zbc_error("%s: fstat metadata file %s failed %d (%s)\n", fdev->dev.zbd_filename, meta_path, errno, strerror(errno)); ret = -errno; goto out; } /* mmap metadata file */ fdev->zbd_meta_size = st.st_size; fdev->zbd_meta = mmap(NULL, fdev->zbd_meta_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdev->zbd_meta_fd, 0); if ( fdev->zbd_meta == MAP_FAILED ) { fdev->zbd_meta = NULL; zbc_error("%s: mmap metadata file %s failed\n", fdev->dev.zbd_filename, meta_path); ret = -ENOMEM; goto out; } /* Check */ if ( (fdev->zbd_meta->zbd_capacity > (fdev->dev.zbd_info.zbd_logical_block_size * fdev->dev.zbd_info.zbd_logical_blocks)) || (! fdev->zbd_meta->zbd_nr_zones) ) { /* Do not report an error here to allow the execution of zbc_set_zones */ zbc_debug("%s: invalid metadata file %s\n", fdev->dev.zbd_filename, meta_path); zbc_fake_close_metadata(fdev); ret = 0; goto out; } zbc_debug("%s: %llu sectors of %zuB, %u zones\n", fdev->dev.zbd_filename, (unsigned long long)fdev->dev.zbd_info.zbd_logical_blocks, (size_t)fdev->dev.zbd_info.zbd_logical_block_size, fdev->zbd_meta->zbd_nr_zones); fdev->zbd_nr_zones = fdev->zbd_meta->zbd_nr_zones; fdev->zbd_zones = (struct zbc_zone *) (fdev->zbd_meta + 1); if ( fdev->dev.zbd_info.zbd_max_nr_open_seq_req > fdev->zbd_meta->zbd_nr_seq_zones ) { fdev->dev.zbd_info.zbd_max_nr_open_seq_req = fdev->zbd_meta->zbd_nr_seq_zones - 1; } ret = 0; out: if ( ret != 0 ) { zbc_fake_close_metadata(fdev); } return ret; }
/** * Initialize an emulated device metadata. */ static int zbc_fake_set_zones(struct zbc_device *dev, uint64_t conv_sz, uint64_t zone_sz) { zbc_fake_device_t *fdev = zbc_fake_to_file_dev(dev); uint64_t lba = 0, device_size = dev->zbd_info.zbd_logical_blocks; zbc_fake_meta_t fmeta; char meta_path[512]; struct zbc_zone *zone; unsigned int z = 0; int ret; /* Initialize metadata */ if ( fdev->zbd_meta ) { zbc_fake_close_metadata(fdev); } memset(&fmeta, 0, sizeof(zbc_fake_meta_t)); pthread_mutexattr_init(&fdev->zbd_mutex_attr); pthread_mutexattr_setpshared(&fdev->zbd_mutex_attr, PTHREAD_PROCESS_SHARED); /* Calculate zone configuration */ if ( (conv_sz + zone_sz) > device_size ) { zbc_error("%s: invalid zone sizes (too large)\n", fdev->dev.zbd_filename); return -EINVAL; } fmeta.zbd_nr_conv_zones = conv_sz / zone_sz; if ( conv_sz && (! fmeta.zbd_nr_conv_zones) ) { fmeta.zbd_nr_conv_zones = 1; } fmeta.zbd_nr_seq_zones = (device_size - (fmeta.zbd_nr_conv_zones * zone_sz)) / zone_sz; if ( ! fmeta.zbd_nr_seq_zones ) { zbc_error("%s: invalid zone sizes (too large)\n", fdev->dev.zbd_filename); return -EINVAL; } fmeta.zbd_nr_zones = fmeta.zbd_nr_conv_zones + fmeta.zbd_nr_seq_zones; fdev->zbd_nr_zones = fmeta.zbd_nr_zones; dev->zbd_info.zbd_logical_blocks = fdev->zbd_nr_zones * zone_sz; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks / (dev->zbd_info.zbd_physical_block_size / dev->zbd_info.zbd_logical_block_size); fmeta.zbd_capacity = dev->zbd_info.zbd_logical_blocks * dev->zbd_info.zbd_logical_block_size; /* Open metadata file */ zbc_fake_dev_meta_path(fdev, meta_path); fdev->zbd_meta_fd = open(meta_path, O_RDWR | O_CREAT, 0600); if ( fdev->zbd_meta_fd < 0 ) { ret = -errno; zbc_error("%s: open metadata file %s failed %d (%s)\n", fdev->dev.zbd_filename, meta_path, errno, strerror(errno)); return ret; } /* Truncate metadata file */ fdev->zbd_meta_size = sizeof(zbc_fake_meta_t) + (fdev->zbd_nr_zones * sizeof(struct zbc_zone)); if ( ftruncate(fdev->zbd_meta_fd, fdev->zbd_meta_size) < 0) { ret = -errno; zbc_error("%s: truncate metadata file %s to %zu B failed %d (%s)\n", fdev->dev.zbd_filename, meta_path, fdev->zbd_meta_size, errno, strerror(errno)); goto out; } /* mmap metadata file */ fdev->zbd_meta = mmap(NULL, fdev->zbd_meta_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdev->zbd_meta_fd, 0); if ( fdev->zbd_meta == MAP_FAILED ) { fdev->zbd_meta = NULL; zbc_error("%s: mmap metadata file %s failed\n", fdev->dev.zbd_filename, meta_path); ret = -ENOMEM; goto out; } fdev->zbd_zones = (struct zbc_zone *) (fdev->zbd_meta + 1); /* Setup metadata header */ memcpy(fdev->zbd_meta, &fmeta, sizeof(zbc_fake_meta_t)); ret = pthread_mutex_init(&fdev->zbd_meta->zbd_mutex, &fdev->zbd_mutex_attr); if ( ret != 0 ) { zbc_error("%s: Initialize metadata mutex failed %d (%s)\n", fdev->dev.zbd_filename, ret, strerror(ret)); goto out; } /* Setup conventional zones descriptors */ for(z = 0; z < fmeta.zbd_nr_conv_zones; z++) { zone = &fdev->zbd_zones[z]; zone->zbz_type = ZBC_ZT_CONVENTIONAL; zone->zbz_condition = ZBC_ZC_NOT_WP; zone->zbz_start = lba; zone->zbz_write_pointer = (uint64_t)-1; zone->zbz_length = zone_sz; memset(&zone->__pad, 0, sizeof(zone->__pad)); lba += zone_sz; } /* Setup sequential zones descriptors */ for (; z < fdev->zbd_nr_zones; z++) { zone = &fdev->zbd_zones[z]; zone->zbz_type = ZBC_ZT_SEQUENTIAL_REQ; zone->zbz_condition = ZBC_ZC_EMPTY; zone->zbz_start = lba; zone->zbz_write_pointer = zone->zbz_start; zone->zbz_length = zone_sz; memset(&zone->__pad, 0, sizeof(zone->__pad)); lba += zone_sz; } ret = 0; out: if ( ret != 0 ) { zbc_fake_close_metadata(fdev); } 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 ); }
/** * Open a disk. */ static int zbc_scsi_open(const char *filename, int flags, struct zbc_device **pdev) { struct zbc_device *dev; struct stat st; int fd, ret; zbc_debug("%s: ########## Trying SCSI driver ##########\n", filename); /* Open the device file */ fd = open(filename, zbc_open_flags(flags)); if ( fd < 0 ) { zbc_error("Open device file %s failed %d (%s)\n", filename, errno, strerror(errno)); ret = -errno; goto out; } /* Check device */ if ( fstat(fd, &st) != 0 ) { zbc_error("Stat device %s failed %d (%s)\n", filename, errno, strerror(errno)); ret = -errno; goto out; } if ( (! S_ISCHR(st.st_mode)) && (! S_ISBLK(st.st_mode)) ) { ret = -ENXIO; goto out; } /* Set device decriptor */ ret = -ENOMEM; dev = calloc(1, sizeof(struct zbc_device)); if ( ! dev ) { goto out; } dev->zbd_filename = strdup(filename); if ( ! dev->zbd_filename ) { goto out_free_dev; } dev->zbd_fd = fd; ret = zbc_scsi_get_info(dev); if ( ret ) { goto out_free_filename; } *pdev = dev; zbc_debug("%s: ########## SCSI driver succeeded ##########\n", filename); return( 0 ); out_free_filename: free(dev->zbd_filename); out_free_dev: free(dev); out: if ( fd >= 0 ) { close(fd); } zbc_debug("%s: ########## SCSI driver failed %d ##########\n", filename, ret); return( ret ); }
/** * Execute a command. */ int zbc_sg_cmd_exec(zbc_device_t *dev, zbc_sg_cmd_t *cmd) { int ret; if ( zbc_log_level >= ZBC_LOG_DEBUG ) { zbc_debug("%s: Sending command 0x%02x:0x%02x (%s):\n", dev->zbd_filename, cmd->cdb_opcode, cmd->cdb_sa, zbc_sg_cmd_name(cmd)); zbc_sg_print_bytes(dev, cmd->cdb, cmd->cdb_sz); } /* Send the SG_IO command */ ret = ioctl(dev->zbd_fd, SG_IO, &cmd->io_hdr); if ( ret != 0 ) { ret = -errno; if ( zbc_log_level >= ZBC_LOG_DEBUG ) { zbc_error("%s: SG_IO ioctl failed %d (%s)\n", dev->zbd_filename, errno, strerror(errno)); } goto out; } /* Reset errno */ zbc_sg_set_sense(dev, NULL); zbc_debug("%s: Command %s done: status 0x%02x (0x%02x), host status 0x%04x, driver status 0x%04x (flags 0x%04x)\n", dev->zbd_filename, zbc_sg_cmd_name(cmd), (unsigned int)cmd->io_hdr.status, (unsigned int)cmd->io_hdr.masked_status, (unsigned int)cmd->io_hdr.host_status, (unsigned int)zbc_sg_cmd_driver_status(cmd), (unsigned int)zbc_sg_cmd_driver_flags(cmd)); /* Check status */ if ( ((cmd->code == ZBC_SG_ATA12) || (cmd->code == ZBC_SG_ATA16)) && (cmd->cdb[2] & (1 << 5)) ) { /* ATA command status */ if ( cmd->io_hdr.status != ZBC_SG_CHECK_CONDITION ) { zbc_sg_set_sense(dev, cmd->sense_buf); ret = -EIO; goto out; } if ( (zbc_sg_cmd_driver_status(cmd) == ZBC_SG_DRIVER_SENSE) && (cmd->io_hdr.sb_len_wr > 21) && (cmd->sense_buf[21] != 0x50) ) { zbc_sg_set_sense(dev, cmd->sense_buf); ret = -EIO; goto out; } cmd->io_hdr.status = 0; } if ( cmd->io_hdr.status || (cmd->io_hdr.host_status != ZBC_SG_DID_OK) || (zbc_sg_cmd_driver_status(cmd) && (zbc_sg_cmd_driver_status(cmd) != ZBC_SG_DRIVER_SENSE)) ) { if ( zbc_log_level >= ZBC_LOG_DEBUG ) { zbc_error("%s: Command %s failed with status 0x%02x (0x%02x), host status 0x%04x, driver status 0x%04x (flags 0x%04x)\n", dev->zbd_filename, zbc_sg_cmd_name(cmd), (unsigned int)cmd->io_hdr.status, (unsigned int)cmd->io_hdr.masked_status, (unsigned int)cmd->io_hdr.host_status, (unsigned int)zbc_sg_cmd_driver_status(cmd), (unsigned int)zbc_sg_cmd_driver_flags(cmd)); if ( cmd->io_hdr.sb_len_wr ) { zbc_debug("%s: Sense data (%d B):\n", dev->zbd_filename, cmd->io_hdr.sb_len_wr); zbc_sg_print_bytes(dev, cmd->sense_buf, cmd->io_hdr.sb_len_wr); } else { zbc_debug("%s: No sense data\n", dev->zbd_filename); } } zbc_sg_set_sense(dev, cmd->sense_buf); ret = -EIO; goto out; } if ( cmd->io_hdr.resid ) { zbc_debug("%s: Transfer missing %d B of data\n", dev->zbd_filename, cmd->io_hdr.resid); cmd->out_bufsz -= cmd->io_hdr.resid; } zbc_debug("%s: Command %s executed in %u ms, %zu B transfered\n", dev->zbd_filename, zbc_sg_cmd_name(cmd), cmd->io_hdr.duration, cmd->out_bufsz); out: return( ret ); }
/** * Allocate and initialize a new command. */ int zbc_sg_cmd_init(zbc_sg_cmd_t *cmd, int cmd_code, uint8_t *out_buf, size_t out_bufsz) { int ret = 0; if ( (! cmd) || (cmd_code < 0) || (cmd_code >= ZBC_SG_CMD_NUM) ) { zbc_error("Invalid command specified\n"); return( -EINVAL ); } /* Set command */ memset(cmd, 0, sizeof(zbc_sg_cmd_t)); cmd->code = cmd_code; cmd->cdb_sz = zbc_sg_cmd_list[cmd_code].cdb_length; zbc_assert(cmd->cdb_sz <= ZBC_SG_CDB_MAX_LENGTH); cmd->cdb_opcode = zbc_sg_cmd_list[cmd_code].cdb_opcode; cmd->cdb_sa = zbc_sg_cmd_list[cmd_code].cdb_sa; /* Set output buffer */ if ( out_buf ) { /* Set specified buffer */ if ( ! out_bufsz ) { zbc_error("Invalid 0 output buffer size\n"); ret = -EINVAL; goto out; } cmd->out_buf = out_buf; cmd->out_bufsz = out_bufsz; } else if ( out_bufsz ) { /* Allocate a buffer */ ret = posix_memalign((void **) &cmd->out_buf, sysconf(_SC_PAGESIZE), out_bufsz); if ( ret != 0 ) { zbc_error("No memory for output buffer (%zu B)\n", out_bufsz); ret = -ENOMEM; goto out; } memset(cmd->out_buf, 0, out_bufsz); cmd->out_bufsz = out_bufsz; cmd->out_buf_needfree = 1; } /* OK: setup SGIO header */ memset(&cmd->io_hdr, 0, sizeof(sg_io_hdr_t)); cmd->io_hdr.interface_id = 'S'; cmd->io_hdr.timeout = 20000; cmd->io_hdr.flags = 0; //SG_FLAG_DIRECT_IO; cmd->io_hdr.cmd_len = cmd->cdb_sz; cmd->io_hdr.cmdp = &cmd->cdb[0]; cmd->io_hdr.dxfer_direction = zbc_sg_cmd_list[cmd_code].dir; cmd->io_hdr.dxfer_len = cmd->out_bufsz; cmd->io_hdr.dxferp = cmd->out_buf; cmd->io_hdr.mx_sb_len = ZBC_SG_SENSE_MAX_LENGTH; cmd->io_hdr.sbp = cmd->sense_buf; out: if ( ret != 0 ) { 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 ); }