Example #1
0
/**
 * 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 );

}
Example #2
0
/**
 * 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 );

}
Example #3
0
/**
 * 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 );

}
Example #4
0
/**
 * 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 );

}
Example #5
0
/**
 * 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 );

}
Example #6
0
/**
 * 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 );

}
Example #7
0
/**
 * 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 );

}
Example #8
0
/**
 * 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 );

}
Example #9
0
/**
 * 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 );

}
Example #10
0
/**
 * 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 );

}
Example #11
0
/**
 * 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 );

}
Example #12
0
/**
 * 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 );

}
Example #13
0
/**
 * 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 );

}
Example #14
0
/**
 * 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 );

}