コード例 #1
0
ファイル: zbc_sg.c プロジェクト: nmtadam/libzbc
/**
 * 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 );

}
コード例 #2
0
ファイル: zbc_scsi.c プロジェクト: ceph/libzbc
/**
 * 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 );

}
コード例 #3
0
/**
 * 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;

}
コード例 #4
0
/**
 * 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;

}
コード例 #5
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;
}
コード例 #6
0
/**
 * 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;

}
コード例 #7
0
ファイル: zbc_scsi.c プロジェクト: ceph/libzbc
/**
 * 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 );

}
コード例 #8
0
ファイル: zbc_scsi.c プロジェクト: ceph/libzbc
/**
 * 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 );

}
コード例 #9
0
ファイル: zbc_scsi.c プロジェクト: ceph/libzbc
/**
 * 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 );

}
コード例 #10
0
ファイル: zbc_scsi.c プロジェクト: ceph/libzbc
/**
 * 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 );

}
コード例 #11
0
ファイル: zbc_sg.c プロジェクト: nmtadam/libzbc
/**
 * 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 );

}
コード例 #12
0
ファイル: zbc_sg.c プロジェクト: nmtadam/libzbc
/**
 * 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 );

}
コード例 #13
0
ファイル: zbc_scsi.c プロジェクト: sahlberg/libzbc
/**
 * 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 );

}