예제 #1
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 );

}
예제 #2
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 );

}