示例#1
0
/**
 * Read from the emulated device/file.
 */
static int32_t
zbc_fake_pread(struct zbc_device *dev,
               struct zbc_zone *z,
               void *buf,
               uint32_t lba_count,
               uint64_t start_lba)
{
    zbc_fake_device_t *fdev = zbc_fake_to_file_dev(dev);
    struct zbc_zone *zone, *next_zone;
    uint64_t lba;
    ssize_t ret = -EIO;
    off_t offset;
    size_t count;

    if ( ! fdev->zbd_meta ) {
        return -ENXIO;
    }

    zbc_fake_lock(fdev);

    /* Find the target zone */
    zone = zbc_fake_find_zone(fdev, zbc_zone_start_lba(z));
    if ( ! zone ) {
        goto out;
    }

    if ( start_lba > zbc_zone_length(zone) ) {
        goto out;
    }

    lba = zbc_zone_next_lba(zone);
    next_zone = zbc_fake_find_zone(fdev, lba);
    start_lba += zbc_zone_start_lba(zone);

    /* Note: unrestricted read will be added to the standard */
    /* and supported by a drive if the URSWRZ bit is set in  */
    /* VPD page. So this test will need to change.           */
    if ( zone->zbz_type == ZBC_ZT_SEQUENTIAL_REQ ) {

        /* Cannot read unwritten data */
        if ( (start_lba + lba_count) > lba ) {
            if ( next_zone ) {
                dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
                dev->zbd_errno.asc_ascq = ZBC_E_READ_BOUNDARY_VIOLATION;
            } else {
                dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
                dev->zbd_errno.asc_ascq = ZBC_E_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
            }
            goto out;
        }

        if ( (start_lba + lba_count) > zbc_zone_wp_lba(zone) ) {
            dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
            dev->zbd_errno.asc_ascq = ZBC_E_ATTEMPT_TO_READ_INVALID_DATA;
            goto out;
        }

    } else {

        /* Reads spanning other types of zones are OK. */

        if ( (start_lba + lba_count) > lba ) {

            uint64_t count = start_lba + lba_count - lba;

            while( count && next_zone ) {
                if ( zbc_zone_sequential_req(next_zone) ) {
                    dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
                    dev->zbd_errno.asc_ascq = ZBC_E_ATTEMPT_TO_READ_INVALID_DATA;
                    goto out;
                }
                if ( count > zbc_zone_length(next_zone) ) {
                    count -= zbc_zone_length(next_zone);
                }
                lba += zbc_zone_length(next_zone);
            }

        }

    }

    /* XXX: check for overflows */
    count = lba_count * dev->zbd_info.zbd_logical_block_size;
    offset = start_lba * dev->zbd_info.zbd_logical_block_size;

    ret = pread(dev->zbd_fd, buf, count, offset);
    if ( ret < 0 ) {
        ret = -errno;
    } else {
        ret /= dev->zbd_info.zbd_logical_block_size;
    }

out:

    zbc_fake_unlock(fdev);

    return ret;

}
示例#2
0
/**
 * Write to the emulated device/file.
 */
static int32_t
zbc_fake_pwrite(struct zbc_device *dev,
                struct zbc_zone *z,
                const void *buf,
                uint32_t lba_count,
                uint64_t start_lba)
{
    zbc_fake_device_t *fdev = zbc_fake_to_file_dev(dev);
    struct zbc_zone *zone, *next_zone;
    uint64_t lba;
    off_t offset;
    size_t count;
    ssize_t ret = -EIO;

    if ( ! fdev->zbd_meta ) {
        return -ENXIO;
    }

    zbc_fake_lock(fdev);

    /* Find the target zone */
    zone = zbc_fake_find_zone(fdev, zbc_zone_start_lba(z));
    if ( ! zone ) {
        goto out;
    }

    /* Writes cannot span zones */
    if ( start_lba > zbc_zone_length(zone) ) {
        goto out;
    }

    lba = zbc_zone_next_lba(zone);
    next_zone = zbc_fake_find_zone(fdev, lba);
    start_lba += zone->zbz_start;

    if ( (start_lba + lba_count) > lba ) {
        if ( next_zone ) {
            dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
            dev->zbd_errno.asc_ascq = ZBC_E_WRITE_BOUNDARY_VIOLATION;
        } else {
            dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
            dev->zbd_errno.asc_ascq = ZBC_E_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
        }
        goto out;
    }

    if ( zbc_zone_sequential_req(zone) ) {

        /* Can only write at the write pointer */
        if ( start_lba != zbc_zone_wp_lba(zone) ) {
            dev->zbd_errno.sk = ZBC_E_ILLEGAL_REQUEST;
            dev->zbd_errno.asc_ascq = ZBC_E_UNALIGNED_WRITE_COMMAND;
            goto out;
        }

        /* Can only write an open zone */
        if ( ! zbc_zone_is_open(zone) ) {

            if ( fdev->zbd_meta->zbd_nr_exp_open_zones >= fdev->dev.zbd_info.zbd_max_nr_open_seq_req ) {
                /* Too many explicit open on-going */
                dev->zbd_errno.sk = ZBC_E_ABORTED_COMMAND;
                dev->zbd_errno.asc_ascq = ZBC_E_INSUFFICIENT_ZONE_RESOURCES;
                ret = -EIO;
                goto out;
            }

            /* Implicitely open the zone */
            if ( fdev->zbd_meta->zbd_nr_imp_open_zones >= fdev->dev.zbd_info.zbd_max_nr_open_seq_req ) {
                unsigned int i;
                for(i = 0; i < fdev->zbd_nr_zones; i++) {
                    if ( zbc_zone_imp_open(&fdev->zbd_zones[i]) ) {
                        zbc_zone_do_close(fdev, &fdev->zbd_zones[i]);
                        break;
                    }
                }
            }

            zone->zbz_condition = ZBC_ZC_IMP_OPEN;
            fdev->zbd_meta->zbd_nr_imp_open_zones++;

        }

    }

    /* XXX: check for overflows */
    count = (size_t)lba_count * dev->zbd_info.zbd_logical_block_size;
    offset = start_lba * dev->zbd_info.zbd_logical_block_size;

    ret = pwrite(dev->zbd_fd, buf, count, offset);
    if ( ret < 0 ) {

        ret = -errno;

    } else {

        ret /= dev->zbd_info.zbd_logical_block_size;

        if ( zbc_zone_sequential_req(zone) ) {

            /*
             * XXX: What protects us from a return value that's not LBA aligned?
             * (Except for hoping the OS implementation isn't insane..)
             */
            if ( (zbc_zone_wp_lba(zone) + lba_count) >= zbc_zone_next_lba(zone) ) {
                if ( zbc_zone_imp_open(zone) ) {
                    fdev->zbd_meta->zbd_nr_imp_open_zones--;
                } else {
                    fdev->zbd_meta->zbd_nr_exp_open_zones--;
                }
            }

            /* Advance write pointer */
            zbc_zone_wp_lba_inc(zone, lba_count);

        }

    }

out:

    zbc_fake_unlock(fdev);

    return ret;

}
示例#3
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 );

}