Ejemplo n.º 1
0
/**
 * Get the maximum allowed command blocks for the device.
 */
void
zbc_sg_get_max_cmd_blocks(struct zbc_device *dev)
{
    struct stat st;
    int ret, sgsz = ZBC_SG_MAX_SGSZ;

    /* Get device stats */
    if ( fstat(dev->zbd_fd, &st) < 0 ) {
	if ( zbc_log_level >= ZBC_LOG_DEBUG ) {
	    zbc_error("%s: stat failed %d (%s)\n",
		      dev->zbd_filename,
		      errno,
		      strerror(errno));
	}
	goto out;
    }

    if ( S_ISCHR(st.st_mode) ) {
	ret = ioctl(dev->zbd_fd, SG_GET_SG_TABLESIZE, &sgsz);
	if ( ret != 0 ) {
	    if ( zbc_log_level >= ZBC_LOG_DEBUG ) {
		ret = -errno;
		zbc_error("%s: SG_GET_SG_TABLESIZE ioctl failed %d (%s)\n",
			  dev->zbd_filename,
			  errno,
			  strerror(errno));
		sgsz = ZBC_SG_MAX_SGSZ;
	    }
	}
	if (!sgsz)
	    sgsz = 1;
    } else if ( S_ISBLK(st.st_mode) ) {
	sgsz = zbc_sg_max_segments(dev);
    } else {
	/* Files for fake backend */
	sgsz = 128;
    }

out:

    dev->zbd_info.zbd_max_rw_logical_blocks =
	(uint32_t)sgsz * sysconf(_SC_PAGESIZE) / dev->zbd_info.zbd_logical_block_size;

    zbc_debug("%s: Maximum command data transfer size is %llu logical blocks\n",
	      dev->zbd_filename,
	      (unsigned long long)dev->zbd_info.zbd_max_rw_logical_blocks);

    return;

}
Ejemplo n.º 2
0
/**
 * zbc_list_zones - report zones for a ZBC device
 * @dev:                (IN) ZBC device handle to report on
 * @start_lba:          (IN) start LBA for the first zone to reported
 * @ro:                 (IN) Reporting options
 * @zones:              (OUT) pointer for reported zones
 * @nr_zones:           (OUT) number of returned zones
 *
 * Reports the number and details of available zones.  The @zones
 * parameter is used to return an array of zones which is allocated using
 * malloc(3) internally and needs to be freed using free(3).  The number
 * of zones in @zones is returned in @nr_zones.
 *
 * Returns -EIO if an error happened when communicating to the device.
 * Returns -ENOMEM if memory could not be allocated for @zones.
 */
int
zbc_list_zones(struct zbc_device *dev,
               uint64_t start_lba,
               enum zbc_reporting_options ro,
               struct zbc_zone **pzones,
               unsigned int *pnr_zones)
{
    zbc_zone_t *zones = NULL;
    unsigned int nr_zones;
    int ret;

    /* Get total number of zones */
    ret = zbc_report_nr_zones(dev, start_lba, ro, &nr_zones);
    if ( ret < 0 ) {
        return( ret );
    }

    zbc_debug("Device %s: %d zones\n",
              dev->zbd_filename,
              nr_zones);

    /* Allocate zone array */
    zones = (zbc_zone_t *) malloc(sizeof(zbc_zone_t) * nr_zones);
    if ( ! zones ) {
        zbc_error("No memory\n");
        return( -ENOMEM );
    }
    memset(zones, 0, sizeof(zbc_zone_t) * nr_zones);

    /* Get zones info */
    ret = zbc_report_zones(dev, start_lba, ro, zones, &nr_zones);
    if ( ret != 0 ) {
        zbc_error("zbc_report_zones failed\n");
        free(zones);
    } else {
        *pzones = zones;
        *pnr_zones = nr_zones;
    }

    return( ret );

}
Ejemplo n.º 3
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 );

}
Ejemplo n.º 4
0
/**
 * Print an array of bytes.
 */
void
zbc_sg_print_bytes(zbc_device_t *dev,
		   uint8_t *buf,
		   unsigned int len)
{
    char msg[512];
    unsigned i = 0, j;
    int n;

    zbc_debug("%s: * +==================================\n", dev->zbd_filename);
    zbc_debug("%s: * |Byte |   0  |  1   |  2   |  3   |\n", dev->zbd_filename);
    zbc_debug("%s: * |=====+======+======+======+======+\n", dev->zbd_filename);

    while( i < len ) {

	n = sprintf(msg, "%s: * | %3d |", dev->zbd_filename, i);
	for(j = 0; j < 4; j++) {
	    if ( i < len ) {
		n += sprintf(msg + n, " 0x%02x |",
			     (unsigned int)buf[i]);
	    } else {
		n += sprintf(msg + n, "      |");
	    }
	    i++;
	}

	zbc_debug("%s\n", msg);
	if ( i < (len - 4) ) {
	    zbc_debug("%s: * |=====+======+======+======+======+\n", dev->zbd_filename);
	} else {
	    zbc_debug("%s: * +==================================\n", dev->zbd_filename);
	}
    }

    return;

}
Ejemplo n.º 5
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;

}
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
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 );

}
Ejemplo n.º 8
0
/**
 * 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 );

}
Ejemplo n.º 9
0
/**
 * 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 );

}